#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 <time.h>
#include <netinet/tcp.h>
#include <sys/socket.h>

#include "common.h"

static int __mqtt_handle_recv_msg(void *obj, ipc_msg_t *mqtt_msg)
{
    var_t *var = (var_t *)obj;

    if (strstr(mqtt_msg->topic, "property"))
    {
        dy_syslog(LOG_DEBUG, "rcv topic: %s ", mqtt_msg->topic);
        int ret = 0;

        cJSON *root = cJSON_Parse(mqtt_msg->payload);
        if (root)
        {
            char buf[128];
            char sn[64];
            char *tag_node;
            int i;
            node_data_t *node = NULL;

            GET_JSON_VALUE_STRING(root, "sn", sn);
            GET_JSON_VALUE_DY_STRING(root, "tag_node", tag_node);

            cJSON *jtag = cJSON_Parse(tag_node);
            for (i = 0; i < var->nodes_cfg_table->node_cnt; i++)
            {
                node = &var->node[i];
                if (strcmp(node->node->sn, sn) == 0)
                {
                    cJSON *root_single = jtag;
                    if (root_single && root_single->child)
                    {
                        cJSON *child = root_single->child;

                        while (child)
                        {
                            char *key = child->string;
                            prop_info_t *prop_info = NULL;
                            int j;
                            for (j = 0; j < node->prop_cnt; j++)
                            {
                                prop_info = &node->prop_info[j];
                                if (strcmp(key, prop_info->identifier) == 0)
                                {
                                    if (cJSON_IsNumber(child))
                                    {
                                        unsigned char data[8] = {0};
                                        if (prop_info->format == TAG_TYPE_FLOAT)
                                        {
                                            float *t = (float *)data;
                                            *t = child->valuedouble;
                                        }
                                        else if (prop_info->format == TAG_TYPE_INT16)
                                        {
                                            short *t = (short *)data;
                                            *t = child->valuedouble;
                                        }
                                        memcpy(node->reg_data.data + prop_info->offset, data, get_format_size(prop_info->format));
                                    }
                                    break;
                                }
                            }
                            child = child->next;
                        }
                    }
                }
            }
            cJSON_Delete(jtag);
            free(tag_node);
        }
        else
        {
            dy_syslog(LOG_ERR, "json parse error");
            ret = -1;
        }

    out:
        cJSON_Delete(root);
        return ret;
    }
    return 0;
}

static void __subscribe_all(var_t *var)
{
    ipc_session_t *ipc_session = var->session;
    char topic[TOPIC_MAX_LEN] = {0};

    snprintf(topic, TOPIC_MAX_LEN, "ipc/+/+/device/+/data/property/+");
    ipc_session_subscribe(ipc_session, topic);
}

static int __mqtt_client_init(var_t *var)
{
    char clientId[MAX_CLIENT_ID_LEN] = {0};

    snprintf(clientId, MAX_CLIENT_ID_LEN, "INT_modbus_tcp_slave_%s", var->sn_str);
    var->session = ipc_session_new(clientId, (void *)var, IPC_DEFAULT);
    if (var->session == NULL)
        return -1;

    ipc_session_set_callbacks(var->session, __mqtt_handle_recv_msg, NULL);
    __subscribe_all(var);
    ipc_session_start(var->session);
}

static int __create_node_addr_table(var_t *var, node_data_t *node)
{
    object_cfg_t *obj = NULL;
    int i;
    int prop_cnt;
    object_property_cfg_t *prop = NULL;
    prop_info_t *pinfo = NULL;
    int offset = 0;

    obj = find_object_by_id(var->object_table, node->node->template_id);

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

    prop_cnt = obj->property_tab->propertyCnt;

    node->prop_info = calloc(prop_cnt, sizeof(prop_info_t));

    for (i = 0; i < prop_cnt; i++)
    {
        prop = &obj->property_tab->property[i];
        pinfo = &node->prop_info[i];

        pinfo->offset = offset;
        pinfo->identifier = strdup(prop->identifier);
        pinfo->format = prop->raw_data_type;
        offset += get_format_size(pinfo->format);
    }

    node->prop_cnt = prop_cnt;

    node->reg_data.size = offset;
    node->reg_data.mb_mapping = modbus_mapping_new_start_address(0, 0, 0, 0, 0, offset, 0, 0);
    node->reg_data.data = (unsigned char *)node->reg_data.mb_mapping->tab_registers;
}

static int __init_addr_table(var_t *var)
{
    int i;
    node_data_t *node = NULL;

    int node_cnt = var->nodes_cfg_table->node_cnt;
    var->node = calloc(node_cnt, sizeof(node_data_t));

    for (i = 0; i < node_cnt; i++)
    {
        node = &var->node[i];
        node->node = &var->nodes_cfg_table->node[i];
        node->reg_data.term_addr = i + 1;

        __create_node_addr_table(var, node);
    }
}

int init(var_t *var)
{
    if (load_nodes_cfg(&var->nodes_cfg_table, NODES_CFG_PATH) == -1)
    {
        dy_syslog(LOG_ERR, "load nodes cfg fail");
    }
    if (load_templates_cfg(&var->template_table, TEMPLATES_CFG_PATH) == -1)
    {
        dy_syslog(LOG_ERR, "load template cfg fail");
    }

    if (load_objects_cfg(&var->object_table, OBJECTS_CFG_PATH) == -1)
    {
        dy_syslog(LOG_ERR, "load object cfg fail");
    }

    get_board_sn(var->sn_str);

    __mqtt_client_init(var);

    __init_modbus_tcp_slave(var);

    if (var->nodes_cfg_table)
    {
        __init_addr_table(var);
    }
}

static void __loop(var_t *var)
{
    int ret = -1, maxfd, i;
    fd_set rset;
    struct timeval timeout;

    while (1)
    {
        SELECT_INIT();
        timeout.tv_usec = 0;
        timeout.tv_sec = 5;

        ret = select(maxfd + 1, &rset, 0, 0, &timeout);
        if (ret < 0)
        {
            int error = errno;
            dy_syslog(LOG_INFO, "errno %d\n", error);

            if (error == EINTR)
            {
                continue;
            }
            else
            {
                break;
            }
        }
        else if (ret > 0)
        {
        }
    }
}

int main(int argc, char *argv[])
{
    var_t var = {0};

    memset(&var, 0, sizeof(var));

    init(&var);
    __loop(&var);
}
