#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 "zigbee_common.h"
#include "uart.h"

gw_port_e ports[] = {ZIGBEE};


static int ds_flush_timer(zigbee_var_t *var)
{
    time_t now = time(NULL);

    TIMER_CONFIRM(var->node_status_timer);

    if (now >= var->nodes_status_sync_date + 30)
    {
        static time_t lasttime = 0;
        var->nodes_status_sync_date = now;
        char *node_status = ds_print(var->ds);

        dy_syslog(LOG_INFO, "online_status_changed:%d strlen(node_status):%d, (now-lasttime):%d",
                  var->ds->online_status_changed, strlen(node_status), now - lasttime);
        if (node_status && strlen(node_status) > 20 &&
                (var->ds->online_status_changed || now > lasttime + 3600)) //在线状态变化上报，1小时也会上报
        {
            char topic[TOPIC_MAX_LEN];
            int ret = 0;

            snprintf(topic, TOPIC_MAX_LEN, "ipc/%s/%s/%s", var->sn_str, "zigbee", TOPIC_EVT_NODESSTATUS);

            ret = ipc_session_publish(var->session, topic, node_status, strlen(node_status));
            if (ret == 0)
            {
                lasttime = now;
                var->ds->online_status_changed = false;
            }
            node_sta_backup(var->ds, ZIGBEE_NODE_STATUS_BAK_FILE);
        }
        if (node_status)
        {
            write_file_data(NODES_CACHE "/zigbee_status.json", node_status, strlen(node_status));
            free(node_status);
        }
    }
}

int zigbee_send_rglt(zigbee_var_t *var, pp_regulate_signal_t *regulate_data)
{
    char *data_b64 = NULL;
    char topic[TOPIC_MAX_LEN] = {0};
    cJSON *mqtt_json = NULL;
    int len;
    char *data_tmp = NULL;
    int mi;
    int period;
    int buff_len = regulate_data->len * 2 + 1024;

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

    //store the identifier
    {
        char key_str[64] = {0};
        char *identifier = NULL;
        regulate_cmd_backup_t fields;
        time_t now = time(NULL);

        strcpy(fields.src_identifier, regulate_data->src_identifier);
        strcpy(fields.sn, regulate_data->sn);
        fields.mi = regulate_data->mi;
        fields.ts = now;
        strcpy(fields.requester, regulate_data->requester);
        sprintf(key_str, "%s", regulate_data->sn);
        free(identifier);
        var->identifier_backup.insert(&var->identifier_backup, key_str, &fields, sizeof(fields));
    }

    //将数据加入list,由poll函数去发送
    send_zigbee_data(var, regulate_data->data, regulate_data->len, regulate_data->term_addr , 0, UNICAST_NO);

    flush_device_status_by_sn(var->ds, ACTION_SND, regulate_data->sn);
}

static int zigbee_msg_tag_data_ctrl(zigbee_var_t *var, ipc_msg_t *mqtt_msg)
{
    pp_regulate_signal_t regulate_data;
    int ret = 0;

    ret = get_rglt_data_from_json(&regulate_data, mqtt_msg->payload);
    if (ret < 0)
    {
        dy_syslog(LOG_ERR, "parse real data structure failed");
        return -1;
    }
    if (regulate_data.period > 0)
    {
        period_msg_t once;

        dy_syslog(LOG_INFO, "Recevied interval ctrl cmd periad %d", regulate_data.period);
        snprintf(once.key, 128, "%s_%s", regulate_data.sn, regulate_data.src_identifier);
        once.interval = regulate_data.period;
        once.last_poll = 0;
        once.rs = calloc(1, sizeof(pp_regulate_signal_t));
        memcpy(once.rs, &regulate_data, sizeof(pp_regulate_signal_t));
        once.started = 1;
        once.rs->data = calloc(1, regulate_data.len);
        memcpy(once.rs->data, regulate_data.data, regulate_data.len);

        period_msg_add(var->period, &once);
    }
    else
    {
        ret = zigbee_send_rglt(var, &regulate_data);
        if (ret < 0)
        {
            dy_syslog(LOG_ERR, "parse real data structure failed");
            return -1;
        }
    }

out:
    free(regulate_data.data);

    return 0;
}


static int period_service_poll(zigbee_var_t *var)
{
    TIMER_CONFIRM(var->period_poll_timer);
    int ret = 0;
    static int all_started = 0;
    static time_t last_time = 0;
    time_t now = time(NULL);

    period_msg_t *msg = period_msg_next(var->period);
    if (msg && msg->started)
    {
        ret = zigbee_send_rglt(var, msg->rs);
        if (ret < 0)
        {
            dy_syslog(LOG_ERR, "parse real data structure failed");
        }
    }

    if (now - last_time > 3600)
    {
        period_services_trigger(var->session, var->period, var->nodes_cfg_table, var->template_table, ports, ARRAY_SIZE(ports));
        last_time = now;
        all_started = 0;
    }

    if (all_started == 0)
    {
        all_started = period_services_retrigger(var->session, var->period, var->nodes_cfg_table, var->template_table, ports, ARRAY_SIZE(ports));
    }
    return 0;
}

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

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

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

        ret = select(maxfd + 1, &rset, 0, 0, &timeout);
        if (ret < 0)
        {
            if (errno == EINTR)
            {
                continue;
            }
            else
            {
                break;
            }
        }
        else if (ret > 0)
        {
            if (var->node_status_timer > 0 && FD_ISSET(var->node_status_timer, &rset))
            {
                FD_CLR(var->node_status_timer, &rset);
                ds_flush_timer(var);
            }
        }
    }
}

static void zigbee_subscribe_all(zigbee_var_t *var)
{
    ipc_session_t *ipc_session = var->session;
    char topic[TOPIC_MAX_LEN] = {0};
    int i = 0;

    snprintf(topic, TOPIC_MAX_LEN, "ipc/+/%s/device/+/data/%s", "ZIGBEE", TOPIC_SEND_RGLT_SIGNAL_RAW_DATA);
    ipc_session_subscribe(ipc_session, topic);
    snprintf(topic, TOPIC_MAX_LEN, "ipc/+/%s/%s", "ZIGBEE", TOPIC_NOTIFY_UPGRADE);
    ipc_session_subscribe(ipc_session, topic);
}

static int zigbee_mqtt_handle_recv_msg(void *obj, ipc_msg_t *mqtt_msg)
{
    zigbee_var_t *var = (zigbee_var_t *)obj;
    dy_syslog(LOG_DEBUG, "received MQTT topic:%s payload length:%d", mqtt_msg->topic, mqtt_msg->payloadLen);
    if (strstr(mqtt_msg->topic, TOPIC_SEND_RGLT_SIGNAL_RAW_DATA))
    {
        //发送下行数据
        zigbee_msg_tag_data_ctrl(var, mqtt_msg);
    }
}


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

    snprintf(clientId, MAX_CLIENT_ID_LEN, "INT_zigbee_%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, zigbee_mqtt_handle_recv_msg, NULL);
    zigbee_subscribe_all(var);
    ipc_session_start(var->session);
}

static int zigbee_load_config(zigbee_var_t *var, char *cfg_file)
{

    char *data = NULL;
    cJSON *root = NULL;
    int ret = -1;
    char buf[64] = {0};

    var->zigbee_cfg.type = END_DEVICE;
    var->zigbee_cfg.mode = TRANSPARENT;
    strcpy(var->zigbee_cfg.PAN_ID, "FFFF");
    strcpy(var->zigbee_cfg.key, "11131517191B1D1F10121416181A1C1D");
    var->zigbee_cfg.group_id = 1;
    var->zigbee_cfg.channel = 11;
    var->zigbee_cfg.tx_power = 4;
    var->zigbee_cfg.data_time = 30;

    if (cfg_file == NULL)
    {
        dy_syslog(LOG_ERR, "cfg_file:%s not exist", cfg_file);
        goto out;
    }

    data = read_file_data(cfg_file);
    if (data == NULL)
    {
        dy_syslog(LOG_ERR, "read cfg file %s error", cfg_file);
        goto out;
    }

    root = cJSON_Parse(data);
    if (!root)
    {
        dy_syslog(LOG_ERR, "parse cfg file %s error", cfg_file);
        goto out;
    }

    GET_JSON_VALUE_STRING(root, "dev_type", buf);
    if (strcmp(buf, "Coordinator") == 0)
    {
        var->zigbee_cfg.type = COORDINATOR;
    }
    else if (strcmp(buf, "Router") == 0)
    {
        var->zigbee_cfg.type = ROUTER;
    }
    else if (strcmp(buf, "End-Device") == 0)
    {
        var->zigbee_cfg.type = END_DEVICE;
    }
    GET_JSON_VALUE_STRING(root, "mode", buf);
    if (strcmp(buf, "transparent") == 0)
    {
        var->zigbee_cfg.mode = TRANSPARENT;
    }
    else if (strcmp(buf, "semi-transparent") == 0)
    {
        var->zigbee_cfg.mode = SEMI_TRANSPARENT;
    }
    else if (strcmp(buf, "protocol") == 0)
    {
        var->zigbee_cfg.mode = PROTOCOL;
    }

    GET_JSON_VALUE_STRING(root, "PAN_ID", var->zigbee_cfg.PAN_ID);
    GET_JSON_VALUE_STRING(root, "key", var->zigbee_cfg.key);
    GET_JSON_VALUE_INT(root, "Group_ID", var->zigbee_cfg.group_id);
    GET_JSON_VALUE_INT(root, "channel", var->zigbee_cfg.channel);
    GET_JSON_VALUE_INT(root, "tx_power", var->zigbee_cfg.tx_power);
    GET_JSON_VALUE_INT(root, "data_time", var->zigbee_cfg.data_time);

    cJSON_Delete(root);
    ret = 0;

out:
    if (data)
    {
        free(data);
    }
    set_zigbee_AT_enter(var->zigbee_cfg.tty_fd);
    set_zigbee_dev_type(var->zigbee_cfg.tty_fd, var->zigbee_cfg.type);
    set_zigbee_trans_mode(var->zigbee_cfg.tty_fd, var->zigbee_cfg.mode);
    set_zigbee_PAN_ID(var->zigbee_cfg.tty_fd, var->zigbee_cfg.PAN_ID);
    set_zigbee_key(var->zigbee_cfg.tty_fd, var->zigbee_cfg.key);
    set_zigbee_group_id(var->zigbee_cfg.tty_fd, var->zigbee_cfg.group_id);
    set_zigbee_channel(var->zigbee_cfg.tty_fd, var->zigbee_cfg.channel);
    set_zigbee_tx_power(var->zigbee_cfg.tty_fd, var->zigbee_cfg.tx_power);
    set_zigbee_data_time(var->zigbee_cfg.tty_fd, var->zigbee_cfg.data_time);
    set_zigbee_rmode(var->zigbee_cfg.tty_fd, 7);
    set_zigbee_reset(var->zigbee_cfg.tty_fd);
    set_zigbee_AT_exit(var->zigbee_cfg.tty_fd);
    return ret;
}


int zigbee_init(zigbee_var_t *var)
{
    int i, j;
    pthread_t thread_rcv;

    get_board_sn(var->sn_str);
    var->board_name = get_board_name();

    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");
    }
    dev_status_init(&var->ds, var->nodes_cfg_table, var->template_table, ports, ARRAY_SIZE(ports));
    node_sta_recovery(var->ds, ZIGBEE_NODE_STATUS_BAK_FILE);
    var->zigbee_cfg.tty_fd = uart_init(TTY_DEV, BAUD_RATE, 1, 0, 8, 1);
    if (var->zigbee_cfg.tty_fd > 0)
    {
        sleep(1);
    }
    else
    {
        dy_syslog(LOG_ERR, "dev:%s open failed\n", TTY_DEV);
        return -1;
    }
    period_msg_init(&var->period, 128);
    kv_array_init(&var->identifier_backup, 32);

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

    zigbee_load_config(var, ZIGBEE_CFG);
    zigbee_mqtt_client_init(var);

    var->node_status_timer = my_timer_create();
    if (var->node_status_timer > 0)
    {
        my_timer_set(var->node_status_timer, 1, 5000);
    }

    var->period_poll_timer = my_timer_create();
    if (var->period_poll_timer > 0)
    {
        my_timer_set(var->period_poll_timer, 1, 1000);
    }

    return 0;
}


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

    openlog("ZIGBEE", LOG_PID, LOG_DAEMON);

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

    zigbee_init(&var);
    zigbee_loop(&var);

    return 0;
}
