#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 ra06_load_cfg(ra06_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->lora_cfg.sf = DEFAULT_SF;
    var->lora_cfg.bw = DEFAULT_BW;
    var->lora_cfg.freq = DEFAULT_FREQ;
    var->lora_cfg.power = DEFAULT_POWER;
    var->lora_cfg.addr = DEFAULT_ADDR;
    strcpy(var->lora_cfg.cr, DEFAULT_CR);
    // TODO

    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 == LORA_2)
        {
            cJSON *ext = cJSON_Parse(node_cfg->ext_data);
            int addr;
            int slot;

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

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

            cJSON_Delete(ext);
        }
    }

    dy_syslog(LOG_DEBUG, "===node cnt:%d", node_cnt);

    var->nodes_table.node_cnt = node_cnt;
    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 == LORA_2)
        {
            cJSON *ext = cJSON_Parse(node_cfg->ext_data);
            int addr;
            int slot;

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

            if (slot == var->slot)
            {
                var->nodes_table.nodes[j].addr = addr;
                var->nodes_table.nodes[j].node = node_cfg;
            }

            cJSON_Delete(ext);
        }
    }

    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;
    }

    GET_JSON_VALUE_INT(root, "sf", var->lora_cfg.sf);
    GET_JSON_VALUE_INT(root, "bw", var->lora_cfg.bw);
    GET_JSON_VALUE_INT(root, "freq", var->lora_cfg.freq);
    GET_JSON_VALUE_INT(root, "power", var->lora_cfg.power);
    GET_JSON_VALUE_INT(root, "addr", var->lora_cfg.addr);
    GET_JSON_VALUE_STRING(root, "cr", var->lora_cfg.cr);

    dy_syslog(LOG_DEBUG, "sf:%d freq:%d, cr:%s", var->lora_cfg.sf, var->lora_cfg.freq, var->lora_cfg.cr);
out:
    free(data);
    cJSON_Delete(root);
    return ret;
}

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

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

    snprintf(clientId, MAX_CLIENT_ID_LEN, "RA06_%s_%d", var->sn_str, var->slot);
    var->session = ipc_session_new(clientId, (void*)var, IPC_MQTT);
    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 ra06_init(ra06_var_t *var)
{
    char cfg[32] = {0};
    int fd;
    char dev[16] = {0};
    char buf[128] = {0};
    int i;
    pthread_t thread_rcv;
    int node_cnt = 0;

    get_board_sn(var->sn_str);

    sprintf(cfg, RA06_CFG, var->slot);
    ra06_load_cfg(var, cfg);

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

    sprintf(dev, "/dev/slot%d_Ra06", 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;

    sprintf(buf, "AT+FAC\r\n");
    modem_send_data(var->fd, buf, strlen(buf), "system init");
    sleep(2);
    sprintf(buf, "AT+SBC=%d,%d,%d\r\n", get_sf_value(var->lora_cfg.sf), get_bw_value(var->lora_cfg.bw), get_cr_value(var->lora_cfg.cr));
    modem_send_data(var->fd, buf, strlen(buf), "+OK");
    sleep(1);
    sprintf(buf, "AT+FREQ=%d\r\n", var->lora_cfg.freq);
    modem_send_data(var->fd, buf, strlen(buf), "+Frequency");
    sleep(1);
    sprintf(buf, "AT+POWER=%d\r\n", var->lora_cfg.power);
    modem_send_data(var->fd, buf, strlen(buf), "+OK");
    sleep(1);
    sprintf(buf, "AT+ADDR=%d\r\n", var->lora_cfg.addr);
    modem_send_data(var->fd, buf, strlen(buf), "+OK");
    sleep(1);
    if (var->nodes_table.node_cnt > 0)
    {
        sprintf(buf, "AT+TADDR=%d\r\n", var->nodes_table.nodes[0].addr);
        modem_send_data(var->fd, buf, strlen(buf), "+OK");
        sleep(1);
    }
    sprintf(buf, "AT+RST\r\n");
    modem_send_data(var->fd, buf, strlen(buf), "system init");
    sleep(2);
    sprintf(buf, "AT+RECV=4\r\n");
    modem_send_data(var->fd, buf, strlen(buf), "+OK");
    mqtt_client_init(var);

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

static void ra06_loop(ra06_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[])
{
    ra06_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("ra06", LOG_PID, LOG_DAEMON);

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

    ra06_loop(&var);

    return 0;
}
