#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 "vdev_common.h"

void virtual_dev_data(vdev_var_t *var, char *sn, unsigned char *data)
{
    pp_real_time_data_t rtd = {0};

    rtd.data = data;
    rtd.len = strlen(rtd.data);
    rtd.port = VIRTUAL_1;
    strcpy(rtd.sn, sn);
    strcpy(rtd.src_identifier, "post");

    dy_syslog(LOG_INFO, "%s %d report data %s len %d src_identifier %s", __FUNCTION__, __LINE__, rtd.data, rtd.len, rtd.src_identifier);

    //协议解析，注意需要根据存储周期存储位号数据
    send_to_proto_parser(var->session, &rtd);
}

static int vdev_query_finish(vdev_var_t *var, int i, struct list_head *query_list)
{
    tag_list_node_t *tmp = NULL;
    tag_list_node_t *tag_list = NULL;
    dy_db_session_t *session = var->db_session;
    char *errorMessage;
    int ret = 0;
    int cnt = 0;
    tag_table_t *p = NULL;
    pp_real_time_data_t rtd = {0};

    if (list_empty(query_list))
    {
        char data[128] = {0};
        snprintf(data, 128, "{\"sn\":\"%s\"}", var->virtual_cfg->node[i].sn);
        virtual_dev_data(var, var->virtual_cfg->node[i].sn, data);

        return 0;
    }

    cJSON *root = cJSON_CreateObject();
    cJSON *nodes = cJSON_CreateArray();
    cJSON_AddItemToObject(root, "datas", nodes);

    list_for_each_entry_safe(tag_list, tmp, query_list, list)
    {
        cJSON *item = cJSON_CreateObject();
        cJSON_AddItemToArray(nodes, item);
        p = &tag_list->tag;

        cJSON_AddStringToObject(item, "sn", p->sn);
        cJSON_AddNumberToObject(item, "time", p->time);
        cJSON_AddNumberToObject(item, "quality", p->res);
        if (strlen(p->identifier))
        {
            cJSON_AddStringToObject(item, "identifier", p->identifier);
        }
        cJSON *jtag = cJSON_Parse(p->tag_node);
        cJSON_AddItemToObject(item, "tags", jtag);

        list_del(&tag_list->list);
        free(tag_list->tag.tag_node);
        free(tag_list);
    }
    rtd.data = cJSON_PrintUnformatted(root);
    rtd.len = strlen(rtd.data);
    rtd.port = VIRTUAL_1;
    strcpy(rtd.sn, var->virtual_cfg->node[i].sn);
    strcpy(rtd.src_identifier, "post");

    //dy_syslog(LOG_INFO, "report data %s len %d src_identifier %s", rtd.data, rtd.len, rtd.src_identifier);

    send_to_proto_parser(var->session, &rtd);
    cJSON_Delete(root);
    free(rtd.data);
}

static int period_timer(vdev_var_t *var)
{
    TIMER_CONFIRM(var->period_timer);
    int i, j;
    static unsigned int last_time[MAX_VDEV_CNT] = {0};
    unsigned int now_time = time(NULL);
    struct list_head query_list;

    //遍历虚拟设备
    for (i = 0; i < var->virtual_cfg->node_cnt; i++)
    {
        if (var->virtual_cfg->node[i].calculate_window != 0 && now_time - last_time[i] >= var->virtual_cfg->node[i].calculate_window)
        {
            int cnt = 0;
            char SNs[1024];
            int pos = 0;

            last_time[i] = now_time;
            cnt = var->virtual_cfg->node[i].referCnt;
            INIT_LIST_HEAD(&query_list);

            if (cnt > 0)
            {
                for (j = 0; j < cnt; j++)
                {
                    if (j == 0)
                    {
                        pos += snprintf(SNs + pos, 1024 - pos, "%s", var->virtual_cfg->node[i].pparam[j].refer_sn);
                    }
                    else
                    {
                        pos += snprintf(SNs + pos, 1024 - pos, ",%s", var->virtual_cfg->node[i].pparam[j].refer_sn);
                    }
                }
                dy_db_select_property_by_sn(var->db_session, SNs, &query_list);
            }
            vdev_query_finish(var, i, &query_list);
        }
    }
    return 0;
}

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

    while (1)
    {
        SELECT_INIT();
        SELECT_ADD_FD(var->period_timer);
        timeout.tv_usec = 0;
        timeout.tv_sec = 2;
        ret = select(maxfd + 1, &rset, 0, 0, &timeout);
        if (ret < 0)
        {
            int error = errno;
            printf("errno %d\n", error);
            if (error == EINTR)
            {
                continue;
            }
            else
            {
                break;
            }
        }
        else if (ret > 0)
        {
            if (var->period_timer > 0 && FD_ISSET(var->period_timer, &rset))
            {
                FD_CLR(var->period_timer, &rset);
                period_timer(var);
            }
        }
    }
}

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

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

    //ipc_session_subscribe(ipc_session, topic);
}

static int vdev_mqtt_handle_recv_msg(void *obj, ipc_msg_t *mqtt_msg)
{
    vdev_var_t *var = (vdev_var_t *)obj;

    dy_syslog(LOG_DEBUG, " MQTT client: received MQTT topic:%s payload length:%d",
              mqtt_msg->topic, mqtt_msg->payloadLen);
}

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

    snprintf(clientId, MAX_CLIENT_ID_LEN, "INT_vdev_%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, vdev_mqtt_handle_recv_msg, NULL);

    ipc_session_start(var->session);
    vdev_subscribe_all(var);
}

int vdev_init_cfg(vdev_var_t *var)
{
    int i;
    int virtual_dev_cnt = 0;
    nodes_cfg_table_t *vdev_cfg_table = NULL;
    int index = 0;

    for (i = 0; i < var->nodes_cfg_table->node_cnt; i++)
    {
        node_cfg_t *node = &var->nodes_cfg_table->node[i];
        if (node->calculate_window == 0) continue;
        if (node->port == VIRTUAL_1 || node->referCnt > 0)
        {
            // 该节点是虚拟设备
            virtual_dev_cnt++;
        }
    }

    dy_syslog(LOG_ERR, "virtual_dev_cnt:%d", virtual_dev_cnt);

    vdev_cfg_table = malloc(sizeof(nodes_cfg_table_t) + sizeof(node_cfg_t) * virtual_dev_cnt);
    if (vdev_cfg_table == NULL)
    {
        dy_syslog(LOG_ERR, "malloc failed, cnt:%d", virtual_dev_cnt);
        return -1;
    }

    for (i = 0; i < var->nodes_cfg_table->node_cnt; i++)
    {
        node_cfg_t *node = &var->nodes_cfg_table->node[i];
        if (node->calculate_window == 0) continue;
        if (node->port == VIRTUAL_1 || node->referCnt > 0)
        {
            node_cfg_t *vdev_node = &vdev_cfg_table->node[index];
            index++;
            memcpy(vdev_node, node, sizeof(node_cfg_t));
            if (node->referCnt > 0)
            {
                vdev_node->pparam = malloc(sizeof(refer_param_t) * vdev_node->referCnt);
                memcpy(vdev_node->pparam, node->pparam, sizeof(refer_param_t) * vdev_node->referCnt);
            }
        }
    }

    vdev_cfg_table->node_cnt = virtual_dev_cnt;
    var->virtual_cfg = vdev_cfg_table;
    return 0;
}
static int vdev_init(vdev_var_t *var)
{
    int ret = 0;
    const char *board_name = NULL;
    board_name = get_board_name();

    get_board_sn(var->sn_str);
    get_process_name(var->proc_name);
    if (load_nodes_cfg(&var->nodes_cfg_table, NODES_CFG_PATH) == -1)
    {
        dy_syslog(LOG_ERR, "load nodes cfg fail");
    }
    vdev_init_cfg(var);

    if (strcmp(board_name, BOARD_WOOLINK_MT7628) == 0)
    {
        dy_db_session_init(&var->db_session, DATA_COLLECT_DB_TMPFS_NAME, (void*)var);
    }
    else
    {
        dy_db_session_init(&var->db_session, DATA_COLLECT_DB_NAME, (void*)var);
    }
    vdev_mqtt_client_init(var);
    dy_syslog(LOG_INFO, "init done, board SN:%s", var->sn_str);

    var->period_timer = my_timer_create();
    if (var->period_timer > 0)
    {
        my_timer_set(var->period_timer, 10, PERIOD_TIMER_INTERVAL);
    }
    else
    {
        dy_syslog(LOG_ERR, "timer create failed");
        ret = -1;
    }

    return ret;
}

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

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

    vdev_init(&var);
    vdev_loop(&var);

    return 0;
}
