#include "dy_ds.h"

/**
*******************************************************************************
*
* @brief 打印设备状态信息，json格式
* @param ns_table 设备状态表
* @return success: json字符串; fail:NULL
*
*******************************************************************************
*/
char *ds_print(dev_status_table_t *ns_table)
{
    cJSON *root = cJSON_CreateObject();
    if (root == NULL || ns_table == NULL)
    {
        return NULL;
    }

    cJSON *nodes = cJSON_CreateArray();
    cJSON_AddItemToObject(root, "status", nodes);
    int i;
    bool online;
    time_t now = time(NULL);

    for (i = 0; i < ns_table->node_cnt; i++)
    {
        cJSON *item = cJSON_CreateObject();
        cJSON_AddItemToArray(nodes, item);
        char buff[100];
        if (item)
        {
            uint8_t *term = 0;
            cJSON_AddStringToObject(item, "sn", ns_table->status[i].sn);
            term = ns_table->status[i].addr;
            if (term)
            {
                snprintf(buff, sizeof(buff), "%02X %02X %02X %02X %02X %02X ", term[0], term[1], term[2], term[3], term[4], term[5]);
            }
            cJSON_AddStringToObject(item, "addr", buff);
            cJSON_AddNumberToObject(item, "pak_rssi", ns_table->status[i].pak_rssi);
            cJSON_AddNumberToObject(item, "last_rcv", ns_table->status[i].last_rcv_time);
            cJSON_AddNumberToObject(item, "login_time", ns_table->status[i].login_time);
            cJSON_AddNumberToObject(item, "tx_cnt", ns_table->status[i].tx);
            cJSON_AddNumberToObject(item, "rx_cnt", ns_table->status[i].rx);
            if ((ns_table->status[i].offline_times != 0) && ((now >= ns_table->status[i].last_rcv_time + ns_table->status[i].offline_times) || (ns_table->status[i].last_rcv_time == 0)))
            {
                online = false;
            }
            else
            {
                online = true;
            }
            if (ns_table->status[i].online != online) //通知上报状态，因为状态被改变
            {
                ns_table->online_status_changed = true;
            }
            ns_table->status[i].online = online;
            cJSON_AddBoolToObject(item, "online", ns_table->status[i].online);
            cJSON_AddNumberToObject(item, "hard_ver", ns_table->status[i].hard_ver);
            cJSON_AddNumberToObject(item, "soft_ver", ns_table->status[i].soft_ver);
            cJSON_AddNumberToObject(item, "proto_ver", ns_table->status[i].proto_ver);
            cJSON_AddNumberToObject(item, "retry_ratio", 100 * (ns_table->status[i].retry) / (ns_table->status[i].tx + 1));
            if (ns_table->status[i].rx > ns_table->status[i].tx)
            {
                cJSON_AddNumberToObject(item, "loss_ratio", 0);
            }
            else if (ns_table->status[i].tx)
            {
                cJSON_AddNumberToObject(item, "loss_ratio", 100 - 100 * (ns_table->status[i].rx) / (ns_table->status[i].tx));
            }
            else
            {
                cJSON_AddNumberToObject(item, "loss_ratio", 100 - 100 * (ns_table->status[i].rx) / (ns_table->status[i].tx + 1));
            }
        }
    }
    char *str = (cJSON_Print(root));
    cJSON_Delete(root);
    return str;
}

int ds_flush_status_action(dev_status_table_t *ns_table, node_message_action_t *nm_action)
{
    int i;
    time_t now = time(NULL);
    int status_changed = 0;

    if (strlen(nm_action->sn) == 0)
    {
        dy_syslog(LOG_WARNING, "empty SN not support");
        return -1;
    }

    for (i = 0; i < ns_table->node_cnt; i++)
    {
        if (strcmp(nm_action->sn, ns_table->status[i].sn) == 0)
        {
            //dy_syslog(LOG_DEBUG, "flush sn:%s action;%d", nm_action->sn, nm_action->action);
            if (nm_action->action == ACTION_LOGIN)
            {
                if (ns_table->status[i].online != true)
                {
                    ns_table->status[i].online = true;
                    status_changed = 1;
                }
                ns_table->status[i].login_time = now;
                ns_table->status[i].last_rcv_time = now;
                ns_table->status[i].rx++;
            }
            else if (nm_action->action == ACTION_RCV)
            {
                if (ns_table->status[i].online != true)
                {
                    ns_table->status[i].online = true;
                    status_changed = 1;
                }
                ns_table->status[i].online = true;
                ns_table->status[i].last_rcv_time = now;
                ns_table->status[i].rx++;
            }
            else if (nm_action->action == ACTION_SND)
            {
                ns_table->status[i].tx++;
            }
            else if (nm_action->action == ACTION_RETRY)
            {
                ns_table->status[i].retry++;
            }
            else if (nm_action->action == ACTION_SOFT_INFO)
            {
                register_info_t *psoft = (register_info_t *)nm_action->res;

                ns_table->status[i].proto_ver = psoft->protocol_ver;
                ns_table->status[i].soft_ver = psoft->soft_ver;
                ns_table->status[i].hard_ver = psoft->hardware_ver;
                ns_table->status[i].class = psoft->class;
            }

            break;
        }
    }

    if (status_changed)
    {
        ns_table->online_status_changed = status_changed;
    }

    return status_changed;
}

/**
*******************************************************************************
*
* @brief 根据SN刷新设备状态 softinfo
* @param ns_table 设备状态表
* @param action 设备动作，send/recv/login
* @param sn SN
* @return 0 for success, -1 for error
*
*******************************************************************************
*/
int flush_device_status_soft_info_by_sn(dev_status_table_t *ns_table, void *info, int len, char *sn)
{
    node_message_action_t nm_action = {0};
    nm_action.action = ACTION_SOFT_INFO;
    nm_action.port = 0;
    if (!info && len > sizeof(nm_action.res))
    {
        dy_syslog(LOG_ERR, "action info too long");
        return -1;
    }
    memcpy(nm_action.res, info, len);
    strcpy(nm_action.sn, sn);
    return ds_flush_status_action(ns_table, &nm_action);
}

/**
*******************************************************************************
*
* @brief 根据SN刷新设备状态
* @param ns_table 设备状态表
* @param action 设备动作，send/recv/login
* @param sn SN
* @return 0 for success, -1 for error
*
*******************************************************************************
*/
int flush_device_status_by_sn(dev_status_table_t *ns_table, node_action_e action, char *sn)
{
    node_message_action_t nm_action = {0};
    if (sn == NULL || strlen(sn) == 0)
    {
        return -1;
    }
    nm_action.action = action;
    nm_action.port = 0;
    strcpy(nm_action.sn, sn);
    return ds_flush_status_action(ns_table, &nm_action);
}

/**
*******************************************************************************
*
* @brief 从文件恢复设备状态信息
* @param tab 设备状态表
* @param bak_file 备份文件
* @return 0 for success, -1 for error
*
*******************************************************************************
*/
int node_sta_recovery(dev_status_table_t *tab, char *bak_file)
{
    char *data = NULL;
    node_status_t tmp = {0};

    data = read_file_data(bak_file);
    if (data == NULL)
    {
        dy_syslog(LOG_INFO, "No backup node status");
        return 0;
    }
    char *ptr = data;
    ptr = strtok(data, "\n");

    while (ptr != NULL)
    {
        int online, class;
        int count = sscanf(ptr, "%[^,],%d,%d,%d,%d,%d,%ld,%ld\n", tmp.sn, (int *)&online, (int *)&class, &tmp.proto_ver, &tmp.hard_ver, &tmp.soft_ver, &tmp.login_time, &tmp.last_rcv_time);
        if (count < 8)
        {
            dy_syslog(LOG_WARNING, "sscanf error, ptr:%s", ptr);
            break;
        }
        tmp.online = online;
        tmp.class = class;
        ptr = strtok(NULL, "\n");
        int i;
        for (i = 0; i < tab->node_cnt; i++)
        {
            node_status_t *node = &tab->status[i];
            if (strcmp(tmp.sn, node->sn) == 0)
            {
                node->online = tmp.online;
                node->class = tmp.class;
                node->proto_ver = tmp.proto_ver;
                node->hard_ver = tmp.hard_ver;
                node->soft_ver = tmp.soft_ver;
                node->login_time = tmp.login_time;
                node->last_rcv_time = tmp.last_rcv_time;
            }
        }
    }

    free(data);
    return 0;
}

/**
*******************************************************************************
*
* @brief 将设备状态信息保存到文件
* @param tab 设备状态表
* @param bak_file 备份文件
* @return 0 for success, -1 for error
*
*******************************************************************************
*/
int node_sta_backup(dev_status_table_t *tab, char *bak_file)
{
    int i;

    if (tab->node_cnt == 0)
    {
        dy_syslog(LOG_INFO, "Node status back up, no node");
        return 0;
    }

    char *data = NULL;
    int pos = 0;
    int size = tab->node_cnt * 128 + 1;

    data = malloc(size);
    if (data == NULL)
    {
        dy_syslog(LOG_ERR, "malloc failed, size:%d", size);
        return -1;
    }

    for (i = 0; i < tab->node_cnt; i++)
    {
        node_status_t *tmp = &tab->status[i];

        pos += snprintf(data + pos, size - pos, "%s,%d,%d,%d,%d,%d,%ld,%ld\n", tmp->sn, tmp->online, tmp->class, tmp->proto_ver, tmp->hard_ver, tmp->soft_ver, tmp->login_time, tmp->last_rcv_time);
    }
    write_file_data(bak_file, data, strlen(data));
    free(data);

    return 0;
}

/**
*******************************************************************************
*
* @brief 设备状态结构初始化, 从所有的设备中，过滤属于自己的设备，并为其初始化设备状态信息
* @param p_ns_table 用于存储设备状态的结构
* @param nodes 设备节点数据
* @param emplate_table 魔板配置
* @param ports 所关心的端口数组
* @param cnt 所关心的端口数组的元素个数
* @return 0 for success, -1 for error
*
*******************************************************************************
*/
int dev_status_init(dev_status_table_t **p_ns_table, nodes_cfg_table_t *nodes, template_table_t *template_table, gw_port_e *ports, int cnt)
{
    node_cfg_t *node = NULL;
    int i, j, k;
    int node_cnt = 0;
    dev_status_table_t *ds = NULL;

    if (nodes != NULL)
    {
        for (i = 0; i < nodes->node_cnt; i++)
        {
            node = &nodes->node[i];
            for (k = 0; k < cnt; k++)
            {
                if (node->port == ports[k])
                {
                    node_cnt++;
                    break;
                }
            }
        }
    }

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

    ds = calloc(1, sizeof(dev_status_table_t) + sizeof(node_status_t) * node_cnt);
    if (ds == NULL)
    {
        dy_syslog(LOG_ERR, "malloc failed, node_cnt:%d", node_cnt);
        return -1;
    }
    memset(ds, 0, sizeof(dev_status_table_t) + sizeof(node_status_t) * node_cnt);
    if (nodes && node_cnt > 0)
    {
        int index = 0;

        for (i = 0; i < nodes->node_cnt; i++)
        {
            node = &nodes->node[i];
            if (strlen(node->app_key) > 0)
            {
                continue;
            }

            for (k = 0; k < cnt; k++)
            {
                if (node->port == ports[k])
                {
                    node_status_t *ns = &ds->status[index];
                    index++;

                    memcpy(ns->addr, node->term_addr, sizeof(node->term_addr));
                    strcpy(ns->sn, node->sn);
                    ns->port = node->port;
                    ns->depth = node->depth;
                    for (j = 0; j < template_table->template_cnt; j++)
                    {
                        if (strcmp(node->template_id, template_table->template[j].template_id) == 0)
                        {
                            ns->offline_times = template_table->template[j].offline_times;
                            ns->protocol = template_table->template[j].protocol;
                            break;
                        }
                    }

                    break;
                }
            }
        }
    }
    ds->node_cnt = node_cnt;

    *p_ns_table = ds;

    return 0;
}

/**
*******************************************************************************
*
* @brief 设备状态结构初始化, 从所有的设备中，过滤属于自己的设备，并为其初始化设备状态信息
* @param p_ns_table 用于存储设备状态的结构
* @param nodes 设备节点数据
* @param emplate_table 魔板配置
* @param ports 所关心的端口数组
* @param cnt 所关心的端口数组的元素个数
* @return 0 for success, -1 for error
*
*******************************************************************************
*/
int dev_status_init_by_app_key(dev_status_table_t **p_ns_table, nodes_cfg_table_t *nodes, template_table_t *template_table, char *app_key)
{
    node_cfg_t *node = NULL;
    int i, j;
    int node_cnt = 0;
    dev_status_table_t *ds = NULL;

    if (nodes != NULL)
    {
        for (i = 0; i < nodes->node_cnt; i++)
        {
            node = &nodes->node[i];
            if (strcmp(node->app_key, app_key) == 0)
            {
                node_cnt++;
            }
        }
    }

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

    ds = calloc(1, sizeof(dev_status_table_t) + sizeof(node_status_t) * node_cnt);
    if (ds == NULL)
    {
        dy_syslog(LOG_ERR, "malloc failed, node_cnt:%d", node_cnt);
        return -1;
    }
    memset(ds, 0, sizeof(dev_status_table_t) + sizeof(node_status_t) * node_cnt);
    if (nodes && node_cnt > 0)
    {
        int index = 0;
        for (i = 0; i < nodes->node_cnt; i++)
        {
            node = &nodes->node[i];
            if (strcmp(node->app_key, app_key) == 0)
            {
                node_status_t *ns = &ds->status[index];
                index++;

                memcpy(ns->addr, node->term_addr, sizeof(node->term_addr));
                strcpy(ns->sn, node->sn);
                ns->port = node->port;
                ns->depth = node->depth;
                for (j = 0; j < template_table->template_cnt; j++)
                {
                    if (strcmp(node->template_id, template_table->template[j].template_id) == 0)
                    {
                        ns->offline_times = template_table->template[j].offline_times;
                        ns->protocol = template_table->template[j].protocol;
                        break;
                    }
                }
            }
        }
    }
    ds->node_cnt = node_cnt;

    *p_ns_table = ds;

    return 0;
}
