#include <sys/select.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/syscall.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <errno.h>
#include <limits.h>

#include "common.h"

static int fastzigbee_load_cfg(fastzigbee_var_t *var, char *cfg)
{
    int node_cnt = 0;
    int i;
    int j;
    char *data = NULL;
    cJSON *root = NULL;
    char *endptr;
    int ret = 0;

    var->dev_info->PanID = ntohs(DEFAULT_PANID);
    var->dev_info->MyAddr = ntohs(DEFAULT_ADDR);
    var->dev_info->Chan = DEFAULT_CH;

    if (load_nodes_cfg(&var->nodes_cfg_table, NODES_CFG_PATH) == -1)
    {
        dy_syslog(LOG_ERR, "load nodes cfg fail");
        return -1;
    }

    for (i = 0; i < var->nodes_cfg_table->node_cnt; i++)
    {
        node_cfg_t *node_cfg = &var->nodes_cfg_table->node[i];
        if (node_cfg->port == ZIGBEE)
        {
            cJSON *ext = cJSON_Parse(node_cfg->ext_data);
            int slot;

            GET_JSON_VALUE_INT(ext, "slot", slot);

            if (slot == var->slot)
            {
                node_cnt++;
            }

            cJSON_Delete(ext);
        }
    }

    var->nodes_table.node_cnt = node_cnt;
    if (node_cnt <= 0)
    {
        dy_syslog(LOG_ERR, "No node");
        return -1;
    }
    var->nodes_table.nodes = calloc(node_cnt, sizeof(node_t));
    if (var->nodes_table.nodes == NULL)
    {
        dy_syslog(LOG_ERR, "calloc failed, cnt:%d", node_cnt);
        return -1;
    }
    j = 0;
    for (i = 0; i < var->nodes_cfg_table->node_cnt; i++)
    {
        node_cfg_t *node_cfg = &var->nodes_cfg_table->node[i];
        if (node_cfg->port == ZIGBEE)
        {
            cJSON *ext = cJSON_Parse(node_cfg->ext_data);
            int addr;
            char addr_str[8];
            int slot;

            GET_JSON_VALUE_STRING(ext, "addr", addr_str);
            GET_JSON_VALUE_INT(ext, "slot", slot);

            addr = strtol(addr_str, &endptr, 16);
            if (slot == var->slot)
            {
                var->nodes_table.nodes[j].addr = addr;
                var->nodes_table.nodes[j].node = node_cfg;
            }

            cJSON_Delete(ext);
        }
    }

    var->dev_info->DstAddr = ntohs(var->nodes_table.nodes[0].addr);

    data = read_file_data(cfg);
    if (data == NULL)
    {
        dy_syslog(LOG_ERR, "read cfg file %s error", cfg);
        ret = -1;
        goto out;
    }
    root = cJSON_Parse(data);
    if (root == NULL)
    {
        dy_syslog(LOG_ERR, "json format invalid");
        ret = -1;
        goto out;
    }
    char tmp[12] = {0};

    GET_JSON_VALUE_STRING(root, "PanID", tmp);
    var->dev_info->PanID = ntohs(strtol(tmp, &endptr, 16));
    GET_JSON_VALUE_STRING(root, "MyAddr", tmp);
    var->dev_info->MyAddr = ntohs(strtol(tmp, &endptr, 16));
    GET_JSON_VALUE_INT(root, "Chan", var->dev_info->Chan);

    dy_syslog(LOG_DEBUG, "PanID:%04X MyAddr:%04X, Chan:%d", ntohs(var->dev_info->PanID), ntohs(var->dev_info->MyAddr), var->dev_info->Chan);
out:
    free(data);
    cJSON_Delete(root);
    return ret;
}

static void mqtt_subscribe_all(fastzigbee_var_t *var)
{
    ipc_session_t *ipc_session = var->session;
    ipc_session_subscribe(ipc_session, SUB_TOPIC);
}

// 建立与内部broker之间的MQTT连接
static int mqtt_client_init(fastzigbee_var_t *var)
{
    char clientId[MAX_CLIENT_ID_LEN] = {0};

    snprintf(clientId, MAX_CLIENT_ID_LEN, "fastzigbee_%s_%d", var->sn_str, var->slot);
    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);
    mqtt_subscribe_all(var);
    ipc_session_start(var->session);
}

static int fastzigbee_init(fastzigbee_var_t *var)
{
    char cfg[32] = {0};
    int fd;
    char dev[16] = {0};
    unsigned char buf[128] = {0};
    unsigned char rcv_buf[512] = {0};
    int i;
    pthread_t thread_rcv;
    int node_cnt = 0;
    int len;
    unsigned short tmp_addr = 0;
    unsigned short tmp_panid = 0;
    unsigned char tmp_chan = 0;
    unsigned short tmp_dstaddr = 0;

    get_board_sn(var->sn_str);

    var->poll_timer = my_timer_create();
    if (var->poll_timer > 0)
    {
        my_timer_set(var->poll_timer, 1, 10000);
    }

    var->dev_info = calloc(1, sizeof(dev_info_t));
    dy_syslog(LOG_DEBUG, "====sizeof(dev_info_t):%d", sizeof(dev_info_t));
    printf("====sizeof(dev_info_t):%d", sizeof(dev_info_t));

    sprintf(dev, "/dev/slot%d_ZLGzigbee", var->slot);
    fd = uart_init(dev, SPEED, STOP, PARITY, BITS, 1);
    if (fd > 0)
    {
        sleep(1);
    }
    else
    {
        fprintf(stderr, "dev:%s open failed\n", dev);
        return -1;
    }

    var->fd = fd;

    buf[0] = 0xAB;
    buf[1] = 0xBC;
    buf[2] = 0xCD;

    buf[3] = 0xD1;
    buf[4] = 0xAA;
    len = modem_send_data(var->fd, buf, 5, rcv_buf, sizeof(rcv_buf));
    if (len > 0)
    {
        if (rcv_buf[0] == 0xAB && rcv_buf[1] == 0xBC && rcv_buf[2] == 0xCD)
        {
            if (rcv_buf[3] == 0xD1)
            {
                memcpy(var->dev_info, rcv_buf + 4, sizeof(dev_info_t));
                dy_syslog(LOG_DEBUG, "ch:%d myaddr:%04X PANID:%04X\n", var->dev_info->Chan, ntohs(var->dev_info->MyAddr), ntohs(var->dev_info->PanID));
                tmp_addr = var->dev_info->MyAddr;
                tmp_panid = var->dev_info->PanID;
                tmp_chan = var->dev_info->Chan;
                tmp_dstaddr = var->dev_info->DstAddr;
            }
            else
            {
                dy_syslog(LOG_ERR, "rcv not right");
                return -1;
            }
        }
        else
        {
            dy_syslog(LOG_ERR, "rcv not right");
            return -1;
        }
    }
    else
    {
        dy_syslog(LOG_ERR, "send read dev info failed");
        return -1;
    }

    buf[3] = 0xE5;
    buf[4] = DEFAULT_AUTO;
    buf[5] = DEFAULT_TYPE;
    buf[6] = 0xAA;
    len = modem_send_data(var->fd, buf, 6, rcv_buf, sizeof(rcv_buf));
    if (len > 0)
    {
        if (rcv_buf[0] == 0xAB && rcv_buf[1] == 0xBC && rcv_buf[2] == 0xCD)
        {
            if (rcv_buf[3] == 0xE5)
            {
                dy_syslog(LOG_DEBUG, "return:%d\n", rcv_buf[6]);
            }
        }
    }

    sprintf(cfg, FASTZIGBEE_CFG, var->slot);
    fastzigbee_load_cfg(var, cfg);

    if (tmp_addr != var->dev_info->MyAddr || tmp_chan != var->dev_info->Chan || tmp_panid != var->dev_info->PanID || tmp_dstaddr != var->dev_info->DstAddr)
    {
        buf[3] = 0xD6;
        buf[4] = tmp_addr & 0xFF;
        buf[5] = (tmp_addr >> 8) & 0xFF;
        memcpy(buf + 6, var->dev_info, sizeof(dev_info_t));
        buf[6 + sizeof(dev_info_t)] = 0xAA;
        len = modem_send_data(var->fd, buf, 7 + sizeof(dev_info_t), rcv_buf, sizeof(rcv_buf));
        if (len > 0)
        {
            if (rcv_buf[0] == 0xAB && rcv_buf[1] == 0xBC && rcv_buf[2] == 0xCD)
            {
                if (rcv_buf[3] == 0xD6)
                {
                    if (rcv_buf[6] == 0)
                    {
                        dy_syslog(LOG_DEBUG, "change addr right return:%d\n", rcv_buf[6]);
                    }
                    else
                    {
                        dy_syslog(LOG_ERR, "change addr wrong return:%d\n", rcv_buf[6]);
                        return -1;
                    }
                }
            }
        }

        buf[3] = 0xD9;
        buf[4] = tmp_addr & 0xFF;
        buf[5] = (tmp_addr >> 8) & 0xFF;
        buf[6] = 00;
        buf[7] = 0x03;
        buf[8] = 0xAA;
        len = modem_send_data(var->fd, buf, 9, rcv_buf, sizeof(rcv_buf));
        sleep(5);
    }

    buf[3] = 0xF0;
    buf[4] = var->dev_info->MyAddr & 0xFF;
    buf[5] = (var->dev_info->MyAddr >> 8) & 0xFF;
    buf[6] = 1;
    buf[7] = 1;
    buf[8] = 0xAA;
    len = modem_send_data(var->fd, buf, 9, rcv_buf, sizeof(rcv_buf));
    if (len > 0)
    {
        if (rcv_buf[0] == 0xAB && rcv_buf[1] == 0xBC && rcv_buf[2] == 0xCD)
        {
            if (rcv_buf[3] == 0xF0)
            {
                dy_syslog(LOG_DEBUG, "return:%d\n", rcv_buf[7]);
            }
        }
    }

    mqtt_client_init(var);

    pthread_create(&thread_rcv, NULL, recv_loop, (void *)var);
}

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

    while (1)
    {
        SELECT_INIT();
        SELECT_ADD_FD(var->poll_timer);

        timeout.tv_usec = 0;
        timeout.tv_sec = 5;

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

            if (errno == EINTR)
            {
                continue;
            }
            else
            {
                break;
            }
        }
        else if (ret > 0)
        {
            if (var->poll_timer > 0 && FD_ISSET(var->poll_timer, &rset))
            {
                FD_CLR(var->poll_timer, &rset);
            }
        }
    }
}

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

    if (argc != 2)
    {
        fprintf(stderr, "Usage: %s <slot>", argv[0]);
        return -1;
    }

    errno = 0; /* To distinguish success/failure after call */
    slot = strtol(argv[1], &endptr, 10);
    if ((errno == ERANGE && (slot == LONG_MAX || slot == LONG_MIN)) || (errno != 0 && slot == 0))
    {
        perror("strtol");
        exit(EXIT_FAILURE);
    }

    if (endptr == optarg)
    {
        fprintf(stderr, "No digits were found\n");
        exit(EXIT_FAILURE);
    }

    openlog("fastzigbee", LOG_PID, LOG_DAEMON);

    memset(&var, 0, sizeof(var));
    var.slot = slot;
    fastzigbee_init(&var);

    fastzigbee_loop(&var);

    return 0;
}
