/**
* Nudsf_DataRepository
* Nudsf Data Repository Service. © 2021, 3GPP Organizational Partners (ARIB, ATIS, CCSA, ETSI, TSDSI, TTA, TTC). All rights reserved. 
*
* The version of the OpenAPI document: 1.1.0-alpha.2
* 
*
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
* https://openapi-generator.tech
* Do not edit the class manually.
*/

#include "PluginApi.hpp"
//#include "Helpers.h"
#include "logger.hpp"
#include <nlohmann/json.hpp>
#include "TransData.hpp"
#include <stdlib.h>
#include <map>
#include <algorithm>
#include "plugin_config.hpp"
#include <cmath>
using namespace config;
std::map<std::string,long> ip2len;
std::map<std::string,bool> all_rp;

long msg_len[4]={0};
long pro_cap[4] = {0};
bool isall[4] = {0};

extern int weight[15];
extern plugin_config plugin_cfg;
using json = nlohmann::json;

//using namespace org::openapitools::server::model;

namespace org {
namespace openapitools {
namespace server {
namespace api {

using namespace org::openapitools::server::model;



PluginApi::PluginApi(std::shared_ptr<Pistache::Rest::Router> rtr) { 
    router = rtr;
}

void PluginApi::init() {
    setupRoutes();
}

void PluginApi::setupRoutes() {
    using namespace Pistache::Rest;

    Routes::Get(*router, base + "/modify/AMFIP/:AmfIp/:AmfPort", Routes::bind(&PluginApi::modify_AmfIP_handler, this));
    // Routes::Delete(*router, base + "/:realmId/:storageId/records/:recordId", Routes::bind(&PluginApi::delete_record_handler, this));
    // Routes::Get(*router, base + "/:realmId/:storageId/records/:recordId/meta", Routes::bind(&PluginApi::get_meta_handler, this));
    // Routes::Get(*router, base + "/:realmId/:storageId/records/:recordId", Routes::bind(&PluginApi::get_record_handler, this));
    Routes::Put(*router, base + "/:assoid/:stream", Routes::bind(&PluginApi::N2_trans_handler, this));
    // Routes::Patch(*router, base + "/:realmId/:storageId/records/:recordId/meta", Routes::bind(&PluginApi::update_meta_handler, this));
    Routes::Post(*router, base+"/:amf-id/algorithm/omf", Routes::bind(&PluginApi::algorithm_omf_handler, this));
    // Default handler, called when a route is not found
    //router->addCustomHandler(Routes::bind(&PluginApi::record_crud_api_default_handler, this));
}
void PluginApi::algorithm_omf_handler(const Pistache::Rest::Request &request, Pistache::Http::ResponseWriter response) {
    auto amf_id = request.param(":amf-id").as<std::uint32_t>();
    Logger::plugin_server().debug("Receiving algorithm-omf info from amf-%d",amf_id);
    Logger::plugin_server().debug("%s",request.body().c_str());
    TransData transdata;
    try {
        nlohmann::json::parse(request.body()).get_to(transdata);
	Logger::plugin_server().debug("ip address : %s",transdata.ip_addr.c_str());
	Logger::plugin_server().debug("current message queue length : %d",transdata.mq_len);
	Logger::plugin_server().debug("current capbility : %f",transdata.cap);
	if(!transdata.ip_addr.compare("10.103.239.116")){
	  msg_len[0] = transdata.mq_len;
	  pro_cap[0] = (long)transdata.cap;
	  isall[0]   = true;
	  Logger::plugin_server().debug("capability(0) %d",pro_cap[0]);
	}
	if(!transdata.ip_addr.compare("10.103.239.114")){
	  msg_len[1] = transdata.mq_len;
	  pro_cap[1] = (long)transdata.cap;
	  isall[1]   = true;
	  Logger::plugin_server().debug("capability(1) %d",pro_cap[1]);
	}
	if(!transdata.ip_addr.compare("10.103.239.112")){
	  msg_len[2] = transdata.mq_len;
	  pro_cap[2] = (long)transdata.cap;
	  isall[2]   = true;
	  Logger::plugin_server().debug("capability(2) %d",pro_cap[2]);
	}
	if(!transdata.ip_addr.compare("10.103.238.75")){
	  msg_len[3] = transdata.mq_len;
	  pro_cap[3] = (long)transdata.cap;
	  isall[3]   = true;
	  Logger::plugin_server().debug("capability(3) %d",pro_cap[3]);
	}
	bool update = true;
	for(int i=0; i<4; i++){
	  if(!isall[i]){
            Logger::plugin_server().debug("update = false");
	    update = false;
	    break;
	  }
	}
        long N = 50;
	long K[4] = {0};
	double sum_c = 0;
	double sum = 0;
	double tmp_w[4]={0};
	if(update){
	  Logger::plugin_server().debug("new round...");
	  Logger::plugin_server().debug("capability(0) %d",pro_cap[0]);
	  Logger::plugin_server().debug("capability(1) %d",pro_cap[1]);
	  Logger::plugin_server().debug("capability(2) %d",pro_cap[2]);
	  Logger::plugin_server().debug("capability(3) %d",pro_cap[3]);
	  for(int i=0; i<4; i++){
	    isall[i] = false;
	    Logger::plugin_server().debug("capability(%d) %d",i,pro_cap[i]);
	    Logger::plugin_server().debug("message_length(%d) %d",i,msg_len[i]);
	    if((pro_cap[i]-0)<1e-8) {
              Logger::plugin_server().debug("update = false capability = 0");
              response.send(Pistache::Http::Code::Ok, "n2 receive \n");
	      return;
	    }else{
	      pro_cap[i] = 1000000/(double)pro_cap[i];
              Logger::plugin_server().debug("update = true capability = %d",pro_cap[i]);
	    }
	  }
	  Logger::plugin_server().debug("Updating...");
	  for(int i=0; i<4; i++){
	    //isall[i] = false;
	    sum_c += pro_cap[i];
	    K[i] = msg_len[i] - pro_cap[i]*1;
	    //K[i] = msg_len[i] - (1/(double)pro_cap[i]*1000000)*1;
	    if(K[i]<0) K[i] = 0;
            Logger::plugin_server().debug("K[%d] = %d",i,K[i]);
	  }
	  if(plugin_cfg.is_omf){
	    for(int j=0; j<4; j++){
	      sum += (double)(K[j]*pro_cap[0]-K[0]*pro_cap[j]); 
	      //sum += (double)(K[j]*pro_cap[0]-K[0]*pro_cap[j])/1000000; 
	    }
            Logger::plugin_server().debug("sum = %f",sum);
	    tmp_w[0] = pro_cap[0]/sum_c + sum/N*sum_c;
	    //tmp_w[0] = pro_cap[0]/sum_c + sum/N*sum_c/1000000;
            Logger::plugin_server().debug("weight[0] = %f",tmp_w[0]);
	    for(int j=1; j<4; j++){
	      tmp_w[j] =(double)(pro_cap[j]*K[0]-pro_cap[0]*K[j])/(pro_cap[0]*N)+((double)pro_cap[j]/pro_cap[0])*tmp_w[0];
	    }
	    double dwt = 0;
	    for(int j=0; j<4; j++){
	      Logger::plugin_server().debug("weight[%d] %f",j,tmp_w[j]);
	      dwt +=tmp_w[j];
	      //weight[j] = std::floor(weight[j]*N);
	    }
	    for(int j=0; j<4; j++){
	      Logger::plugin_server().debug("weight[%d] %f",j,tmp_w[j]/dwt);
	      weight[j] = std::floor(tmp_w[j]/dwt*N);
	    }
	  }
          response.send(Pistache::Http::Code::Ok, "Updated \n");
	}else{
	  Logger::plugin_server().debug("not receiving all information");
          response.send(Pistache::Http::Code::Ok, "n2 receive \n");
	  return;
	}

# if 0
	ip2len[transdata.ip_addr] = transdata.mq_len;
	all_rp[transdata.ip_addr] = true;
	bool update = true;
	if(ip2len.size() == 4){
	  Logger::plugin_server().debug("received 4 amf information...");
	  for(std::map<std::string,bool>::iterator it = all_rp.begin(); it!=all_rp.end(); it++){
	    if(!it->second) update = false;
	  }
	}else{
          response.send(Pistache::Http::Code::Ok, "n2 receive \n");
	  return;
	}
	if(update){
	  Logger::plugin_server().debug("Updating...");
	  int len_72 = 0, len_116 = 0, len_114 = 0, len_112 = 0;
	  for(std::map<std::string,long>::iterator it = ip2len.begin();it!=ip2len.end();it++){
	    //if(!it->first.compare("10.103.239.116")) len_116 = it->second;
	    //if(!it->first.compare("10.103.239.114")) len_114 = it->second;
	    //if(!it->first.compare("10.103.239.112")) len_112 = it->second;
	    //if(!it->first.compare("10.103.238.72")) len_72 = it->second;
	    if(!it->first.compare("10.103.239.116")) msg_len[0] = it->second;
	    if(!it->first.compare("10.103.239.114")) msg_len[1] = it->second;
	    if(!it->first.compare("10.103.239.112")) msg_len[2] = it->second;
	    if(!it->first.compare("10.103.238.72"))  msg_len[3] = it->second;
	  }
	  for(std::map<std::string,bool>::iterator it = all_rp.begin(); it!=all_rp.end(); it++){
	    it->second = false;
	  }
	  if(len_72 ==0 && len_112 ==0 && len_114 ==0 && len_116 ==0){
            Logger::plugin_server().debug("all instances are with no signaling, return");
	    return;
	  }
	  if(len_72 == 0) len_72 = 1;
	  if(len_116 == 0) len_116 = 1;
	  if(len_114 == 0) len_114 = 1;
	  if(len_112 == 0) len_112 = 1;
	  int x = (50)/(1+(float)len_114/len_116+(float)len_112/len_116+(float)len_72/len_116);
	  int x_[4];
	  x_[0] = x; x_[1] = (float)len_114/len_116*x; x_[2] = (float)len_112/len_116*x; x_[3] = (float)len_72/len_116*x;
	  if(plugin_cfg.is_omf){
	    weight[0] = (50-x_[0])/3;
	    weight[1] = (50-x_[1])/3;
	    weight[2] = (50-x_[2])/3;
	    weight[3] = (50-x_[3])/3;
	  }
#endif
	  //weight[0] = 50*(len_72+len_114+len_112)/(len_116+len_114+len_112+len_72);
	  //weight[1] = 50*(len_72+len_116+len_112)/(len_116+len_114+len_112+len_72);
	  //weight[2] = 50*(len_72+len_114+len_116)/(len_116+len_114+len_112+len_72);
	  //weight[3] = 50-weight[0]-weight[1]-weight[2];
	  Logger::plugin_server().debug("Updated weight, weight[0]=%d, weight[1]=%d, weight[2]=%d, weight[3]=%d", weight[0],weight[1],weight[2],weight[3]);
        //this->n2_trans_to_gnb(assoc_id,stream,transdata,response);
    } catch (nlohmann::detail::exception &e) {
        //send a 400 error
        response.send(Pistache::Http::Code::Bad_Request, e.what());
        return;
    } catch (Pistache::Http::HttpError &e) {
        response.send(static_cast<Pistache::Http::Code>(e.code()), e.what());
        return;
    } catch (std::exception &e) {
        //send a 500 error
        response.send(Pistache::Http::Code::Internal_Server_Error, e.what());
        return;
    }
    response.send(Pistache::Http::Code::Ok, "n2 receive \n");
    return;
}
void PluginApi::N2_trans_handler(const Pistache::Rest::Request &request, Pistache::Http::ResponseWriter response) {
    // Getting the path params
    auto assoc_id = request.param(":assoid").as<std::uint32_t>();
    auto stream = request.param(":stream").as<std::uint16_t>();
    
    Logger::plugin_server().info("--Put!--assoc_id = (%d), stream = (%d)",assoc_id,stream);


    // SubscriptionData subscriptionData;
    TransData transdata;
    std::cout<<request.body()<<std::endl;
    try {
        nlohmann::json::parse(request.body()).get_to(transdata);
        this->n2_trans_to_gnb(assoc_id,stream,transdata,response);
    } catch (nlohmann::detail::exception &e) {
        //send a 400 error
        response.send(Pistache::Http::Code::Bad_Request, e.what());
        return;
    } catch (Pistache::Http::HttpError &e) {
        response.send(static_cast<Pistache::Http::Code>(e.code()), e.what());
        return;
    } catch (std::exception &e) {
        //send a 500 error
        response.send(Pistache::Http::Code::Internal_Server_Error, e.what());
        return;
    }

}

void PluginApi::modify_AmfIP_handler(const Pistache::Rest::Request &request, Pistache::Http::ResponseWriter response) {
    auto AmfIp = request.param(":AmfIp").as<std::string>();
    auto AmfPort = request.param(":AmfPort").as<unsigned int>();
    Logger::plugin_server().info("--modify amf ip--AmfIp = (%s), AmfPort = (%d)",AmfIp.c_str(),AmfPort);
    try {
        this->modify_AmfIP(AmfIp,AmfPort,response);
    } catch (nlohmann::detail::exception &e) {
        //send a 400 error
        response.send(Pistache::Http::Code::Bad_Request, e.what());
        return;
    } catch (Pistache::Http::HttpError &e) {
        response.send(static_cast<Pistache::Http::Code>(e.code()), e.what());
        return;
    } catch (std::exception &e) {
        //send a 500 error
        response.send(Pistache::Http::Code::Internal_Server_Error, e.what());
        return;
    }
}
}
}
}
}


