#include <stdio.h>
#include <sys/resource.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/timerfd.h>
#include <errno.h>
#include <syslog.h>
#include <string.h>

#include "dy_common.h"
#include "cJSON.h"
#include "dy_pp.h"

static int macs_info_parse(mac_info_table_t **p_tab, cJSON *item)
{
    if (!p_tab)
    {
        dy_syslog(LOG_ERR, "Invalid parameters");
        return -1;
    }

    mac_info_table_t *tab = NULL;
    int cnt = 0;
    char buff[100];
    int i;

    if (item)
    {
        cnt = cJSON_GetArraySize(item);
    }
    tab = calloc(1, sizeof(mac_info_table_t) + cnt * sizeof(mac_info_t));
    if (tab == NULL)
    {
        dy_syslog(LOG_ERR, "malloc failed, %d", cnt);
        return -1;
    }

    for (i = 0; i < cnt; i++)
    {
        cJSON *mac = cJSON_GetArrayItem(item, i);

        GET_JSON_VALUE_STRING(mac, "sign_mark", tab->mac[i].sign_mark);
        GET_JSON_VALUE_INT(mac, "offset", tab->mac[i].offset);
        GET_JSON_VALUE_STRING(mac, "raw_data_type", buff);
        tab->mac[i].raw_data_type = datatype_char2enum(buff);
        GET_JSON_VALUE_DY_STRING(mac, "desc", tab->mac[i].desc);
    }

    tab->macCnt = cnt;
    *p_tab = tab;

    return 0;
}
int tags_info_parse(tag_table_cfg_t **p_tab, cJSON *item)
{
    if (!p_tab)
    {
        dy_syslog(LOG_ERR, "Invalid parameters");
        return -1;
    }

    char buff[100];
    int tagCnt = 0;
    tag_table_cfg_t *tag_tab = NULL;
    int i;

    if (item)
    {
        tagCnt = cJSON_GetArraySize(item);
    }

    tag_tab = calloc(1, sizeof(tag_table_cfg_t) + tagCnt * sizeof(tag_cfg_t));
    if (tag_tab == NULL)
    {
        dy_syslog(LOG_ERR, "malloc failed, %d", tagCnt);
        return -1;
    }
    for (i = 0; i < tagCnt; i++)
    {
        int m;
        cJSON *tag = cJSON_GetArrayItem(item, i);

        GET_JSON_VALUE_INT(tag, "instruction_code", tag_tab->tag[i].instruction_code);
        GET_JSON_VALUE_STRING(tag, "identifier", tag_tab->tag[i].identifier);
        GET_JSON_VALUE_INT(tag, "report_period", tag_tab->tag[i].report_period);

        cJSON *params = cJSON_GetObjectItem(tag, "param");
        int paramCnt = cJSON_GetArraySize(params);
        tag_param_table_t *tag_param_tab = calloc(1, sizeof(tag_param_table_t) + paramCnt * sizeof(tag_param_t));

        if (tag_param_tab == NULL)
        {
            dy_syslog(LOG_ERR, "malloc failed, %d", paramCnt);
            continue;
        }

        for (m = 0; m < paramCnt; m++)
        {
            cJSON *param = cJSON_GetArrayItem(params, m);
            if (param)
            {
                GET_JSON_VALUE_STRING(param, "sign_mark", tag_param_tab->param[m].sign_mark);
                GET_JSON_VALUE_STRING(param, "reference_length", tag_param_tab->param[m].reference_length);
                GET_JSON_VALUE_DOUBLE(param, "k", tag_param_tab->param[m].k);
                GET_JSON_VALUE_DOUBLE(param, "b", tag_param_tab->param[m].b);
                GET_JSON_VALUE_STRING(param, "raw_data_type", buff);
                tag_param_tab->param[m].raw_data_type = datatype_char2enum(buff);
            }
        }
        tag_param_tab->paramCnt = paramCnt;
        tag_tab->tag[i].param_tab = tag_param_tab;
    }

    tag_tab->tagCnt = tagCnt;
    *p_tab = tag_tab;
    return 0;
}

int services_info_parse(service_table_t **p_tab, cJSON *item)
{
    if (!p_tab)
    {
        dy_syslog(LOG_ERR, "Invalid parameters");
        return -1;
    }
    int serviceCnt = 0;
    service_table_t *service_tab = NULL;
    char buff[100];
    int i;

    if (item)
    {
        serviceCnt = cJSON_GetArraySize(item);
    }
    service_tab = calloc(1, sizeof(service_table_t) + serviceCnt * sizeof(service_cfg_t));
    if (service_tab == NULL)
    {
        dy_syslog(LOG_ERR, "malloc failed, %d", serviceCnt);
        return -1;
    }

    for (i = 0; i < serviceCnt; i++)
    {
        int m;
        double server_period = 0;
        cJSON *tag = cJSON_GetArrayItem(item, i);
        GET_JSON_VALUE_INT(tag, "instruction_code", service_tab->service[i].instruction_code);
        GET_JSON_VALUE_DOUBLE(tag, "server_period", server_period);
        GET_JSON_VALUE_INT(tag, "direction", service_tab->service[i].direction);
        GET_JSON_VALUE_STRING(tag, "identifier", service_tab->service[i].identifier);
        GET_JSON_VALUE_INT(tag, "report_period", service_tab->service[i].report_period);

        service_tab->service[i].server_period = (int)(server_period * 1000);
        cJSON *params = cJSON_GetObjectItem(tag, "param");
        int paramCnt = cJSON_GetArraySize(params);
        service_param_table_t *service_param_tab = calloc(1, sizeof(service_param_table_t) + paramCnt * sizeof(service_param_t));
        if (service_param_tab == NULL)
        {
            dy_syslog(LOG_ERR, "malloc failed, %d", paramCnt);
            continue;
        }

        for (m = 0; m < paramCnt; m++)
        {
            cJSON *param = cJSON_GetArrayItem(params, m);
            if (param)
            {
                GET_JSON_VALUE_STRING(param, "sign_mark", service_param_tab->param[m].sign_mark);
                GET_JSON_VALUE_STRING(param, "reference_length", service_param_tab->param[m].reference_length);
                GET_JSON_VALUE_INT(param, "sign_val", service_param_tab->param[m].sign_val);
                GET_JSON_VALUE_INT(param, "min", service_param_tab->param[m].min);
                GET_JSON_VALUE_INT(param, "max", service_param_tab->param[m].max);
                GET_JSON_VALUE_DOUBLE(param, "k", service_param_tab->param[m].k);
                GET_JSON_VALUE_DOUBLE(param, "b", service_param_tab->param[m].b);
                GET_JSON_VALUE_INT(param, "dynamic", service_param_tab->param[m].dynamic);
                GET_JSON_VALUE_STRING(param, "raw_data_type", buff);
                service_param_tab->param[m].raw_data_type = datatype_char2enum(buff);
                GET_JSON_VALUE_DY_STRING(param, "desc", service_param_tab->param[m].desc);
                cJSON *bit_fields = cJSON_GetObjectItem(param, "bit_fields");
                if (bit_fields)
                {
                    int bitf_cnt = cJSON_GetArraySize(bit_fields);
                    service_param_tab->param[m].bit_fields = calloc(bitf_cnt, sizeof(bit_field_t));
                    service_param_tab->param[m].bit_cnt = bitf_cnt;
                    int j = 0;

                    for (j = 0; j < bitf_cnt; j++)
                    {
                        cJSON *field = cJSON_GetArrayItem(bit_fields, j);
                        GET_JSON_VALUE_STRING(field, "sign_mark", service_param_tab->param[m].bit_fields[j].sign_mark);
                        GET_JSON_VALUE_INT(field, "start", service_param_tab->param[m].bit_fields[j].start);
                        GET_JSON_VALUE_INT(field, "count", service_param_tab->param[m].bit_fields[j].count);
                    }
                }
                else
                {
                    service_param_tab->param[m].bit_cnt = 0;
                    service_param_tab->param[m].bit_fields = NULL;
                }
            }
        }
        service_param_tab->paramCnt = paramCnt;
        service_tab->service[i].param_tab = service_param_tab;
    }

    service_tab->serviceCnt = serviceCnt;
    *p_tab = service_tab;
    return 0;
}

int event_info_parse(event_table_t **p_tab, cJSON *item)
{
    if (!p_tab)
    {
        dy_syslog(LOG_ERR, "Invalid parameters");
        return -1;
    }
    int eventCnt = 0;
    event_table_t *event_tab = NULL;
    char buff[100];
    int i;

    if (item)
    {
        eventCnt = cJSON_GetArraySize(item);
    }
    event_tab = calloc(1, sizeof(event_table_t) + eventCnt * sizeof(event_cfg_t));
    if (event_tab == NULL)
    {
        dy_syslog(LOG_ERR, "malloc failed, %d", eventCnt);
        return -1;
    }

    for (i = 0; i < eventCnt; i++)
    {
        int m;
        cJSON *tag = cJSON_GetArrayItem(item, i);
        GET_JSON_VALUE_INT(tag, "instruction_code", event_tab->event[i].instruction_code);
        GET_JSON_VALUE_STRING(tag, "identifier", event_tab->event[i].identifier);
        GET_JSON_VALUE_INT(tag, "report_period", event_tab->event[i].report_period);

        cJSON *params = cJSON_GetObjectItem(tag, "param");
        int paramCnt = cJSON_GetArraySize(params);
        event_param_table_t *event_param_tab = calloc(1, sizeof(event_param_table_t) + paramCnt * sizeof(event_param_t));

        if (event_param_tab == NULL)
        {
            dy_syslog(LOG_ERR, "malloc failed, %d", paramCnt);
            continue;
        }
        for (m = 0; m < paramCnt; m++)
        {
            cJSON *param = cJSON_GetArrayItem(params, m);
            if (param)
            {
                GET_JSON_VALUE_STRING(param, "sign_mark", event_param_tab->param[m].sign_mark);
                GET_JSON_VALUE_STRING(param, "reference_length", event_param_tab->param[m].reference_length);
                GET_JSON_VALUE_DOUBLE(param, "k", event_param_tab->param[m].k);
                GET_JSON_VALUE_DOUBLE(param, "b", event_param_tab->param[m].b);
                GET_JSON_VALUE_STRING(param, "raw_data_type", buff);
                event_param_tab->param[m].raw_data_type = datatype_char2enum(buff);
                cJSON *bit_fields = cJSON_GetObjectItem(param, "bit_fields");
                if (bit_fields)
                {
                    int bitf_cnt = cJSON_GetArraySize(bit_fields);
                    event_param_tab->param[m].bit_fields = calloc(bitf_cnt, sizeof(bit_field_t));
                    event_param_tab->param[m].bit_cnt = bitf_cnt;
                    int j = 0;

                    for (j = 0; j < bitf_cnt; j++)
                    {
                        cJSON *field = cJSON_GetArrayItem(bit_fields, j);
                        GET_JSON_VALUE_STRING(field, "sign_mark", event_param_tab->param[m].bit_fields[j].sign_mark);
                        GET_JSON_VALUE_INT(field, "start", event_param_tab->param[m].bit_fields[j].start);
                        GET_JSON_VALUE_INT(field, "count", event_param_tab->param[m].bit_fields[j].count);
                    }
                }
                else
                {
                    event_param_tab->param[m].bit_cnt = 0;
                    event_param_tab->param[m].bit_fields = NULL;
                }
            }
        }
        event_param_tab->paramCnt = paramCnt;
        event_tab->event[i].param_tab = event_param_tab;
    }

    event_tab->eventCnt = eventCnt;
    *p_tab = event_tab;
    return 0;
}

static int dev_script_overlow(char *url, char *script, int len)
{
    char buff[100];
    struct stat stat_info;
    int ret;
    int retry_cnt = 0;
    char *file = NULL;

    file = GetFileName(url);
    snprintf(buff, sizeof(buff), "%s/%s", PARSE_SCRIPT_DIR, file);

retry:
    ret = stat(buff, &stat_info);
    if (ret == 0 && stat_info.st_size > 0)
    {
        snprintf(script, len, "%s", file);
        return 0;
    }
    else
    {
        // file not exist
        download_file(PARSE_SCRIPT_DIR, url, 5);
        retry_cnt++;
        if (retry_cnt < 10)
        {
            goto retry;
        }
    }
    memset(script, 0, len);
    dy_syslog(LOG_ERR, "parse script %s ,can't find !", buff);
    return -1;
}

static int update_dc_cfg(cJSON * json)
{
    int ret = ERR_CODE_NONE;
	cJSON * root = NULL;
	int time_limit = 0;
	char *data = NULL;
	char * data_tmp = NULL;

    if (!json)
    {
        return ERR_CODE_JSON_FORMAT;
    }
	
    data = read_file_data(DC_CONFIG_FILE_PATH);
    if (data == NULL)
    {
		root = cJSON_CreateObject();
    }
	else
	{
		root = cJSON_Parse(data);
		if (!root)
		{
			root = cJSON_CreateObject();
		}
	}

	if (cJSON_HasObjectItem(json,"time_limit"))
	{
		if (cJSON_HasObjectItem(root,"time_limit"))
		{
			cJSON_DeleteItemFromObject(root,"time_limit");
		}
    	GET_JSON_VALUE_INT(json, "time_limit", time_limit);
		cJSON_AddNumberToObject(root, "time_limit", time_limit);
	}
    else
    {
        cJSON *identifier_new = cJSON_GetObjectItem(json, "identifier_list");
        int new_cnt = 0;
        if ((identifier_new == NULL) || ((new_cnt = cJSON_GetArraySize(identifier_new)) <= 0))
        {
            dy_syslog(LOG_DEBUG, "update_dc_cfg nothing to do");
            goto update_dc_cfg_end;
        }

        if (!cJSON_HasObjectItem(root,"identifier_list"))
        {
            cJSON *arr = cJSON_CreateArray();
            if (arr == NULL)
            {
                dy_syslog(LOG_ERR, "cJSON_CreateArray return NULL");
                ret = ERR_CODE_NODE_NOT_EXIST;
                goto update_dc_cfg_end;
            }
            cJSON_AddItemToObject(root, "identifier_list", arr);
        }

        cJSON *tmp_json = NULL;
        cJSON *identifier_existed = cJSON_GetObjectItem(root, "identifier_list");
        if(identifier_existed == NULL)
        {
            dy_syslog(LOG_ERR, "cJSON_GetObjectItem return NULL");
            ret = ERR_CODE_NODE_NOT_EXIST;
            goto update_dc_cfg_end;
        }

        int i, cnt = cJSON_GetArraySize(identifier_new);
        char identifier[128] = {0};
        for (i = 0; i < cnt; i++)
        {
            cJSON *item = cJSON_CreateObject();
            tmp_json = cJSON_GetArrayItem(identifier_new, i);
            if (tmp_json != NULL)
            {
                GET_JSON_VALUE_STRING(tmp_json, "identifier", identifier);
                cJSON_AddStringToObject(item, "identifier", identifier);	
                cJSON_AddItemToArray(identifier_existed, item);
            }
            
        }
    }
    
	data_tmp = cJSON_PrintUnformatted(root);
    write_file_data(DC_CONFIG_FILE_PATH, data_tmp, strlen(data_tmp));
    cJSON_free(data_tmp);
update_dc_cfg_end:
	cJSON_Delete(root);
	
	return ret;
}

static int clear_dcjson_identifier_list(void)
{
    cJSON *root = NULL;
    char *data = read_file_data(DC_CONFIG_FILE_PATH);
    if (data == NULL)
    {
        root = cJSON_CreateObject();
    }
    else
    {
        root = cJSON_Parse(data);
        if (!root)
        {
            root = cJSON_CreateObject();
        }
    }

    if (cJSON_HasObjectItem(root, "identifier_list"))
    {
        cJSON_DeleteItemFromObject(root, "identifier_list");
    }

    char *data_tmp = cJSON_PrintUnformatted(root);
    write_file_data(DC_CONFIG_FILE_PATH, data_tmp, strlen(data_tmp));
    cJSON_free(data_tmp);
    cJSON_Delete(root);
    return ERR_CODE_NONE;
}

static int set_dc_conf(cJSON * json, int template_index)
{
	int ret = ERR_CODE_NONE;
	int j = 0;
	int dc_enable = 0;
	int direction = 0;
	char identifier[64] = {0};
    dy_syslog(LOG_DEBUG, "template_index->%d", template_index);

    if (template_index == 0)
    {
        clear_dcjson_identifier_list();
    }

	if (json == NULL)
	{
		return -1;
	}

	cJSON *identifer = cJSON_CreateObject();
	if (identifer == NULL)
	{
		ret = -1;
		goto out;
	}
	
	cJSON *iden = cJSON_CreateArray();
	if (iden == NULL)
	{
		ret = -1;
		cJSON_Delete(identifer);
		identifer = NULL;
		goto out;
	}

	cJSON_AddItemToObject(identifer, "identifier_list", iden);

	int count_ser = cJSON_GetArraySize(json);
	
	if (count_ser <= 0)
	{
		ret = -1;
		cJSON_Delete(identifer);
		identifer = NULL;
		goto out;
	}

	for (j = 0; j < count_ser; j++)
	{
		cJSON *dc_filter = cJSON_GetArrayItem(json, j);
		if (dc_filter == NULL)
		{
			continue;
		}
		GET_JSON_VALUE_INT(dc_filter, "periodic_storage", dc_enable);
		GET_JSON_VALUE_INT(dc_filter, "direction", direction);
		if (dc_enable && direction)
		{
			cJSON *item = cJSON_CreateObject();
			GET_JSON_VALUE_STRING(dc_filter, "identifier", identifier);
			cJSON_AddStringToObject(item, "identifier", identifier);	
			cJSON_AddItemToArray(iden, item);
		}
	}
out:
	if (identifer)
    {
		update_dc_cfg(identifer);
        cJSON_Delete(identifer);
    }
	return ret;
}


/**
*******************************************************************************
*
* @brief 读取魔板配置
* @param p_template_table 用于存储魔板配置的结构
* @param cfg_file 配置文件
* @return 0 for success, -1 for error
*
*******************************************************************************
*/
int load_templates_cfg(template_table_t **p_template_table, const char *cfg_file)
{
    char *data = NULL;
    template_table_t *template_table = NULL;
    char buff[100];
    int i;
    cJSON *root = NULL;
    cJSON *templates = NULL;
    cJSON *template = NULL;
    int ret = 0;
    int cnt = 0;

    if (p_template_table == NULL || cfg_file == NULL)
    {
        dy_syslog(LOG_ERR, "p_template_table:%p cfg_file:%p", p_template_table, cfg_file);
        return -1;
    }

    data = read_file_data(cfg_file);
    if (data == NULL)
    {
        dy_syslog(LOG_ERR, "read cfg file %s error", cfg_file);
        ret = -1;
        goto out;
    }

    {
        char *new = download_url_data(data, TEMPLATES_CFG_PATH);
        if (new != NULL)
        {
            free(data);
            data = new;
        }
    }

    root = cJSON_Parse(data);
    if (!root)
    {
        dy_syslog(LOG_ERR, "parse cfg file %s error", cfg_file);
        ret = -1;
        goto out;
    }
    templates = cJSON_GetObjectItem(root, "template_cfg");
    if (!templates)
    {
        dy_syslog(LOG_ERR, "%s get template_cfg error", cfg_file);
        ret = -1;
        goto out;
    }

    cnt = cJSON_GetArraySize(templates);
    template_table = calloc(1, sizeof(template_table_t) + cnt * sizeof(template_cfg_t));
    if (template_table == NULL)
    {
        dy_syslog(LOG_ERR, "malloc failed, %d", cnt);
        ret = -1;
        goto out;
    }

    for (i = 0; i < cnt; i++)
    {
        template = cJSON_GetArrayItem(templates, i);
        if (template)
        {
            char script_path[256] = {0};

            GET_JSON_VALUE_STRING(template, "template_id", template_table->template[i].template_id);
            GET_JSON_VALUE_STRING(template, "protocol", buff);
            template_table->template[i].protocol = protocol_char2enum(buff);
            GET_JSON_VALUE_INT(template, "offline_times", template_table->template[i].offline_times);
            GET_JSON_VALUE_INT(template, "report_period", template_table->template[i].report_period);
            GET_JSON_VALUE_INT(template, "logout_times", template_table->template[i].logout_times);
            GET_JSON_VALUE_INT(template, "communication_timeout", template_table->template[i].communication_timeout);
            GET_JSON_VALUE_INT(template, "use_parser_url", template_table->template[i].use_parser_url);
            GET_JSON_VALUE_INT(template, "script_scope", template_table->template[i].script_scope);
            GET_JSON_VALUE_DY_STRING(template, "parser_url", template_table->template[i].parser_url);
            GET_JSON_VALUE_DY_STRING(template, "upgrade_url", template_table->template[i].upgrade_url);

            if (template_table->template[i].parser_url != NULL && strlen(template_table->template[i].parser_url) > 0)
            {
                dev_script_overlow(template_table->template[i].parser_url, script_path, sizeof(script_path));
                template_table->template[i].parser_script = strdup(script_path);
            }

            if (template_table->template[i].upgrade_url != NULL && strlen(template_table->template[i].upgrade_url) > 0)
            {
                dev_script_overlow(template_table->template[i].upgrade_url, script_path, sizeof(script_path));
                template_table->template[i].upgrade_script = strdup(script_path);
            }

            macs_info_parse(&template_table->template[i].mac_tab, cJSON_GetObjectItem(template, "mac_info"));
            tags_info_parse(&template_table->template[i].tag_tab, cJSON_GetObjectItem(template, "tag_cfg"));
            services_info_parse(&template_table->template[i].service_tab, cJSON_GetObjectItem(template, "service_cfg"));
			set_dc_conf(cJSON_GetObjectItem(template, "service_cfg"), i);
            event_info_parse(&template_table->template[i].event_tab, cJSON_GetObjectItem(template, "event_cfg"));
        }
    }

    template_table->template_cnt = cnt;

out:
    cJSON_Delete(root);
    if (data != NULL)
    {
        free(data);
    }
    if (template_table == NULL)
    {
        //分配node cnt等于0的node_cfg出来
        template_table = calloc(1, sizeof(template_table_t));
        template_table->template_cnt = 0;
    }
    *p_template_table = template_table;

    return ret;
}

/**
*******************************************************************************
*
* @brief 根据模板id获取模板配置
* @param template_table 用于存储魔板配置的结构
* @param ptemplate_id 模板ID
* @return 模板的配置
*
*******************************************************************************
*/
template_cfg_t *pp_find_template(template_table_t *template_table, char *ptemplate_id)
{
    int i;

    for (i = 0; i < template_table->template_cnt; i++)
    {
        if (strcmp(ptemplate_id, template_table->template[i].template_id) == 0)
        {
            return &(template_table->template[i]);
        }
    }

    return NULL;
}

/**
*******************************************************************************
*
* @brief 获取指定端口的设备的周期服务列表
* @param nodes_cfg_table 节点配置信息
* @param template_table 用于存储魔板配置的结构
* @param ports 选择的端口数组
* @param cnt 选择的端口个数
* @param[out] period_list 将生成的周期服务list挂在这个头上
* @return 0 - success; -1 - failed;
*
*******************************************************************************
*/
int period_instance_fetch(nodes_cfg_table_t *nodes_cfg_table, template_table_t *template_table, gw_port_e *ports, int cnt, struct list_head *period_list)
{
    int i, j, k, l;

    for (i = 0; i < template_table->template_cnt; i++)
    {
        //解析周期命令
        for (j = 0; j < template_table->template[i].service_tab->serviceCnt; j++)
        {
            if (template_table->template[i].service_tab->service[j].server_period > 0 && !template_table->template[i].service_tab->service[j].direction)
            {
                //节点信息
                for (k = 0; k < nodes_cfg_table->node_cnt; k++)
                {
                    for (l = 0; l < cnt; l++)
                    {
                        if ((nodes_cfg_table->node[k].port == ports[l]) &&
                            strcmp(nodes_cfg_table->node[k].template_id, template_table->template[i].template_id) == 0)
                        {
                            period_service_instance_t *period_node = NULL;
                            period_node = calloc(1, sizeof(period_service_instance_t));
                            if (period_node == NULL)
                            {
                                dy_syslog(LOG_ERR, "malloc failed");
                                abort();
                            }
                            else
                            {
                                strcpy(period_node->sn, nodes_cfg_table->node[k].sn);
                                period_node->pservice = &template_table->template[i].service_tab->service[j];
                                list_add_tail(&period_node->list, period_list);
                            }
                        }
                    }
                }
            }
        }
    }
    return 0;
}
