#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <sys/epoll.h>
#include <sys/time.h>
#include <sys/resource.h>

#include "vint_common.h"
#include "libubox/utils.h"
#include "dy_utils/dy_ipc.h"

#define MAXEPOLLSIZE 10000
#define LISTEN_BACKLOG 10
#define DEFAULT_PORT  45678


static int tcp_server_loop = 0;

static int send_tcp_soft_info(vint_var_t *var, char *data, payload_data_hdr_t *hdr)
{
    cJSON *root;
    char tmp[512] = {0};
    char ver_str[32] = {0};
    char p_ver[32] = {0};
    int ver;
    float pver;
    char *p = NULL;
    register_info_t psoft = {0};
    node_message_action_t nm_action = {0};
    if (data == NULL)
    {
        return -1;
    }
    strncpy(tmp, data, 512);
    tmp[strlen(tmp) - 5] = 0;
    p = tmp + 1;
    root = cJSON_Parse(p);
    if (!root)
    {
        dy_syslog(LOG_ERR, "parse error");
        return -1;
    }
    GET_JSON_VALUE_STRING(root, "ver", ver_str);
    GET_JSON_VALUE_STRING(root, "pver", p_ver);
    cJSON_Delete(root);
    sscanf(ver_str, "V%d-%*s", &ver);
    sscanf(p_ver, "%f", &pver);
    psoft.soft_ver = ver;
    psoft.protocol_ver = pver;
    nm_action.action = ACTION_SOFT_INFO;
    nm_action.port = VINTF_MQTT;
    strcpy(nm_action.sn, hdr->sn);
    memcpy(nm_action.res, &psoft, sizeof(psoft));
    ds_flush_status_action(var->ds, &nm_action);

    return 0;
}


// `{"T":"ECX/210142FF00FF/R_LogIn","ts":536533253,"mi":1,
// "ver":"V2035-test9","pver":"6.0"}86EB'
static char *parse_data(unsigned char *buf, int len, payload_data_hdr_t *hdr)
{
    char *data = malloc(len + 1);

    if (strstr(buf, "`{\"K\":") || strstr(buf, "`{\"X\":"))
    {
        // 压缩协议
        char topic[128];
        char *new_data = NULL;
        int new_len;
        char sn_str[32];

        int count = sscanf(buf, "`{%[^:]:%[^}]%*s", topic, data);
        if (count != 2)
        {
            dy_syslog(LOG_ERR, "match buf failed, buf:%s", buf);
            free(data);
            return NULL;
        }
        if (topic[0] == '\"' && topic[strlen(topic) - 1 ] == '\"')
        {
            topic[strlen(topic) - 1] = 0;
        }
        if (data[0] == '\"' && data[strlen(data) - 1 ] == '\"')
        {
            data[strlen(data) - 1] = 0;
        }
        strncpy(hdr->cmd, topic + 1, sizeof(hdr->cmd));
        new_data = malloc(256);
        if (new_data == NULL)
        {
            dy_syslog(LOG_ERR, "malloc failed");
            free(data);
            return NULL;
        }
        dy_syslog(LOG_INFO, "data:%s", data + 1);
        new_len = b64_decode(data + 1, new_data, 256);
        free(data);
        data = new_data;
        len = new_len;
        sprintf(sn_str, "2101%02X%02X%02X%02X", (uint8_t)data[0], (uint8_t)data[1], (uint8_t)data[2], (uint8_t)data[3]);
        hdr->mi = data[4];
        strcpy(hdr->sn, sn_str);
        strcpy(hdr->source, "RTU");
        dy_syslog_hex(LOG_DEBUG, data, len, "receive data cmd:%s from SN:%s new_len:%d", hdr->cmd, hdr->sn, new_len);

        return data;
    }
    else
    {
        int in = 0;
        int i = 0;
        char *p[128] = {0};
        char *t[6] = {0};
        char *outer_ptr = NULL;
        char *inner_ptr = NULL;

        memcpy(data, buf, len + 1);
        while ((p[in] = strtok_r(buf, ",", &outer_ptr)) != NULL)
        {
            buf = p[in];
            while ((p[in] = strtok_r(buf, ":", &inner_ptr)) != NULL)
            {
                in++;
                buf = NULL;
            }
            buf = NULL;
        }
        for (int j = 0; j < in; j += 2)
        {
            if (strstr(p[j], "\"T\""))
            {
                dy_syslog(LOG_INFO, "topic:%s\n", p[j + 1] + 1);
                buf = p[j + 1] + 1;
                if (buf[strlen(buf) - 1] == '"')
                {
                    buf[strlen(buf) - 1] = 0;
                }
                i = 0;
                while ((t[i] = strtok_r(buf, "/", &outer_ptr)) != NULL)
                {
                    dy_syslog(LOG_INFO, "t[%d]:%s\n", i, t[i]);
                    i++;
                    buf = NULL;
                }
                strcpy(hdr->source, t[0]);
                strcpy(hdr->sn, t[1]);
                strcpy(hdr->cmd, t[2]);
            }
            if (strstr(p[j], "\"mi\""))
            {
                dy_syslog(LOG_INFO, "mi:%s\n", p[j + 1]);
                sscanf(p[j + 1], "%d", &hdr->mi);
            }
        }
        return data;
    }
}

int handle_dyiot_tcp_data(vint_var_t *var, uint8_t *buf, int len, payload_data_hdr_t *phdr)
{
    pp_real_time_data_t real_data = {0};
    node_cfg_t *p_node = NULL;
    payload_data_hdr_t tmp_hdr = {0};
    payload_data_hdr_t *hdr = &tmp_hdr;
    int ret = NORETURN;
    int i;
    char *data = NULL;
    regulate_cmd_backup_t *fields = NULL;

    data = parse_data(buf, len, hdr);
    if (data == NULL)
    {
        dy_syslog(LOG_ERR, "parse data failed");
        return -1;
    }

    dy_syslog(LOG_INFO, "tcp iot data source:%s sn:%s cmd:%s mi:%d", hdr->source, hdr->sn, hdr->cmd, hdr->mi);
    for (i = 0; i < var->nodes_cfg_table->node_cnt; i++)
    {
        if (strcmp(hdr->sn, var->nodes_cfg_table->node[i].sn) == 0 &&
                var->nodes_cfg_table->node[i].port == VINTF_TCP)
        {
            p_node = &var->nodes_cfg_table->node[i];
        }
    }
    if (p_node == NULL)
    {
        dy_syslog(LOG_INFO, "The device:%s not belong to us", hdr->sn);
        ret = NOT_BELONG_TO_US;
        goto out;
    }

    if (phdr != NULL)
    {
        memcpy(phdr, hdr, sizeof(payload_data_hdr_t));
    }

    // the device use this socket to communicate
    if (strcmp(hdr->cmd, "R_LogIn") == 0)
    {
        send_tcp_soft_info(var, data, hdr);
        flush_device_status_by_sn(var->ds, ACTION_LOGIN, hdr->sn);
    }
    else
    {
        flush_device_status_by_sn(var->ds, ACTION_RCV, hdr->sn);
    }

    //restore the identifier
    {
        char key_str[64] = {0};
        data_info_t *data_info = NULL;

        sprintf(key_str, "%s_%d",  hdr->sn, hdr->mi);

        data_info = var->identifier_backup.get(&var->identifier_backup, key_str);
        if (data_info)
        {
            fields = (regulate_cmd_backup_t *)data_info->data;
        }
    }
    if ((strcmp(hdr->cmd, "HB") == 0) &&
            (strcmp(hdr->source, "ECX") == 0 || strcmp(hdr->source, "RTU") == 0))
    {
        // login and Heart beat
        ret = RETURN_ACK;
    }
    if ((strcmp(hdr->cmd, "R_LogIn") == 0) &&
            (strcmp(hdr->source, "ECX") == 0 || strcmp(hdr->source, "RTU") == 0))
    {
        // login and Heart beat
        ret = RETURN_ACK;
    }
    if ((strcmp(hdr->cmd, "R_Stat") == 0
            || strcmp(hdr->cmd, "R_Warn") == 0
            || strcmp(hdr->cmd, "R_Log") == 0
            || strcmp(hdr->cmd, "K") == 0
            || strcmp(hdr->cmd, "X") == 0)
            && (strcmp(hdr->source, "ECX") == 0 || strcmp(hdr->source, "RTU") == 0))
    {
        // 平台回复通用应答
        ret = RETURN_ACK;
    }

    real_data.port = VINTF_TCP;
    real_data.len = len;
    real_data.data = data;
    strcpy(real_data.sn, hdr->sn);
    real_data.mi = hdr->mi;
    if (fields && fields->mi != 0)
    {
        real_data.mi = fields->mi;
    }

    if (strlen(p_node->sub_template_id))
    {
        strncpy(real_data.sub_template_id, p_node->sub_template_id, strlen(p_node->sub_template_id));
    }
    if ((strcmp(hdr->cmd, "A") == 0 &&
            (strcmp(hdr->source, "ECX") == 0 || strcmp(hdr->source, "RTU") == 0))
            && fields && strlen(fields->src_identifier) != 0)
    {
        // Device side reply ACK to us
        dy_syslog(LOG_INFO, "restore recorded identifier:%s mi:%d ", fields->src_identifier, real_data.mi);
        memcpy(real_data.src_identifier, fields->src_identifier,
               sizeof(real_data.src_identifier));
    }
    else if (strncmp(hdr->cmd, "B_", 2) == 0 && hdr->cmd[2] >= 'A' && hdr->cmd[2] <= 'Z'
             && (strcmp(hdr->source, "ECX") == 0 || strcmp(hdr->source, "RTU") == 0))
    {
        hdr->cmd[0] = 'G';
        strcpy(real_data.src_identifier, hdr->cmd);
    }
    else
    {
        strcpy(real_data.src_identifier, hdr->cmd);
    }
    send_to_proto_parser(var->session_int, &real_data);
out:

    free(data);
    return ret;
}

int handle_unmanned_ship_tcp_data(vint_var_t *var, uint8_t *buf, int len, payload_data_hdr_t *phdr)
{
    pp_real_time_data_t real_data = {0};
    node_cfg_t *p_node = NULL;
    payload_data_hdr_t tmp_hdr = {0};
    payload_data_hdr_t *hdr = &tmp_hdr;
    int ret = NORETURN;
    int i;
    char *data = NULL;
    regulate_cmd_backup_t *fields = NULL;

	int data_len = buf[2];
	int sum = 0;
	char sn_str[32] = {0};
	for(i=0; i<data_len; i++)
	{
		sum += buf[3+i]; 
	}
	sum = sum & 0xFF;
	if(sum != buf[data_len+3])//len -1
	{
		dy_syslog(LOG_WARNING, "CRC ERROR 0x%x 0x%x data_len %d", sum, buf[data_len+3], data_len);
        return -1;
	}

	sprintf(sn_str, "%d", buf[6]*256 + buf[5]);
    hdr->mi = 0;
    strcpy(hdr->sn, sn_str);
	if(buf[3] == 0xE1)
		strcpy(hdr->cmd, "ship_data_report");
	else if(buf[3] == 0xE3)
		strcpy(hdr->cmd, "read_reroute_points");
	data = calloc(1, len);
	memcpy(data, buf, len);

    dy_syslog(LOG_INFO, "tcp iot data source:%s sn:%s cmd:%s mi:%d", hdr->source, hdr->sn, hdr->cmd, hdr->mi);
    for (i = 0; i < var->nodes_cfg_table->node_cnt; i++)
    {
        if (strcmp(hdr->sn, var->nodes_cfg_table->node[i].sn) == 0 &&
                var->nodes_cfg_table->node[i].port == VINTF_TCP)
        {
            p_node = &var->nodes_cfg_table->node[i];
        }
    }
    if (p_node == NULL)
    {
        dy_syslog(LOG_INFO, "The device:%s not belong to us", hdr->sn);
        ret = NOT_BELONG_TO_US;
        goto out;
    }

    if (phdr != NULL)
    {
        memcpy(phdr, hdr, sizeof(payload_data_hdr_t));
    }

    // the device use this socket to communicate
    flush_device_status_by_sn(var->ds, ACTION_RCV, hdr->sn);

    //restore the identifier
    {
        char key_str[64] = {0};
        data_info_t *data_info = NULL;

        sprintf(key_str, "%s_%d",  hdr->sn, hdr->mi);

        data_info = var->identifier_backup.get(&var->identifier_backup, key_str);
        if (data_info)
        {
            fields = (regulate_cmd_backup_t *)data_info->data;
        }
    }
	
    if (strcmp(hdr->cmd, "ship_data_report") == 0 || strcmp(hdr->cmd, "read_reroute_points") == 0)
    {
        // 平台回复通用应答
        ret = NORETURN;
    }

    real_data.port = VINTF_TCP;
    real_data.len = len;
    real_data.data = data;
    strcpy(real_data.sn, hdr->sn);
    real_data.mi = hdr->mi;
    if (fields && fields->mi != 0)
    {
        real_data.mi = fields->mi;
    }

    if (strlen(p_node->sub_template_id))
    {
        strncpy(real_data.sub_template_id, p_node->sub_template_id, strlen(p_node->sub_template_id));
    }
    send_to_proto_parser(var->session_int, &real_data);
out:

    free(data);
    return ret;
}

int handle_puyu_tcp_data(vint_var_t *var, uint8_t *buf, int len, payload_data_hdr_t *phdr)
{
    pp_real_time_data_t real_data = {0};
    node_cfg_t *p_node = NULL;
    payload_data_hdr_t tmp_hdr = {0};
    payload_data_hdr_t *hdr = &tmp_hdr;
    int ret = NORETURN;
    int i;
    char *data = NULL;
    regulate_cmd_backup_t *fields = NULL;

	char *MN = strstr(buf, "MN=");
	if(MN == NULL)
	{
		dy_syslog(LOG_WARNING, "can't find MN %s len %d", buf, len);
        return -1;
	}
	char *MN_END = strstr(MN, ";");
	if(MN_END == NULL)
	{
		dy_syslog(LOG_WARNING, "can't find MN_NED %s len %d", buf, len);
        return -1;
	}
	char sn_str[32] = {0};
	memcpy(sn_str, MN+3, MN_END-MN-3);

    hdr->mi = 0;
    strcpy(hdr->sn, sn_str);
	strcpy(hdr->cmd, "data_report");
	data = calloc(1, len);
	memcpy(data, buf, len);

    dy_syslog(LOG_INFO, "tcp iot data source:%s sn:%s cmd:%s mi:%d", hdr->source, hdr->sn, hdr->cmd, hdr->mi);
    for (i = 0; i < var->nodes_cfg_table->node_cnt; i++)
    {
        if (strcmp(hdr->sn, var->nodes_cfg_table->node[i].sn) == 0 &&
                var->nodes_cfg_table->node[i].port == VINTF_TCP)
        {
            p_node = &var->nodes_cfg_table->node[i];
        }
    }
    if (p_node == NULL)
    {
        dy_syslog(LOG_INFO, "The device:%s not belong to us", hdr->sn);
        ret = NOT_BELONG_TO_US;
        goto out;
    }

    if (phdr != NULL)
    {
        memcpy(phdr, hdr, sizeof(payload_data_hdr_t));
    }

    // the device use this socket to communicate
    flush_device_status_by_sn(var->ds, ACTION_RCV, hdr->sn);

    //restore the identifier
    {
        char key_str[64] = {0};
        data_info_t *data_info = NULL;

        sprintf(key_str, "%s_%d",  hdr->sn, hdr->mi);

        data_info = var->identifier_backup.get(&var->identifier_backup, key_str);
        if (data_info)
        {
            fields = (regulate_cmd_backup_t *)data_info->data;
        }
    }
	
    if (strcmp(hdr->cmd, "data_report") == 0)
    {
        // 平台回复通用应答
        ret = NORETURN;
    }

    real_data.port = VINTF_TCP;
    real_data.len = len;
    real_data.data = data;
    strcpy(real_data.sn, hdr->sn);
    real_data.mi = hdr->mi;
    if (fields && fields->mi != 0)
    {
        real_data.mi = fields->mi;
    }

    if (strlen(p_node->sub_template_id))
    {
        strncpy(real_data.sub_template_id, p_node->sub_template_id, strlen(p_node->sub_template_id));
    }
    send_to_proto_parser(var->session_int, &real_data);
out:

    free(data);
    return ret;
}

int handle_csi_dt_tcp_data(vint_var_t *var, uint8_t *buf, int len, payload_data_hdr_t *phdr)
{
    pp_real_time_data_t real_data = {0};
    node_cfg_t *p_node = NULL;
    payload_data_hdr_t tmp_hdr = {0};
    payload_data_hdr_t *hdr = &tmp_hdr;
    int ret = NORETURN;
    int i;
    char *data = NULL;
    regulate_cmd_backup_t *fields = NULL;

	char *Station_SN = strstr(buf, "Station_SN:");
	if(Station_SN == NULL)
	{
		dy_syslog(LOG_WARNING, "can't find Station_SN %s len %d", buf, len);
        return -1;
	}
	char *Station_SN_END = strstr(Station_SN, ";");
	if(Station_SN_END == NULL)
	{
		dy_syslog(LOG_WARNING, "can't find Station_SN_NED %s len %d", buf, len);
        return -1;
	}
	char sn_str[32] = {0};
	memcpy(sn_str, Station_SN+11, Station_SN_END-Station_SN-11);

    hdr->mi = 0;
    strcpy(hdr->sn, sn_str);
	strcpy(hdr->cmd, "data_report");
	data = calloc(1, len);
	memcpy(data, buf, len);

    dy_syslog(LOG_INFO, "tcp iot data source:%s sn:%s cmd:%s mi:%d", hdr->source, hdr->sn, hdr->cmd, hdr->mi);
    for (i = 0; i < var->nodes_cfg_table->node_cnt; i++)
    {
        if (strcmp(hdr->sn, var->nodes_cfg_table->node[i].sn) == 0 &&
                var->nodes_cfg_table->node[i].port == VINTF_TCP)
        {
            p_node = &var->nodes_cfg_table->node[i];
        }
    }
    if (p_node == NULL)
    {
        dy_syslog(LOG_INFO, "The device:%s not belong to us", hdr->sn);
        ret = NOT_BELONG_TO_US;
        goto out;
    }

    if (phdr != NULL)
    {
        memcpy(phdr, hdr, sizeof(payload_data_hdr_t));
    }

    // the device use this socket to communicate
    flush_device_status_by_sn(var->ds, ACTION_RCV, hdr->sn);

    //restore the identifier
    {
        char key_str[64] = {0};
        data_info_t *data_info = NULL;

        sprintf(key_str, "%s_%d",  hdr->sn, hdr->mi);

        data_info = var->identifier_backup.get(&var->identifier_backup, key_str);
        if (data_info)
        {
            fields = (regulate_cmd_backup_t *)data_info->data;
        }
    }
	
    if (strcmp(hdr->cmd, "data_report") == 0)
    {
        // 平台回复通用应答
        ret = NORETURN;
    }

    real_data.port = VINTF_TCP;
    real_data.len = len;
    real_data.data = data;
    strcpy(real_data.sn, hdr->sn);
    real_data.mi = hdr->mi;
    if (fields && fields->mi != 0)
    {
        real_data.mi = fields->mi;
    }

    if (strlen(p_node->sub_template_id))
    {
        strncpy(real_data.sub_template_id, p_node->sub_template_id, strlen(p_node->sub_template_id));
    }
    send_to_proto_parser(var->session_int, &real_data);
out:

    free(data);
    return ret;
}

int vint_tcp_gen_common_ack(vint_var_t *var, payload_data_hdr_t *hdr, char *buf, int ret)
{
    uint8_t crc_h, crc_l;
    int len;

    buf[0] = 0x60;
    sprintf(buf + 1, "{\"T\":\"M/%s/A\",\"ts\":%ld,\"mi\":%d,\"errid\":%d}",
            hdr->sn,  time(NULL) - UTC_AT_2000_01_01_00_00_00, hdr->mi, ret != RETURN_ACK ? 1401 : 0);
    len = strlen(buf);
    calculate_crc(buf + 1, len - 1);
    // crc stored in buf[len] buf[len+1]
    crc_h = buf[len];
    crc_l = buf[len + 1];
    sprintf(buf + len, "%02X%02X", crc_l, crc_h);
    buf[len + 4] = 0x27;
    buf[len + 5] = 0;

    return 0;
}
