#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 "dy_common.h"
#include "cJSON.h"
#include "libubox/utils.h"
#include "ipc_session.h"
#include "dy_ipc.h"
#include "dy_pp.h"

static error_code_msg_t err_code[] = ERROR_CODE_TAB_INIT;
char *downlink_response(char *sn, int status)
{
    cJSON *node = cJSON_CreateObject();

    cJSON_AddStringToObject(node, "sn", sn);
    cJSON_AddNumberToObject(node, "status", status);
    char *data = cJSON_Print(node);
    cJSON_Delete(node);
    return data;
}

char *service_rs_data(service_cfg_t *pservice, char *sn)
{
    cJSON *node = cJSON_CreateObject();

    cJSON_AddStringToObject(node, "sn", sn);
    cJSON_AddStringToObject(node, "identifier", pservice->identifier);
    cJSON_AddNumberToObject(node, "server_period", pservice->server_period);

    char *data = cJSON_Print(node);
    cJSON_Delete(node);

    return data;
}

/**
*******************************************************************************
*
* @brief 发送数据到协议解析，该函数将收到的数据转成json格式，并以TOPIC_UP_RAW_DATA为topic发布MQTT消息
* @param sesson MQTT会话
* @param real_data 实时数据
* @return success: 0; fail:-1
*
*******************************************************************************
*/
int send_to_proto_parser(ipc_session_t *sesson, pp_real_time_data_t *real_data)
{
    char *data_tmp = NULL;
    char *buf = NULL;
    int buff_len = real_data->len * 2 + 1024;
    cJSON *rt_data = cJSON_CreateObject();
    char topic[TOPIC_MAX_LEN] = {0};
    GET_SN_PROC

    buf = (char *)malloc(buff_len);
    if (buf == NULL)
    {
        if (rt_data != NULL)
            cJSON_Delete(rt_data);
        dy_syslog(LOG_ERR, "malloc fail");
        return -1;
    }

    cJSON_AddStringToObject(rt_data, "port", port_enum2char(real_data->port));
    cJSON_AddNumberToObject(rt_data, "instruction_code", real_data->instruction_code);
    cJSON_AddNumberToObject(rt_data, "mi", real_data->mi);
    cJSON_AddStringToObject(rt_data, "sub_template_id", real_data->sub_template_id);
    cJSON_AddStringToObject(rt_data, "src_identifier", real_data->src_identifier);
    cJSON_AddStringToObject(rt_data, "sn", real_data->sn);
    if (real_data->requester[0]) cJSON_AddStringToObject(rt_data, "requester", real_data->requester);
    cJSON_AddStringToObject(rt_data, "dtu_sn", real_data->dtu_sn);
    cJSON_AddNumberToObject(rt_data, "sub_term_addr_len", real_data->sub_term_addr_len);
    cJSON_AddNumberToObject(rt_data, "len", real_data->len);
    cJSON_AddNumberToObject(rt_data, "ts", real_data->ts);


    b64_encode(real_data->term_addr, sizeof(real_data->term_addr), buf, buff_len);
    cJSON_AddStringToObject(rt_data, "term_addr_b64", buf);

    b64_encode(real_data->data, real_data->len, buf, buff_len);
    cJSON_AddStringToObject(rt_data, "data_b64", buf);

    b64_encode(real_data->down_data, real_data->down_len, buf, buff_len);
    cJSON_AddStringToObject(rt_data, "down_data_b64", buf);

    b64_encode(real_data->sub_term_addr, sizeof(real_data->sub_term_addr), buf, buff_len);
    cJSON_AddStringToObject(rt_data, "sub_term_addr_b64", buf);


    // 以字符串的方式发送数据
    data_tmp = cJSON_Print(rt_data);

    snprintf(topic, TOPIC_MAX_LEN, "ipc/%s/%s/device/%s/data/%s", sn, port_enum2char(real_data->port),
             real_data->sn[0] == '\0' ? "UNKNOWN" : real_data->sn, TOPIC_UP_RAW_DATA);

    ipc_session_publish(sesson, topic, (unsigned char *) data_tmp, (int) strlen(data_tmp));

    // 回收资源
    free(data_tmp);
    free(buf);
    cJSON_Delete(rt_data);

    return 0;
}

/**
*******************************************************************************
*
* @brief 发送数据到协议解析，该函数将收到的数据转成json格式，并以TOPIC_UP_RAW_DATA为topic发布MQTT消息
* @param sesson MQTT会话
* @param real_data 实时数据
* @return success: 0; fail:-1
*
*******************************************************************************
*/
int send_to_proto_parser_by_app_key(ipc_session_t *sesson, pp_real_time_data_t *real_data, char *app_key)
{
    char *data_tmp = NULL;
    char *buf = NULL;
    cJSON *rt_data = cJSON_CreateObject();
    char topic[TOPIC_MAX_LEN] = {0};
    GET_SN_PROC

    if (app_key == NULL || app_key[0] == '\0')
    {
        if (rt_data != NULL)
            cJSON_Delete(rt_data);
        dy_syslog(LOG_ERR, "wrong APP KEY");
        return -1;
    }

    buf = (char *)malloc(MAXBUF);
    if (buf == NULL)
    {
        if (rt_data != NULL)
            cJSON_Delete(rt_data);
        dy_syslog(LOG_ERR, "malloc fail");
        return -1;
    }

    cJSON_AddStringToObject(rt_data, "port", port_enum2char(real_data->port));
    cJSON_AddNumberToObject(rt_data, "instruction_code", real_data->instruction_code);
    cJSON_AddNumberToObject(rt_data, "mi", real_data->mi);
    cJSON_AddStringToObject(rt_data, "sub_template_id", real_data->sub_template_id);
    cJSON_AddStringToObject(rt_data, "src_identifier", real_data->src_identifier);
    cJSON_AddStringToObject(rt_data, "sn", real_data->sn);
    cJSON_AddStringToObject(rt_data, "dtu_sn", real_data->dtu_sn);
    cJSON_AddNumberToObject(rt_data, "sub_term_addr_len", real_data->sub_term_addr_len);
    cJSON_AddNumberToObject(rt_data, "len", real_data->len);
    cJSON_AddNumberToObject(rt_data, "ts", real_data->ts);


    b64_encode(real_data->term_addr, sizeof(real_data->term_addr), buf, MAXBUF);
    cJSON_AddStringToObject(rt_data, "term_addr_b64", buf);

    b64_encode(real_data->data, real_data->len, buf, MAXBUF);
    cJSON_AddStringToObject(rt_data, "data_b64", buf);


    b64_encode(real_data->sub_term_addr, sizeof(real_data->sub_term_addr), buf, MAXBUF);
    cJSON_AddStringToObject(rt_data, "sub_term_addr_b64", buf);

    // 以字符串的方式发送数据
    data_tmp = cJSON_Print(rt_data);

    snprintf(topic, TOPIC_MAX_LEN, "ipc/%s/%s/device/%s/data/%s", sn, app_key,
             real_data->sn[0] == '\0' ? "UNKNOWN" : real_data->sn, TOPIC_UP_RAW_DATA);
    ipc_session_publish(sesson, topic, (unsigned char *) data_tmp, (int) strlen(data_tmp));

    // 回收资源
    free(data_tmp);
    free(buf);
    cJSON_Delete(rt_data);

    return 0;
}

/**
*******************************************************************************
*
* @brief 并以TOPIC_RT_DATA_REPORT为topic发布解析后的数据
* @param sesson MQTT会话
* @param ptag 解析后的数据
*
*******************************************************************************
*/
int pp_send_tag_data(ipc_session_t *sesson, tag_table_t *ptag)
{
    char topic[TOPIC_MAX_LEN] = {0};
    GET_SN_PROC
    if (ptag->tag_node == NULL)
    {
        return 0;
    }
    char *data_tmp = NULL;
    cJSON *tag_data = cJSON_CreateObject();

    cJSON_AddStringToObject(tag_data, "sn", ptag->sn);
    cJSON_AddStringToObject(tag_data, "tag_node", ptag->tag_node);
    cJSON_AddStringToObject(tag_data, "identifier", ptag->identifier);
    if (ptag->requester[0]) cJSON_AddStringToObject(tag_data, "requester", ptag->requester);
    cJSON_AddNumberToObject(tag_data, "mi", ptag->mi);
    cJSON_AddNumberToObject(tag_data, "time", ptag->time);
    cJSON_AddNumberToObject(tag_data, "report", ptag->report);
    cJSON_AddNumberToObject(tag_data, "res", ptag->res);
    cJSON_AddNumberToObject(tag_data, "report_period", ptag->report_period);
    cJSON_AddNumberToObject(tag_data, "data_type", ptag->data_type);                                                                                                                            
    cJSON_AddStringToObject(tag_data, "port", port_enum2char(ptag->port));
    if (ptag->app_key[0])
    {
        cJSON_AddStringToObject(tag_data, "app_key", ptag->app_key);
    }
    if (ptag->ext != NULL)
    {
        cJSON_AddStringToObject(tag_data, "ext", ptag->ext);
    }


    // 以字符串的方式发送数据
    data_tmp = cJSON_Print(tag_data);

    switch (ptag->data_type)
    {
        case DATA_TYPE_PROPERTY:
            snprintf(topic, TOPIC_MAX_LEN, "ipc/%s/%s/device/%s/data/property/%s", sn, ptag->app_key[0] ? ptag->app_key : port_enum2char(ptag->port), ptag->sn, ptag->identifier);
            ipc_session_publish(sesson, topic, (unsigned char *) data_tmp, (int) strlen(data_tmp));
            break;
        case DATA_TYPE_SERVICE:
            snprintf(topic, TOPIC_MAX_LEN, "ipc/%s/%s/device/%s/data/service/%s", sn, ptag->app_key[0] ? ptag->app_key : port_enum2char(ptag->port), ptag->sn, ptag->identifier);
            ipc_session_publish(sesson, topic, (unsigned char *) data_tmp, (int) strlen(data_tmp));
            break;
        case DATA_TYPE_EVENT:
            snprintf(topic, TOPIC_MAX_LEN, "ipc/%s/%s/device/%s/data/event/%s", sn, ptag->app_key[0] ? ptag->app_key : port_enum2char(ptag->port), ptag->sn, ptag->identifier);
            ipc_session_publish(sesson, topic, (unsigned char *) data_tmp, (int) strlen(data_tmp));
            break;
        default:
            break;
    }

    // 回收资源
    free(data_tmp);
    cJSON_Delete(tag_data);

    return 0;
}

int app_manager_send_package_info(ipc_session_t *sesson, app_config_t *app_cfg)
{
    char topic[TOPIC_MAX_LEN] = {0};
    GET_SN_PROC
    if (app_cfg == NULL)
    {
        return 0;
    }
    char *data_tmp = NULL;
    cJSON *package_data = cJSON_CreateObject();

    cJSON_AddStringToObject(package_data, "name", app_cfg->name);
    cJSON_AddStringToObject(package_data, "version", app_cfg->version);
    if (app_cfg->base_url)
        cJSON_AddStringToObject(package_data, "base_url", app_cfg->base_url);
    cJSON_AddStringToObject(package_data, "depend_name", app_cfg->depend_name);
    cJSON_AddStringToObject(package_data, "cmd", app_cfg->cmd);
    cJSON_AddStringToObject(package_data, "check_cmd", app_cfg->check_cmd);

    // 以字符串的方式发送数据
    data_tmp = cJSON_Print(package_data);

    snprintf(topic, TOPIC_MAX_LEN, "ipc/%s/%s/%s", sn, process, TOPIC_SET_DEPENDAPPINSTALL);
    ipc_session_publish(sesson, topic, (unsigned char *) data_tmp, (int) strlen(data_tmp));

    // 回收资源
    free(data_tmp);
    cJSON_Delete(package_data);

    return 0;
}

int gw_common_result(ipc_session_t *sesson, int mi, int code, char *t)
{
    char topic[TOPIC_MAX_LEN] = {0};
    GET_SN_PROC
    cJSON *root = cJSON_CreateObject();
    if (root == NULL)
    {
        return -1;
    }
    cJSON_AddNumberToObject(root, "mi", mi);
    cJSON_AddNumberToObject(root, "timestamp", time(NULL));
    cJSON_AddNumberToObject(root, "error_code", code);
    char *err_msg = NULL;
    int i;
    for (i = 0; i < sizeof(err_code) / sizeof(err_code[0]); i++)
    {
        if ((int)err_code[i].code == code)
        {
            err_msg = err_code[i].msg;
            break;
        }
    }
    cJSON_AddStringToObject(root, "error_msg", err_msg);
    char *str = (cJSON_Print(root));
    cJSON_Delete(root);

    snprintf(topic, TOPIC_MAX_LEN, "G/%s/gateway/device/%s/service/%s", sn, sn, t == NULL ? TOPIC_CMD_RESULT : t);
    dy_syslog(LOG_DEBUG, "pub:topic:%s", topic);
    ipc_session_publish(sesson, topic, (unsigned char *) str, (int) strlen(str));
    free(str);

    return 0;
}

/**
*******************************************************************************
*
* @brief 从json字符串还原出regulate signal数据
* @param rs_data[out] 用于存储regulate signal 数据
* @param json_str json 字符串
* @return success: 0; fail: -1
*
*******************************************************************************
*/
int get_rglt_data_from_json(pp_regulate_signal_t *rs_data, char *json_str)
{
    int ret = 0;

    cJSON *root = cJSON_Parse(json_str);
    if (root)
    {
        char *buf = NULL;
        int len;
        char *tmp = NULL;

        buf = (char *)malloc(10 * 1024);
        if (buf == NULL)
        {
            dy_syslog(LOG_ERR, "malloc failed");
            ret = -1;
            goto out;
        }

        GET_JSON_VALUE_STRING(root, "port", buf);
        rs_data->port = port_char2enum(buf);
        GET_JSON_VALUE_INT(root, "len", rs_data->len);

        GET_JSON_VALUE_DY_STRING(root, "data_b64", tmp);
        if (tmp != NULL)
        {
            if (tmp[0] == '\0')
            {
                free(tmp);
                tmp = NULL;
            }
            else
            {
                len = b64_decode(tmp, buf, 10 * 1024);
                ASSERT(len == rs_data->len);
                rs_data->data = malloc(len);
                if (rs_data->data == NULL)
                {
                    dy_syslog(LOG_ERR, "malloc fail len:%d", len);
                    free(tmp);
                    free(buf);
                    goto out;
                }
                memcpy(rs_data->data, buf, len);
                free(tmp);
                tmp = NULL;
            }
        }

        GET_JSON_VALUE_INT(root, "period", rs_data->period);

        GET_JSON_VALUE_INT(root, "mi", rs_data->mi);
        GET_JSON_VALUE_INT(root, "protocol", rs_data->protocol);

        GET_JSON_VALUE_STRING(root, "term_addr", buf);
        sscanf(buf, "%02X%02X%02X%02X%02X%02X%02X%02X", &rs_data->term_addr[0],
                &rs_data->term_addr[1], &rs_data->term_addr[2], &rs_data->term_addr[3],
                &rs_data->term_addr[4],
                &rs_data->term_addr[5], &rs_data->term_addr[6], &rs_data->term_addr[7]);

        GET_JSON_VALUE_STRING(root, "src_identifier", rs_data->src_identifier);
        GET_JSON_VALUE_STRING(root, "sn", rs_data->sn);
        GET_JSON_VALUE_STRING(root, "dtu_sn", rs_data->dtu_sn);

        GET_JSON_VALUE_INT(root, "communication_timeout", rs_data->communication_timeout);

        GET_JSON_VALUE_STRING(root, "tcp_ip_addr", rs_data->tcp_ip_addr);
        GET_JSON_VALUE_STRING(root, "requester", rs_data->requester);
        GET_JSON_VALUE_INT(root, "tcp_port", rs_data->tcp_port);
        free(buf);
    }
    else
    {
        dy_syslog(LOG_ERR, "json parse error, %s", json_str);
        ret = -1;
    }

out:
    cJSON_Delete(root);

    return ret;
}


int notify_upgrade_run(ipc_session_t *session, node_cfg_t *node)
{
    char topic[TOPIC_MAX_LEN] = {0};
    GET_SN_PROC

    system("/etc/init.d/data_reporter stop");
    //system("/etc/init.d/protocol_parser stop");

    if (node == NULL)
    {
        return -1;
    }
    snprintf(topic, TOPIC_MAX_LEN, "ipc/%s/%s/%s", sn, port_enum2char(node->port), TOPIC_NOTIFY_UPGRADE);
    ipc_session_publish(session, topic, (unsigned char *) START_UPGRADE, (int) strlen(START_UPGRADE));
    return 0;
}

int notify_upgrade_done(ipc_session_t *session, node_cfg_t *node)
{
    char topic[TOPIC_MAX_LEN] = {0};
    GET_SN_PROC

    system("/etc/init.d/data_reporter start");
    //system("/etc/init.d/protocol_parser start");
    if (node == NULL)
    {
        return -1;
    }
    snprintf(topic, TOPIC_MAX_LEN, "ipc/%s/%s/%s", sn, port_enum2char(node->port), TOPIC_NOTIFY_UPGRADE);
    ipc_session_publish(session, topic, (unsigned char *) STOP_UPGRADE, (int) strlen(STOP_UPGRADE));
    return 0;
}


int get_tag_data_from_str(tag_table_t *tag, char *json_str)
{
    int ret = 0;

    cJSON *root = cJSON_Parse(json_str);
    if (root)
    {
        memset(tag, 0, sizeof(*tag));

        GET_JSON_VALUE_STRING(root, "sn", tag->sn);
        GET_JSON_VALUE_STRING(root, "identifier", tag->identifier);
        cJSON *tag_val = cJSON_GetObjectItem(root, "tags");
        if (tag_val)
        {
            tag->tag_node = cJSON_PrintUnformatted(tag_val);
        }

        GET_JSON_VALUE_INT(root, "mi", tag->mi);
        GET_JSON_VALUE_INT(root, "time", tag->time);

        tag->report_period = 0;
        tag->data_type = DATA_TYPE_PROPERTY;
        tag->report = 0;
    }
    else
    {
        dy_syslog(LOG_ERR, "json parse error");
        ret = -1;
    }

    cJSON_Delete(root);
    return ret;
}
