#include "plugin_config.hpp"

#include <sys/types.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <netinet/in.h>
#include <net/if.h>
#include <net/if_arp.h>
#include <arpa/inet.h>



#include <iostream>

#include "logger.hpp"

using namespace libconfig;
extern std::string amf_ip;
extern unsigned int amf_port;
namespace config {
plugin_config::plugin_config() {
  is_wrr = false;
  is_rr = false;
  is_omf = false;
  rrRate = 0;
}

plugin_config::~plugin_config() {}

int plugin_config::load(const std ::string &config_file) {
  Logger::plugin_app().debug("\nLoad plugin system configuration file(%s)",
                          config_file.c_str());

  Config cfg;
  try {
    cfg.readFile(config_file.c_str());
  } catch (const FileIOException &fioex) {
    Logger::plugin_app().error("I/O error while reading file %s - %s",
                            config_file.c_str(), fioex.what());
    throw;
  } catch (const ParseException &pex) {
    Logger::plugin_app().error("Parse error at %s:%d - %s", pex.getFile(),
                            pex.getLine(), pex.getError());
    throw;
  }
  const Setting &root = cfg.getRoot();
  try {
    const Setting &plugin_cfg = root[PLUGIN_CONFIG_STRING_PLUGIN_CONFIG];
  } catch (const SettingNotFoundException &nfex) {
    Logger::plugin_app().error("%s : %s", nfex.what(), nfex.getPath());
    return -1;
  }
  const Setting &plugin_cfg = root[PLUGIN_CONFIG_STRING_PLUGIN_CONFIG];
   try {
     std::string alg;
     plugin_cfg.lookupValue("ALGORITHM", alg);
     if(!alg.compare("WRR")) is_wrr = true;
     if(!alg.compare("RR")) is_rr = true;
     if(!alg.compare("OMF")) is_omf = true;
   } catch (const SettingNotFoundException &nfex) {
     Logger::plugin_app().warn("%s : %s, using defaults", nfex.what(),
                             nfex.getPath());
   }
   try {
     plugin_cfg.lookupValue("REQUESTRATE", rrRate);
   } catch (const SettingNotFoundException &nfex) {
     Logger::plugin_app().warn("%s : %s, using defaults", nfex.what(),
                             nfex.getPath());
   }

  try {
    const Setting &new_if_cfg = plugin_cfg[PLUGIN_CONFIG_STRING_INTERFACES];
    const Setting& n2_cfg =new_if_cfg[PLUGIN_CONFIG_STRING_INTERFACE_NGAP];
    load_interface(n2_cfg, n2);

    const Setting &sbi_cfg = new_if_cfg[PLUGIN_CONFIG_STRING_INTERFACE_SBI];
    load_interface(sbi_cfg, sbi);
    const Setting &nrf_cfg = new_if_cfg[PLUGIN_CONFIG_STRING_INTERFACE_NRF];
    load_interface(nrf_cfg, nrf);

    amf_ip = nrf.addr4;
    amf_port = nrf.port;
  } catch (const SettingNotFoundException &nfex) {
    Logger::plugin_app().error("%s : %s", nfex.what(),
                            nfex.getPath());
    return -1;
  }

  return 0;
}
int plugin_config::load_interface(const libconfig::Setting &if_cfg,
                               interface_cfg_t &cfg) {
  if_cfg.lookupValue(PLUGIN_CONFIG_STRING_INTERFACE_NAME, cfg.if_name);
  if_cfg.lookupValue(PLUGIN_CONFIG_STRING_IPV4_ADDRESS, cfg.addr4);
  if(!cfg.addr4.compare("read"))
  {
    if(getip(cfg.addr4,cfg.if_name) < 0) return -1;
  }
  if_cfg.lookupValue(PLUGIN_CONFIG_STRING_PORT, cfg.port);

  return 0;
}
int plugin_config::getip(std::string &ip, std::string &ifname)
{
    int sock;
    struct sockaddr_in sin;
    struct ifreq ifr;
    sock = socket(AF_INET, SOCK_DGRAM, 0);
    if (sock == -1)
    {
        perror("socket");
        return -1;
    }
    strncpy(ifr.ifr_name, ifname.c_str(), IFNAMSIZ);
    ifr.ifr_name[IFNAMSIZ - 1] = 0;
    if (ioctl(sock, SIOCGIFADDR, &ifr) < 0)
    {
        perror("ioctl");
        return -1;
    }
    memcpy(&sin, &ifr.ifr_addr, sizeof(sin));
    //printf("ip = %s\n",inet_ntoa(sin.sin_addr));
    //sprintf(ip, "%s", inet_ntoa(sin.sin_addr));
    ip = std::string(inet_ntoa(sin.sin_addr));
    return 0;
}
void plugin_config::display() {
  Logger::config().info(
      "======================    plugin   =====================");
  Logger::config().info("Configuration plugin:");
  Logger::config().info("");
  Logger::config().info("- N2 Networking:");
  Logger::config().info("    iface ................: %s", n2.if_name.c_str());
  Logger::config().info("    ip ...................: %s", n2.addr4.c_str());
  Logger::config().info("    port .................: %d", n2.port);
  Logger::config().info("");

  Logger::config().info("- SBI Networking:");
  Logger::config().info("    iface ................: %s", sbi.if_name.c_str());
  Logger::config().info("    ip ...................: %s", sbi.addr4.c_str());
  Logger::config().info("    port .................: %d", sbi.port);
  Logger::config().info("");

  Logger::config().info("- NRF Networking:");
  Logger::config().info("    iface ................: %s", nrf.if_name.c_str());
  Logger::config().info("    ip ...................: %s", nrf.addr4.c_str());
  Logger::config().info("    port .................: %d", nrf.port);
  Logger::config().info("");
  if(is_wrr)
    Logger::config().info("    algorithm WRR.......: true");
  if(is_rr)
    Logger::config().info("    algorithm RR........: true");
  if(is_omf)
    Logger::config().info("    algorithm OMF.......: true");


}
} // namespace config
