//-*- C++ -*-
#pragma once
#ifndef NET_ROUTER_API
#define NET_ROUTER_API

#include <net/router.hpp>
#include <net/interfaces.hpp>
#include <config>
#include <rapidjson/document.h>

namespace net {

  template <typename T>
  inline Route<IP4> parse_route(const T& obj)
  {
    Expects(obj.HasMember("address"));
    Expects(obj.HasMember("netmask"));
    Expects(obj.HasMember("iface"));

    ip4::Addr address{obj["address"].GetString()};
    ip4::Addr netmask{obj["netmask"].GetString()};

    ip4::Addr nexthop = (obj.HasMember("nexthop"))
      ? ip4::Addr{obj["nexthop"].GetString()} : 0;

    int N = obj["iface"].GetInt();
    auto& iface = Interfaces::get(N);

    int cost = (not obj.HasMember("cost")) ? 100 : obj["cost"].GetInt();

    return {address, netmask, nexthop, iface, cost};
  }

  inline Router<IP4>::Routing_table
  load_routing_table_from_cfg(int N = 0)
  {
    const auto& cfg = Config::get();
    INFO("Routeconf", "Reading route table #%i...", N);

    Expects(not cfg.empty()
      && "No config found");

    rapidjson::Document doc;
    doc.Parse(cfg.data());

    Expects(doc.IsObject()
      && "Malformed config (not an object)");

    Expects(doc.HasMember("router")
      && "Router config not found");

    auto& routers = doc["router"];
    Expects(routers.IsArray()
      && "Malformed router config (not an array)");

    auto routers_arr = routers.GetArray();
    Expects(static_cast<int>(routers.Size()) > N
      && "Route table with given index do not exist");

    Expects(routers_arr[N].IsArray()
      && "Route table with given index malformed (not an array)");
    auto routes = routers_arr[N].GetArray();

    Router<IP4>::Routing_table table{};
    table.reserve(routes.Size());

    for(auto& route : routes)
      table.emplace_back(parse_route(route));

    return table;
  }

  static std::unique_ptr<Router<IP4>>
  create_router_from_config(int N = 0)
  {
    return std::make_unique<Router<IP4>>(load_routing_table_from_cfg(N));
  }

  template <int N = 0>
  static Router<IP4>& get_router()
  {
    static auto router = create_router_from_config(N);
    return *router;
  }

} //< namespace net

#endif
