#include <iostream>
#include "cwmp.nsmap"
#include "httppost.h"
#include "soapcwmpService.h"
#include "plugin/threads.h"
#include <vector>
#include <algorithm>
#include <signal.h>
#include <thread>
#include <fstream>

int port = 8000;

int POST_handler(struct soap *soap);

struct http_post_handlers http_post_handlers[] = {
        {"POST", POST_handler},
        {NULL}
};

void handler(int s) {
    printf("Sassan Caught SIGPIPE....\n");
}

void *process_request(void *arg) {
    cwmpService *service = (cwmpService *) arg;
    THREAD_DETACH(THREAD_ID);
    if (service) {
        service->serve();
        service->destroy(); /* clean up */
        delete service;
    }
    return NULL;
}

//<editor-fold desc="zbx_keys">
/*
Searching for: InternetGatewayDevice.DeviceInfo.SoftwareVersion & Find Result: 18
Searching for: InternetGatewayDevice.DeviceInfo.UpTime & Find Result: 21
Searching for: InternetGatewayDevice.DeviceInfo.X_TGT_IMSI & Find Result: 22
Searching for: InternetGatewayDevice.DeviceInfo.MemoryStatus.Free & Find Result: 28
Searching for: InternetGatewayDevice.DeviceInfo.ProcessStatus.CPUUsage & Find Result: 29
Searching for: InternetGatewayDevice.Time.CurrentLocalTime & Find Result: 229
Searching for: InternetGatewayDevice.WANDevice.1.X_TGT_WANNetConfigInfo.SINR1 & Find Result: 464
Searching for: InternetGatewayDevice.WANDevice.1.X_TGT_WANNetConfigInfo.SINR2 & Find Result: 465
Searching for: InternetGatewayDevice.WANDevice.1.X_TGT_WANNetConfigInfo.EnodeBId & Find Result: 469
Searching for: InternetGatewayDevice.WANDevice.1.X_TGT_WANNetConfigInfo.CellId & Find Result: 470
Searching for: InternetGatewayDevice.WANDevice.1.WANEthernetInterfaceConfig.Stats.BytesSent & Find Result: 542
Searching for: InternetGatewayDevice.WANDevice.1.WANEthernetInterfaceConfig.Stats.BytesReceived & Find Result: 543
Searching for: InternetGatewayDevice.WANDevice.1.WANEthernetInterfaceConfig.Stats.PacketsSent & Find Result: 544
Searching for: InternetGatewayDevice.WANDevice.1.WANEthernetInterfaceConfig.Stats.PacketsReceived & Find Result: 545
Searching for: InternetGatewayDevice.WANDevice.1.WANConnectionDevice.1.WANIPConnection.1.Uptime & Find Result: 553
Searching for: InternetGatewayDevice.WANDevice.1.WANConnectionDevice.1.WANIPConnection.1.ExternalIPAddress & Find Result: 557
Searching for: InternetGatewayDevice.WEB_GUI.Network.LTE_Setting.Status.RSRP1 & Find Result: 580
Searching for: InternetGatewayDevice.WEB_GUI.Network.LTE_Setting.Status.RSRP2 & Find Result: 581
Searching for: InternetGatewayDevice.WEB_GUI.Network.LTE_Setting.Status.TX_Power & Find Result: 585
Searching for: InternetGatewayDevice.WEB_GUI.Network.LTE_Setting.Status.DL_MCS & Find Result: 591
Searching for: InternetGatewayDevice.WEB_GUI.Network.LTE_Setting.Status.UL_MCS & Find Result: 592
Searching for: InternetGatewayDevice.WEB_GUI.Overview.DeviceInfo.IMSI & Find Result: 612
Searching for: InternetGatewayDevice.WEB_GUI.Overview.DeviceInfo.IMEI & Find Result: 613
*/
std::string zbx_keys[] = {"InternetGatewayDevice.WANDevice.1.WANConnectionDevice.1.WANIPConnection.1.Uptime",
                          "InternetGatewayDevice.WEB_GUI.Network.LTE_Setting.Status.UL_MCS",
                          "InternetGatewayDevice.WEB_GUI.Network.LTE_Setting.Status.TX_Power",
                          "InternetGatewayDevice.DeviceInfo.SoftwareVersion",
                          "InternetGatewayDevice.WANDevice.1.X_TGT_WANNetConfigInfo.SINR2",
                          "InternetGatewayDevice.WANDevice.1.X_TGT_WANNetConfigInfo.SINR1",
                          "InternetGatewayDevice.WEB_GUI.Network.LTE_Setting.Status.RSRP2",
                          "InternetGatewayDevice.WEB_GUI.Network.LTE_Setting.Status.RSRP1",
                          "InternetGatewayDevice.WANDevice.1.WANConnectionDevice.1.WANIPConnection.1.ExternalIPAddress",
                          "InternetGatewayDevice.WEB_GUI.Overview.DeviceInfo.IMSI",
                          "InternetGatewayDevice.WEB_GUI.Overview.DeviceInfo.IMEI",
                          "InternetGatewayDevice.WANDevice.1.X_TGT_WANNetConfigInfo.EnodeBId",
                          "InternetGatewayDevice.DeviceInfo.MemoryStatus.Free",
                          "InternetGatewayDevice.WANDevice.1.X_TGT_WANNetConfigInfo.EnodeBId",
                          "InternetGatewayDevice.WEB_GUI.Network.LTE_Setting.Status.DL_MCS",
                          "InternetGatewayDevice.Time.CurrentLocalTime",
                          "InternetGatewayDevice.DeviceInfo.ProcessStatus.CPUUsage",
                          "InternetGatewayDevice.WANDevice.1.X_TGT_WANNetConfigInfo.CellId",
                          "InternetGatewayDevice.WANDevice.1.WANEthernetInterfaceConfig.Stats.BytesSent",
                          "InternetGatewayDevice.WANDevice.1.WANEthernetInterfaceConfig.Stats.BytesReceived",
                          "InternetGatewayDevice.WANDevice.1.WANEthernetInterfaceConfig.Stats.PacketsReceived",
                          "InternetGatewayDevice.WANDevice.1.WANEthernetInterfaceConfig.Stats.PacketsSent",
                          "InternetGatewayDevice.DeviceInfo.UpTime",
                          "InternetGatewayDevice.DeviceInfo.X_TGT_IMSI"
};

//</editor-fold>


void ZBX_Send(char **Name, char **Value, char **imsi) {
    if (!strcmp(*Name, "InternetGatewayDevice.WEB_GUI.Network.LTE_Setting.Status.DL_MCS") == 0 or
        !strcmp(*Name, "InternetGatewayDevice.WEB_GUI.Network.LTE_Setting.Status.UL_MCS") == 0) {
        if (!strcmp(*Value, "QPSK")) {
            *Value = "4";
        } else if (!strcmp(*Value, "QAM8")) {
            *Value = "8";
        } else if (!strcmp(*Value, "QAM16")) {
            *Value = "16";
        } else if (!strcmp(*Value, "QAM32")) {
            *Value = "32";
        } else if (!strcmp(*Value, "QAM64")) {
            *Value = "64";
        }
        std::ofstream zbx_data;
        time_t second;
        second = time(NULL);
        zbx_data.open("/var/tmp/zbx_sender_data", std::ios_base::app);
        zbx_data << "IMSI-" << *imsi << " " << *Name << " " << second << " \"" << *Value << "\"" << std::endl;

    }
}


int main() {
    signal(SIGPIPE, handler);
    std::cout << "TR069 Server Started (1.0)...\n" << std::endl;
    cwmpService service(SOAP_XML_INDENT);
    service.soap->send_timeout = service.soap->recv_timeout = 5;
    service.soap->transfer_timeout = 30;

    service.soap->keep_alive = 1;
    service.soap->tcp_keep_alive = 1;
    service.soap->bind_flags |= SO_REUSEADDR;
    service.soap->bind_flags |= SO_REUSEPORT;
    service.soap->bind_flags |= MSG_NOSIGNAL;

    soap_init2(service.soap, SOAP_IO_KEEPALIVE, SOAP_IO_KEEPALIVE);
    SOAP_SOCKET m = service.bind(NULL, port, 1);              /* master socket */
    if (soap_valid_socket(m)) {
        while (soap_valid_socket(service.accept())) {
            THREAD_TYPE tid;
            void *arg = (void *) service.copy();
            if (arg)
                while (THREAD_CREATE(&tid, (void *(*)(void *)) process_request, arg))
                    sleep(1);
        }
    }
    service.soap_stream_fault(std::cerr);
    service.destroy(); /* clean up */

    return 0;
}


int POST_handler(struct soap *soap) {
//    struct _cwmp__GetParameterValues *res = soap_new__cwmp__GetParameterValues(soap, -1);
//    struct ParameterNames *list = soap_new_ParameterNames(soap, -1);
    const char *buf;
    size_t len;
    buf = soap_http_get_body(soap, &len);
//    std::cerr<<"Sassan3 ID:  "<<soap->header->cwmp__SupportedCWMPVersions<<std::endl;
//    std::cerr<<"Debug1: "<<len<<std::endl;
//    std::cerr<<"Debug2: "<<buf<<std::endl;
    /* if HTTP has a content type header or a HTTP body is present then return 404 */
    if (len == 0) {
        std::cerr << "Sassan 204...: " << buf << std::endl;
        return 204; // HTTP 400 Bad Request
//    }else {
//        std::cerr<<"Debug0: "<<buf<<std::endl;
//        std::cerr<<"Debug1: "<<len<<std::endl;
//        std::cerr<<"Debug2: "<<buf<<std::endl;
    }

//    if (soap->http_content || soap_http_has_body(soap))
//        return 404;
//    (void)soap_end_recv(soap);
//    /* populate the response with one array item */
//    if (res && list)
//    {
//        list->__size = 1;
//        if ((list->__ptrstring = (char**)soap_malloc(soap, list->__size * sizeof(char*))) != NULL)
//        {
//            list->__ptrstring[0] = soap_strdup(soap, "...");
//            res->ParameterNames = list;
//        }
//    }
//    /* send response */
//    soap->encodingStyle = "";
//    soap_serializeheader(soap);
//    soap_serialize__cwmp__GetParameterValues(soap, res);
//    if (soap_begin_count(soap))
//        return soap->error;
//    if (soap->mode & SOAP_IO_LENGTH)
//    {
//        if (soap_envelope_begin_out(soap)
//            || soap_putheader(soap)
//            || soap_body_begin_out(soap)
//            || soap_put__cwmp__GetParameterValues(soap, res, "cwmp:GetParameterValues", "")
//            || soap_body_end_out(soap)
//            || soap_envelope_end_out(soap))
//            return soap->error;
//    };
//    if (soap_end_count(soap)
//        || soap_response(soap, SOAP_OK)
//        || soap_envelope_begin_out(soap)
//        || soap_putheader(soap)
//        || soap_body_begin_out(soap)
//        || soap_put__cwmp__GetParameterValues(soap, res, "cwmp:GetParameterValues", "")
//        || soap_body_end_out(soap)
//        || soap_envelope_end_out(soap)
//        || soap_end_send(soap))
//        return soap->error;
//    return soap_closesock(soap);
}

int cwmpService::GetParameterNames(struct _cwmp__GetParameterNames *cwmp__GetParameterNames,
                                   struct _cwmp__GetParameterNamesResponse *cwmp__GetParameterNamesResponse) { return SOAP_OK; }

int cwmpService::GetParameterValues(struct _cwmp__GetParameterValues *cwmp__GetParameterValues,
                                    struct _cwmp__GetParameterValuesResponse *cwmp__GetParameterValuesResponse) { return SOAP_OK; }

int cwmpService::SetParameterValues(struct _cwmp__SetParameterValues *cwmp__SetParameterValues,
                                    struct _cwmp__SetParameterValuesResponse *cwmp__SetParameterValuesResponse) { return SOAP_OK; }

int cwmpService::SetParameterAttributes(
        struct _cwmp__SetParameterAttributes *cwmp__SetParameterAttributes,
        struct _cwmp__SetParameterAttributesResponse *cwmp__SetParameterAttributesResponse) { return SOAP_OK; }

int cwmpService::GetParameterAttributes(struct _cwmp__GetParameterAttributes *cwmp__GetParameterAttributes,
                                        struct _cwmp__GetParameterAttributesResponse *cwmp__GetParameterAttributesResponse) { return SOAP_OK; }

int cwmpService::Download(struct _cwmp__Download *cwmp__Download,
                          struct _cwmp__DownloadResponse *cwmp__DownloadResponse) { return SOAP_OK; }

int cwmpService::Upload(
        struct _cwmp__Upload *cwmp__Upload,
        struct _cwmp__UploadResponse *cwmp__UploadResponse) { return SOAP_OK; }

int cwmpService::AddObject(
        struct _cwmp__AddObject *cwmp__AddObject,
        struct _cwmp__AddObjectResponse *cwmp__AddObjectResponse) { return SOAP_OK; }

int cwmpService::DeleteObject(
        struct _cwmp__DeleteObject *cwmp__DeleteObject,
        struct _cwmp__DeleteObjectResponse *cwmp__DeleteObjectResponse) { return SOAP_OK; }

int cwmpService::GetRPCMethods(
        struct _cwmp__GetRPCMethods *cwmp__GetRPCMethods,
        struct _cwmp__GetRPCMethodsResponse *cwmp__GetRPCMethodsResponse) { return SOAP_OK; }

int cwmpService::GetParameterValuesResponse(
        struct _cwmp__GetParameterValuesResponse *cwmp__GetParameterValuesResponse) { return SOAP_OK; }

int cwmpService::Inform(struct _cwmp__Inform *cwmp__Inform,
                        struct _cwmp__InformResponse *cwmp__InformResponse) {


    std::cerr << "Inform Recieved..." << cwmp__Inform->DeviceId->SerialNumber <<" at "<<time(NULL)<< std::endl;
    char *imsi;
    auto timestamp = std::chrono::high_resolution_clock::now();
    std::vector<std::thread> th;
//    int indexes[24];
    std::vector<int> ind;
    for (int i = 0 ; i < cwmp__Inform->ParameterList->__size; ++i) {
        if (!strcmp(cwmp__Inform->ParameterList->__ptrParameterValueStruct[i]->Name,
                    "InternetGatewayDevice.DeviceInfo.X_TGT_IMSI")) {
            imsi = cwmp__Inform->ParameterList->__ptrParameterValueStruct[i]->Value;
        }
        std::string cpe = cwmp__Inform->DeviceId->SerialNumber;
        auto j = std::find(std::begin(zbx_keys), std::end(zbx_keys),
                           cwmp__Inform->ParameterList->__ptrParameterValueStruct[i]->Name);
        if (j != std::end(zbx_keys)) {
            th.push_back(std::thread(ZBX_Send, &cwmp__Inform->ParameterList->__ptrParameterValueStruct[i]->Name,
                                     &cwmp__Inform->ParameterList->__ptrParameterValueStruct[i]->Value, &imsi));

            ind.push_back(i);
        }
    }
    for (auto &t : th) {
        t.join();
    }

//    std::ofstream influx_data;
//    time_t second;
//    second = time(NULL);
//    influx_data.open("/var/tmp/influxdb_data", std::ios_base::app);
//    influx_data << "tr069,imsi=" << imsi << " " ;
//    try {
//
//        for (std::vector<int>::iterator it = ind.begin(); it != ind.end(); ++it){
//            influx_data << cwmp__Inform->ParameterList->__ptrParameterValueStruct[*it]->Name << "=\""
//                      << cwmp__Inform->ParameterList->__ptrParameterValueStruct[*it]->Value << "\",";
//        }
//        influx_data<<std::endl;
//    }catch (std::exception e){std::cerr<<e.what()<<std::endl;}


    return SOAP_OK;
}
