#include "plugin_app.hpp"
#include "sctp_server.hpp"
#include "plugin_config.hpp"
#include <curl/curl.h>
#include <nlohmann/json.hpp>
#include "logger.hpp"
#include <sys/time.h>
#include <fstream>
using namespace std;
using namespace sctp;
using namespace config;
extern sctp_server* sctp_s_38412;
extern plugin_config plugin_cfg;


extern std::string amf_ip;
extern unsigned int amf_port;
extern std::map<std::string,long> ip2len;

ofstream timestamp_plugin("/home/xgcore/plugin_timestamp.txt");
string amf_ip_list[15] = {"10.103.239.116","10.103.239.114","10.103.239.112","10.103.238.75","10.103.239.112","10.103.238.72","10.103.239.53","10.103.239.61","10.103.239.63","10.103.238.105","10.103.238.104","10.103.238.103","10.103.238.98","10.103.238.97","10.103.238.96"};
int amf_ports[15] = {8282,8282,8282,8282,8282,8282,8282,8282,8282,8282,8282,8282,8282,8282,8282};
int weight[15] = {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15};
namespace amf_application{

plugin_app::plugin_app() {}
plugin_app::~plugin_app() {}
void octet_stream_2_hex_stream(uint8_t* buf, int len, std::string& out) {
  out       = "";
  char* tmp = (char*) calloc(1, 2 * len * sizeof(uint8_t) + 1);
  for (int i = 0; i < len; i++) {
    sprintf(tmp + 2 * i, "%02x", buf[i]);
  }
  tmp[2 * len] = '\0';
  out          = tmp;
  printf("n1sm buffer: %s\n", out.c_str());
}
static std::size_t callback(
    const char* in, std::size_t size, std::size_t num, std::string* out) {
  const std::size_t totalBytes(size * num);
  out->append(in, totalBytes);
  return totalBytes;
}
//string amf_ip_list[15] = {"10.103.239.56","10.103.239.116","10.103.239.114","10.103.239.113","10.103.239.112","10.103.238.72","10.103.239.53","10.103.239.61","10.103.239.63","10.103.238.105","10.103.238.104","10.103.238.103","10.103.238.98","10.103.238.97","10.103.238.96"};
//int amf_ports[15] = {8282,8282,8282,8282,8282,8282,8282,8282,8282,8282,8282,8282,8282,8282,8282};
//int weight[15] = {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15};
int  index = -1;
int random_index = 0;
int choose = 0;
int num_inst = 4;

void plugin_app::handle_receive(
    bstring payload, sctp_assoc_id_t assoc_id, sctp_stream_id_t stream,
    sctp_stream_id_t instreams, sctp_stream_id_t outstreams)
{
    struct timeval tv1; struct timezone tz1; gettimeofday(&tv1,&tz1); long start = tv1.tv_sec*1000000 +tv1.tv_usec;
    Logger::plugin_sbi().debug("Begining at time %ld", start);
    timestamp_plugin<< "time_stamp	"<<start<<endl;
    index ++;
    Logger::plugin_sbi().debug("index %d",index);
    printf("使用http发送到amf\n");
    Logger::plugin_sbi().debug("receive n2 from gnb then Send N2 message to AMF");
    std::string ngapmsg;
    octet_stream_2_hex_stream((uint8_t*) bdata(payload), blength(payload), ngapmsg);
    Logger::sctp().debug(
        "[Assoc ID %d] Sending buffer %p of %d bytes on stream %d,ngapmsg length %d ",
        assoc_id, bdata(payload), blength(payload), stream,ngapmsg.length());
    
    nlohmann::json json_data = {};
    json_data["n2_data"] = ngapmsg;
    std::string url;

  if(plugin_cfg.is_wrr || plugin_cfg.is_omf){
	  Logger::plugin_sbi().debug("Algorithm WRR or OMF");
    Logger::plugin_sbi().debug("current weight weight[0]=%d, weight[0]=%d, weight[0]=%d, weight[0]=%d", weight[0],weight[1],weight[2],weight[3]);
    int sum = 0;
    int index_num = 0;
    for(int i=0; i < num_inst; i++)
	    index_num += weight[i];
    for(int i=0; i <= choose; i++)
	    sum += weight[i];
    if((index + 1) > sum)
      choose = (choose + 1) % num_inst;
    if((index + 1) > index_num)
	    index = 0;
    Logger::sctp().debug("Choosing %d th AMF instance ", choose+1);
    url  =
        "http://" + std::string(amf_ip_list[choose].c_str()) +
        ":" + std::to_string(amf_ports[choose]) + "/namf-comm/v1/plugin_n2/" +
        to_string (assoc_id ) + "/" + to_string (stream) ;
  }
#if 0
    srand(time(NULL));
    random_index = rand()%4;
    Logger::sctp().debug("Choosing %d th AMF instance ", random_index+1);

    std::string url  =
        "http://" + std::string(amf_ip_list[random_index].c_str()) +
        ":" + std::to_string(amf_ports[random_index]) + "/namf-comm/v1/plugin_n2/" +
        to_string (assoc_id ) + "/" + to_string (stream) ;
#endif
    if(plugin_cfg.is_rr){
	  Logger::plugin_sbi().debug("Algorithm RR ");
      url  =
        "http://" + std::string(amf_ip_list[index % 4].c_str()) +
        ":" + std::to_string(amf_ports[index % 4]) + "/namf-comm/v1/plugin_n2/" +
        to_string (assoc_id ) + "/" + to_string (stream) ;
    }

#if 0
      url  =
        "http://" + std::string(amf_ip_list[0].c_str()) +
        ":" + std::to_string(amf_ports[0]) + "/namf-comm/v1/plugin_n2/" +
        to_string (assoc_id ) + "/" + to_string (stream) ;
#endif
#if 0
    std::string url  =
        "http://" + std::string(amf_ip.c_str()) +
        ":" + std::to_string(amf_port) + "/namf-comm/v1/plugin_n2/" +
        to_string (assoc_id ) + "/" + to_string (stream) ;
#endif
      Logger::plugin_sbi().debug(
          "Send N2 message to AMF , AMF URL %s", url.c_str());

    std::string body = json_data.dump();
    Logger::plugin_sbi().debug(
         "Send N2 message to AMF , msg body: \n %s", body.c_str());

     curl_global_init(CURL_GLOBAL_ALL);
     CURL* curl = curl = curl_easy_init();

     if (curl) {
        CURLcode res               = {};
        struct curl_slist* headers = nullptr;
        // std::string content_type = "content-type: multipart/related; boundary=" +
        //                        std::string(CURL_MIME_BOUNDARY);
        // headers = curl_slist_append(headers, content_type.c_str());
        headers = curl_slist_append(headers, "content-type: application/json");
        headers = curl_slist_append(headers, "Expect:"); 
        curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
        curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
        curl_easy_setopt(curl, CURLOPT_HTTPGET, 1);
        curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "PUT");
        curl_easy_setopt(curl, CURLOPT_TIMEOUT_MS, AMF_CURL_TIMEOUT_MS);

            // Response information.
        long httpCode = {0};
        std::unique_ptr<std::string> httpData(new std::string());
        //std::unique_ptr<std::string> httpHeaderData(new std::string());
         // Hook up data handling function.
        curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &callback);
        curl_easy_setopt(curl, CURLOPT_WRITEDATA, httpData.get());
       // curl_easy_setopt(curl, CURLOPT_HEADERDATA, httpHeaderData.get());
        curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, body.length());
        //curl_easy_setopt(curl, CURLOPT_POSTFIELDS, body_data);
        curl_easy_setopt(curl, CURLOPT_POSTFIELDS, body.c_str());
        res = curl_easy_perform(curl);
        curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &httpCode);

    //     // Response information.
    //     long httpCode = {0};
    //     std::unique_ptr<std::string> httpData(new std::string());
    //     curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &callback);
    //     curl_easy_setopt(curl, CURLOPT_WRITEDATA, httpData.get());
    //     curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, body.length());
    //     curl_easy_setopt(curl, CURLOPT_POSTFIELDS, body.c_str());
    //     res = curl_easy_perform(curl);
    //     curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &httpCode);

        Logger::plugin_sbi().debug("Send NGAP message to AMF, response from AMF, HTTP Code: %d", httpCode);
        if (static_cast<http_response_codes_e>(httpCode) ==http_response_codes_e::HTTP_RESPONSE_CODE_0) {
            Logger::plugin_sbi().info( "Cannot get response when calling %s", url.c_str());
            // free curl before returning
            curl_slist_free_all(headers);
            curl_easy_cleanup(curl);
            return;
        }
        if (static_cast<http_response_codes_e>(httpCode) ==http_response_codes_e::HTTP_RESPONSE_CODE_200_OK) {
            Logger::plugin_sbi().info("sending successful when calling %s", url.c_str());
            curl_slist_free_all(headers);
            curl_easy_cleanup(curl);
            return;
        }
        curl_global_cleanup();

    
    }

}
}//namespace
    //     /




    //     Logger::smf_sbi().debug(
    //         "NF Instance Registration, response from NRF, HTTP Code: %d", httpCode);

    //     if (static_cast<http_response_codes_e>(httpCode) ==
    //         http_response_codes_e::HTTP_RESPONSE_CODE_CREATED) {
    //     json response_data = {};
    //     try {
    //         response_data = json::parse(*httpData.get());
    //     } catch (json::exception& e) {
    //         Logger::smf_sbi().warn(
    //             "NF Instance Registration, could not parse json from the NRF "
    //             "response");
    //     }
    //     Logger::smf_sbi().debug(
    //         "NF Instance Registration, response from NRF, json data: \n %s",
    //         response_data.dump().c_str());

    //     // send response to APP to process
    //     std::shared_ptr<itti_n11_register_nf_instance_response> itti_msg =
    //         std::make_shared<itti_n11_register_nf_instance_response>(
    //             TASK_SMF_SBI, TASK_SMF_APP);
    //     itti_msg->http_response_code = httpCode;
    //     itti_msg->http_version       = msg->http_version;
    //     Logger::smf_app().debug("Registered SMF profile (from NRF)");
    //     itti_msg->profile.from_json(response_data);

    //     int ret = itti_inst->send_msg(itti_msg);
    //     if (RETURNok != ret) {
    //         Logger::smf_sbi().error(
    //             "Could not send ITTI message %s to task TASK_SMF_APP",
    //             itti_msg->get_msg_name());
    //     }
    //     } else {
    //     Logger::smf_sbi().warn(
    //         "NF Instance Registration, could not get response from NRF");
    //     }

    //     curl_slist_free_all(headers);
    //     curl_easy_cleanup(curl);
    // }
    // curl_global_cleanup();
    

