#include "mqtt_client.h"
#include "mqtt_session.h"
#include "sub_functions.h"
#include "my_public.h"
#include <unistd.h>


static int tm1640_state_change(void *obj, int state)
{
    switch (state)
    {
    case MQTT_CONNECTED:
        dy_syslog(LOG_INFO, "tm1640 mqtt connected\n");
        break;
    case MQTT_DISCONNECTED:
        dy_syslog(LOG_ERR, "tm1640 mqtt disconnected!\n");
        break;
    default:
        dy_syslog(LOG_ERR, "tm1640 mqtt unknown state\n");
    }

    return 0;
}

static void tm1640_subscribe_all(tm1640_t *tm1640)
{
    ipc_session_t *ipc_session = tm1640->session;
    char topic[TOPIC_MAX_LEN] = {0};
    snprintf(topic, TOPIC_MAX_LEN, "ipc/+/+/+/+/data_filtered/+/gaojingxinxi");
    ipc_session_subscribe(ipc_session, topic);
    snprintf(topic, TOPIC_MAX_LEN, "ipc/+/+/+/+/data_filtered/+/rackxinxi");
    ipc_session_subscribe(ipc_session, topic);
    snprintf(topic, TOPIC_MAX_LEN, "ipc/+/+/+/+/data_filtered/+/GetHeatAndButtonStatus");
    ipc_session_subscribe(ipc_session, topic);
    snprintf(topic, TOPIC_MAX_LEN, "ipc/+/+/+/+/data_filtered/+/hardwarefault");
    ipc_session_subscribe(ipc_session, topic);
    snprintf(topic, TOPIC_MAX_LEN, "ipc/+/+/+/+/data_filtered/+/GetRackVersion");
    ipc_session_subscribe(ipc_session, topic);
    snprintf(topic, TOPIC_MAX_LEN, "M/+/gateway/device/+/service/Set_NodesCfg");
    ipc_session_subscribe(ipc_session, topic);
}

static void get_hw_fault_from_mqtt_msg(tm1640_t *tm1640, cJSON *node)
{
    int TAG_9DF5 = 0;                        // BMU初始化故障 L8
    int TAG_9DF6 = 0;                        // BMU采样线故障 L8
    int TAG_9DF7 = 0;                        // BMU连接线故障 L8
    int TAG_9DF8 = 0;                        // BMU采样芯片初始化失败 L8
    int TAG_9DF9 = 0;                        // BMU电池电压采样故障 L8
    int TAG_9DFA = 0;                        // BMU电池温度采样故障 L8
    int TAG_9DFB = 0;                        // BMU温度传感器故障 L8
    int TAG_9DFE = 0;                        // BMU被动均衡故障 L8
    int TAG_9DFF = 0;                        // BMU被动均衡温度故障 L8

    cJSON *tags = NULL;
    tags = cJSON_GetObjectItemCaseSensitive(node, "tags");
    GET_JSON_VALUE_INT_LOG(tags, "TAG_9DF5", TAG_9DF5);
    GET_JSON_VALUE_INT_LOG(tags, "TAG_9DF6", TAG_9DF6);
    GET_JSON_VALUE_INT_LOG(tags, "TAG_9DF7", TAG_9DF7);
    GET_JSON_VALUE_INT_LOG(tags, "TAG_9DF8", TAG_9DF8);
    GET_JSON_VALUE_INT_LOG(tags, "TAG_9DF9", TAG_9DF9);
    GET_JSON_VALUE_INT_LOG(tags, "TAG_9DFA", TAG_9DFA);
    GET_JSON_VALUE_INT_LOG(tags, "TAG_9DFB", TAG_9DFB);
    GET_JSON_VALUE_INT_LOG(tags, "TAG_9DFE", TAG_9DFE);
    GET_JSON_VALUE_INT_LOG(tags, "TAG_9DFF", TAG_9DFF);


    if (TAG_9DF5 || TAG_9DF6 || TAG_9DF7 || TAG_9DF8 || TAG_9DF9 || TAG_9DFA || TAG_9DFB || TAG_9DFE || TAG_9DFF) {
        tm1640->is_L8_alarm_set = 1;
    } else {
        tm1640->is_L8_alarm_set = 0;
    }
}

static void get_alarms_from_mqtt_msg(tm1640_t *tm1640, cJSON *node)
{
    int rack_vol_high_critical = 0;         // Warning levle 0
    int cell_vol_high_critical = 0;         // Warning levle 0
    int rack_vol_low_critical = 0;          // Warning levle 1
    int cell_vol_low_critical = 0;          // Warning levle 1
    int chg_over_curr_critical = 0;         // Warning levle 2
    int dsg_over_curr_critical = 0;         // Warning levle 3
    int chg_temp_high_critical = 0;         // Warning levle 4
    int dsg_temp_high_critical = 0;         // Warning levle 4
    int chg_temp_low_critical = 0;          // Warning levle 5
    int dsg_temp_low_critical = 0;          // Warning levle 5
    int bmu_comm_fault = 0;                 // Warning levle 6
    int bau_comm_fault = 0;                 // Warning levle 6

    int TAG_9CC8 = 0;                       // 单体压差过高三级报警 L7
    int TAG_9CC9 = 0;                       // 单体温差过高三级报警 L7
    int TAG_9C74 = 0;                       // 绝缘阻值过低三级报警 L7
    int TAG_9C46 = 0;                       // 接触器粘连故障 L7
    int TAG_9C4F = 0;                       // 预充失败 L7

    cJSON *tags = NULL;
    tags = cJSON_GetObjectItemCaseSensitive(node, "tags");
    GET_JSON_VALUE_INT_LOG(tags, "TAG_9C6A", rack_vol_high_critical);           // Warning levle 0
    GET_JSON_VALUE_INT_LOG(tags, "TAG_9C6C", cell_vol_high_critical);           // Warning levle 0
    GET_JSON_VALUE_INT_LOG(tags, "TAG_9C6B", rack_vol_low_critical);            // Warning levle 1
    GET_JSON_VALUE_INT_LOG(tags, "TAG_9C6D", cell_vol_low_critical);            // Warning levle 1
    GET_JSON_VALUE_INT_LOG(tags, "TAG_9C6F", chg_over_curr_critical);           // Warning levle 2
    GET_JSON_VALUE_INT_LOG(tags, "TAG_9C6E", dsg_over_curr_critical);           // Warning levle 3
    GET_JSON_VALUE_INT_LOG(tags, "TAG_9C72", chg_temp_high_critical);           // Warning levle 4
    GET_JSON_VALUE_INT_LOG(tags, "TAG_9C70", dsg_temp_high_critical);           // Warning levle 4
    GET_JSON_VALUE_INT_LOG(tags, "TAG_9C73", chg_temp_low_critical);            // Warning levle 5
    GET_JSON_VALUE_INT_LOG(tags, "TAG_9C71", dsg_temp_low_critical);            // Warning levle 5
    GET_JSON_VALUE_INT_LOG(tags, "TAG_9C47", bmu_comm_fault);                   // Warning levle 6
    GET_JSON_VALUE_INT_LOG(tags, "TAG_9C48", bau_comm_fault);                   // Warning levle 6
    GET_JSON_VALUE_INT_LOG(tags, "TAG_9CC8", TAG_9CC8);                         // Warning levle 7
    GET_JSON_VALUE_INT_LOG(tags, "TAG_9CC9", TAG_9CC9);                         // Warning levle 7
    GET_JSON_VALUE_INT_LOG(tags, "TAG_9C74", TAG_9C74);                         // Warning levle 7
    GET_JSON_VALUE_INT_LOG(tags, "TAG_9C46", TAG_9C46);                         // Warning levle 7
    GET_JSON_VALUE_INT_LOG(tags, "TAG_9C4F", TAG_9C4F);                         // Warning levle 7

    memset(tm1640->buf_warning, 0, sizeof(tm1640->buf_warning));
    tm1640->cnt_warning = 0;
    int i = 0;
    if (rack_vol_high_critical || cell_vol_high_critical) {
        tm1640->buf_warning[i] = WARNING_LEVEL0;
        tm1640->cnt_warning++;
        i++;
    }
    if (rack_vol_low_critical || cell_vol_low_critical) {
        tm1640->buf_warning[i] = WARNING_LEVEL1;
        tm1640->cnt_warning++;
        i++;
    }
    if (chg_over_curr_critical) {
        tm1640->buf_warning[i] = WARNING_LEVEL2;
        tm1640->cnt_warning++;
        i++;
    }
    if (dsg_over_curr_critical) {
        tm1640->buf_warning[i] = WARNING_LEVEL3;
        tm1640->cnt_warning++;
        i++;
    }
    if (chg_temp_high_critical || dsg_temp_high_critical) {
        tm1640->buf_warning[i] = WARNING_LEVEL4;
        tm1640->cnt_warning++;
        i++;
    }
    if (chg_temp_low_critical || dsg_temp_low_critical) {
        tm1640->buf_warning[i] = WARNING_LEVEL5;
        tm1640->cnt_warning++;
        i++;
    }
    if (bmu_comm_fault || bau_comm_fault) {
        tm1640->buf_warning[i] = WARNING_LEVEL6;
        tm1640->cnt_warning++;
        i++;
    }
    if (TAG_9CC8 || TAG_9CC9 || TAG_9C74 || TAG_9C46 || TAG_9C4F) {
        tm1640->buf_warning[i] = WARNING_LEVEL7;
        tm1640->cnt_warning++;
        i++;
    }
    if (tm1640->is_L8_alarm_set) {
        tm1640->buf_warning[i] = WARNING_LEVEL8;
        tm1640->cnt_warning++;
        i++;
    }
}

static int publish_to_north(ipc_session_t *session, const char *identifier, const char *key, const char *value)
{
    char tagNode[256] = {'0'};
    cJSON *tagData = NULL;
    char *payload = NULL;

    if (session == NULL || identifier == NULL || key == NULL || value == NULL) {
        return -1;
    }

    tagData = cJSON_CreateObject();
    if (tagData == NULL) {
        goto cleanup;
    }

    char dev_sn[50] = {0};
    GET_SN_PROC

    snprintf(dev_sn, sizeof(dev_sn), "%s%s", sn, "_Rack_1");
    cJSON_AddStringToObject(tagData, "sn", dev_sn);
    sprintf(tagNode, "{\"%s\":\"%s\"}", key, value);
    cJSON_AddStringToObject(tagData, "tag_node", tagNode);
    cJSON_AddStringToObject(tagData, "identifier", "GetRackVersionAfterMerge");
    cJSON_AddNumberToObject(tagData, "mi", 0);
    cJSON_AddNumberToObject(tagData, "time", time(NULL));
    cJSON_AddNumberToObject(tagData, "report", 0);
    cJSON_AddNumberToObject(tagData, "res", 0);
    cJSON_AddNumberToObject(tagData, "report_period", 10);
    cJSON_AddNumberToObject(tagData, "data_type", 2);
    cJSON_AddStringToObject(tagData, "port", "RS485_1");

    payload = cJSON_Print(tagData);
    if (payload == NULL) {
        goto cleanup;
    }

    char topic[TOPIC_MAX_LEN] = {0};
    snprintf(topic, sizeof(topic), "ipc/%s/%s/device/%s/data/service/%s", sn, "RS485_1", dev_sn, identifier);
    ipc_session_publish(session, topic, payload, strlen(payload));

cleanup:
    if (tagData) {
        cJSON_Delete(tagData);
    }
    if (payload) {
        free(payload);
    }

    return 0;
}

int publish_to_south(ipc_session_t *sesson, char *identifier, char *key, int value)
{
    if (sesson == NULL || identifier == NULL || key == NULL) {
        dy_syslog(LOG_ERR, "Invalid argument!!!");
        return -1;
    }

    char topic[TOPIC_MAX_LEN] = {0};
    char dev_sn[50] = {0};
    GET_SN_PROC
    cJSON *root = cJSON_CreateObject();
    if (root == NULL)
    {
        dy_syslog(LOG_ERR, "cJSON_CreateObject failed!!!");
        return -1;
    }

    snprintf(dev_sn, 50, "%s%s", sn, "_Rack_1");
    cJSON_AddStringToObject(root, "identifier", identifier);
    cJSON_AddNumberToObject(root, key, value);
    cJSON_AddStringToObject(root, "sn", dev_sn);
    cJSON_AddStringToObject(root, "requester", "local");
    cJSON_AddNumberToObject(root, "mi", 1);
    cJSON_AddNumberToObject(root, "timestamp", time(NULL));
    char *str = (cJSON_Print(root));
    cJSON_Delete(root);

    snprintf(topic, TOPIC_MAX_LEN, "ipc/%s/RS485_1/device/%s%s/data/Set_Rglt", sn, sn, "_Rack_1");
    dy_syslog(LOG_DEBUG, "pub:topic  :%s", topic);
    dy_syslog(LOG_DEBUG, "pub:payload:%s", str);
    ipc_session_publish(sesson, topic, (unsigned char *)str, (int)strlen(str));

    if (str) {
        free(str);
    }

    return 0;
}

static void get_rack_info_from_mqtt_msg(tm1640_t *tm1640, cJSON *node)
{
    cJSON *tags = NULL;
    tags = cJSON_GetObjectItemCaseSensitive(node, "tags");
    GET_JSON_VALUE_INT_LOG(tags, "TAG_9C82", tm1640->rackChgState);
    GET_JSON_VALUE_DOUBLE_LOG(tags, "TAG_9C83", tm1640->rackSoc);
}

static void get_and_report_bms_version(tm1640_t *tm1640, cJSON *node)
{
    int project_id = 0;
    int version_major = 0;
    int version_minor = 0;
    int version_revision = 0;
    char rack_version[32] = {'\0'};
    cJSON *tags = NULL;

    tags = cJSON_GetObjectItemCaseSensitive(node, "tags");
    GET_JSON_VALUE_INT_LOG(tags, "ProjectID", project_id);
    GET_JSON_VALUE_INT_LOG(tags, "VERSION_MAJOR", version_major);
    GET_JSON_VALUE_INT_LOG(tags, "VERSION_MINOR", version_minor);
    GET_JSON_VALUE_INT_LOG(tags, "VERSION_REVISION", version_revision);

    snprintf(rack_version, 32, "%d.%d.%d.%d", project_id, version_major, version_minor, version_revision);
    publish_to_north(tm1640->session, "GetRackVersionAfterMerge", "TAG_9E15", rack_version);
}

static void set_node_conf_recv_flag(tm1640_t *tm1640)
{
    tm1640->is_node_conf_recv = 1;
}

void set_pcs_type_and_pack_num(tm1640_t *tm1640)
{
    int i;
    int pcs_type = 0;   // bms侧的pcs类型编号从0开始
    int pack_num = 0;
    nodes_cfg_table_t *nodes_cfg_table = NULL;

    // sleep(30); // wait for download nodes_cfg.json, pp and rs485 restart

    if (load_nodes_cfg(&nodes_cfg_table, NODES_CFG_PATH) == -1)
    {
        return;
    }

    for (i = 0; i < nodes_cfg_table->node_cnt; i++) {
        printf("nodes_cfg_table->node.sn         : %s\n", nodes_cfg_table->node[i].sn);
        printf("nodes_cfg_table->node.template_id: %s\n", nodes_cfg_table->node[i].template_id);
        if (strstr(nodes_cfg_table->node[i].sn, "_PCS_")) {
            pcs_type = atoi(nodes_cfg_table->node[i].template_id);
        }
        else if (strstr(nodes_cfg_table->node[i].sn, "_Pack_")) {
            pack_num++;
        }
    }

    printf("tm1640->pcs_type: %d\n", pcs_type);
    printf("tm1640->pack_num: %d\n", pack_num);

    publish_to_south(tm1640->session, "SetPackNum", "PackNum", pack_num);
    sleep(1);

    publish_to_south(tm1640->session, "SetPcsType", "PcsType", pcs_type);
    sleep(1);

    publish_to_south(tm1640->session, "SetRackReboot", "RebootValue", 1);
}

static void get_heat_and_button_status(tm1640_t *tm1640, cJSON *node)
{
    cJSON *tags = NULL;
    tags = cJSON_GetObjectItemCaseSensitive(node, "tags");
    GET_JSON_VALUE_INT_LOG(tags, "TAG_BUTTON", tm1640->buttonStatus);
    GET_JSON_VALUE_INT_LOG(tags, "TAG_HEAT", tm1640->heatStatus);
}

int tm1640_mqtt_handle_recv_msg(void *obj, ipc_msg_t *mqtt_msg)
{
    if(strstr(mqtt_msg->topic, "gaojingxinxi"))
    {
        dy_syslog(LOG_INFO, "rcv topic: %s ", mqtt_msg->topic);

        cJSON *root = NULL;
        tm1640_t *tm1640 = (tm1640_t *)obj;
    
        root = cJSON_Parse(mqtt_msg->payload);
        if (!root)
        {
            return E_FALSE;
        }
        get_alarms_from_mqtt_msg(tm1640, root);
        cJSON_Delete(root);
    }
    else if(strstr(mqtt_msg->topic, "hardwarefault"))
    {
        dy_syslog(LOG_INFO, "rcv topic: %s ", mqtt_msg->topic);

        cJSON *root = NULL;
        tm1640_t *tm1640 = (tm1640_t *)obj;

        root = cJSON_Parse(mqtt_msg->payload);
        if (!root)
        {
            return E_FALSE;
        }
        get_hw_fault_from_mqtt_msg(tm1640, root);
        cJSON_Delete(root);
    }
    else if(strstr(mqtt_msg->topic, "rackxinxi"))
    {
        dy_syslog(LOG_INFO, "rcv topic: %s ", mqtt_msg->topic);

        cJSON *root = NULL;
        tm1640_t *tm1640 = (tm1640_t *)obj;

        root = cJSON_Parse(mqtt_msg->payload);
        if (!root)
        {
            return E_FALSE;
        }
        get_rack_info_from_mqtt_msg(tm1640, root);
        cJSON_Delete(root);
    }
    else if(strstr(mqtt_msg->topic, "GetHeatAndButtonStatus"))
    {
        dy_syslog(LOG_INFO, "rcv topic: %s ", mqtt_msg->topic);

        cJSON *root = NULL;
        tm1640_t *tm1640 = (tm1640_t *)obj;

        root = cJSON_Parse(mqtt_msg->payload);
        if (!root)
        {
            return E_FALSE;
        }
        get_heat_and_button_status(tm1640, root);
        cJSON_Delete(root);
    }
    else if(strstr(mqtt_msg->topic, "GetRackVersion"))
    {
        dy_syslog(LOG_INFO, "rcv topic: %s ", mqtt_msg->topic);

        cJSON *root = NULL;
        tm1640_t *tm1640 = (tm1640_t *)obj;

        root = cJSON_Parse(mqtt_msg->payload);
        if (!root)
        {
            return E_FALSE;
        }
        get_and_report_bms_version(tm1640, root);
        cJSON_Delete(root);
    }
    else if(strstr(mqtt_msg->topic, "Set_NodesCfg"))
    {
        tm1640_t *tm1640 = (tm1640_t *)obj;
        set_node_conf_recv_flag(tm1640);
    }

    return 0;
}

int tm1640_mqtt_client_init(tm1640_t *tm1640)
{
    char clientId[MAX_CLIENT_ID_LEN] = {0};     // MAX_CLIENT_ID_LEN is defined in ./libipc_session/src/include/mqtt_session.h

    tm1640->session = ipc_session_new(clientId, (void *)tm1640, IPC_DEFAULT);
    if (tm1640->session == NULL)
        return -1;

    ipc_session_set_callbacks(tm1640->session, tm1640_mqtt_handle_recv_msg, tm1640_state_change);
    tm1640_subscribe_all(tm1640);
    ipc_session_start(tm1640->session);
}