#include "ws_method.h"
#include "myLog.h"
#include "cJSON.h"
#include "mySystem.h"

typedef struct
{
    std::string saddr;
    std::string desc;
    uint8_t data_type;
    uint8_t ctrl_type;
    void *p_data;
    stru_signal_param *p_param;
}stru_ws_signal;

LOCAL std::vector<stru_ws_signal> g_ws_out_signals;
LOCAL std::vector<stru_ws_signal> g_ws_in_signals;
LOCAL std::vector<stru_ws_signal> g_ws_yk_signals;
LOCAL std::vector<stru_ws_signal> g_ws_param_signals;



int signal_is_exist(const std::string& saddr, const std::vector<stru_ws_signal>& signals)
{
    for(uint32_t i = 0; i < signals.size(); i++)
    {
        if(signals[i].saddr == saddr)
        {
            return 0;
        }
    }
    return -1;
}

void add_signal(const std::string& saddr, const std::string& signal_type)
{
    std::vector<stru_ws_signal> *p_signals = NULL;
    stru_ws_signal *p = NULL;

    if(signal_type == "out")
    {
        p_signals = &g_ws_out_signals;

        if(0 == signal_is_exist(saddr, g_ws_out_signals))
        {
            LOG_E("add_signals: signal is already exist, saddr = %s", saddr.c_str());
            return;
        }

        p_signals->push_back({saddr, "", 0, 0, NULL, NULL});
        p = &p_signals->back();

        if(0 != dc_get_out_signal_info(saddr, p->desc, p->data_type, &p->p_data))
        {
            LOG_E("add_signals: dc_get_out_signal_info failed, saddr = %s", saddr.c_str());
            return;
        }
    }
    else if(signal_type == "in")
    {
        p_signals = &g_ws_in_signals;

        if(0 == signal_is_exist(saddr, g_ws_in_signals))
        {
            LOG_E("add_signals: signal is already exist, saddr = %s", saddr.c_str());
            return;
        }

        p_signals->push_back({saddr, "", 0, 0, NULL, NULL});
        p = &p_signals->back();

        if(0 != dc_get_in_signal_info(saddr, p->desc, p->data_type, &p->p_data))
        {
            LOG_E("add_signals: dc_get_in_signal_info failed, saddr = %s", saddr.c_str());
            return;
        }
        
    }
    else if(signal_type == "yk")
    {
        p_signals = &g_ws_yk_signals;

        if(0 == signal_is_exist(saddr, g_ws_yk_signals))
        {
            LOG_E("add_signals: signal is already exist, saddr = %s", saddr.c_str());
            return;
        }

        p_signals->push_back({saddr, "", 0, 0, NULL, NULL});
        p = &p_signals->back();

        if(0 != dc_get_yk_signal_info(saddr, p->desc, p->data_type, p->ctrl_type, &p->p_data))
        {
            LOG_E("add_signals: dc_get_yk_signal_info failed, saddr = %s", saddr.c_str());
            return;
        }
    }
    else if(signal_type == "param")
    {
        p_signals = &g_ws_param_signals;

        if(0 == signal_is_exist(saddr, g_ws_param_signals))
        {
            LOG_E("add_signals: signal is already exist, saddr = %s", saddr.c_str());
            return;
        }
        p_signals->push_back({saddr, "", 0, 0, NULL, NULL});
        p_signals->back().p_param = new stru_signal_param;
        p = &p_signals->back();
        
        if(0 != dc_get_param_signal_info(saddr, p->desc, p->data_type, *p->p_param, p->ctrl_type, &p->p_data))
        {
            LOG_E("add_signals: dc_get_param_signal_info failed, saddr = %s", saddr.c_str());
            return;
        }
    }
    else
    {
        LOG_E("add_signals: invalid signal_type = %s", signal_type.c_str());
        return;
    }
}

void del_signal(const std::string& saddr, const std::string& signal_type)
{
    std::vector<stru_ws_signal> *p_signals = NULL;

    if(signal_type == "out")
    {
        p_signals = &g_ws_out_signals;
    }
    else if(signal_type == "in")
    {
        p_signals = &g_ws_in_signals;
    }
    else if(signal_type == "yk")
    {
        p_signals = &g_ws_yk_signals;
    }
    else if(signal_type == "param")
    {
        p_signals = &g_ws_param_signals;
    }
    else
    {
        LOG_E("del_signal: invalid signal_type = %s", signal_type.c_str());
        return;
    }

    for(uint32_t i = 0; i < p_signals->size(); i++)
    {
        stru_ws_signal *p_signal = &p_signals->at(i);
        if(p_signal->saddr == saddr)
        {
            if(signal_type == "param")
            {
                delete p_signal->p_param;
            }

            p_signals->erase(p_signals->begin() + i);
            return;
        }
    }

    LOG_E("del_signal: not found saddr = %s", saddr.c_str());
}

void set_signal_data(const std::string& saddr, const std::string& signal_type, const std::string& signal_data)
{
    std::vector<stru_ws_signal> *p_signals = NULL;
    
    if(signal_data.empty())
    {
        LOG_E("set_signal_data: signal_data is empty, saddr = %s", saddr.c_str());
        return;
    }

    if(signal_type == "out")
    {
        p_signals = &g_ws_out_signals;
        for(uint32_t i = 0; i < p_signals->size(); i++)
        {
            stru_ws_signal *p_signal = &p_signals->at(i);
            if(p_signal->saddr == saddr)
            {
                if(0 != dc_set_out_signal_val(saddr, (void *)signal_data.c_str()))
                {
                    LOG_E("set_signal_data: dc_set_signal_val failed, saddr = %s, signal_data = %s", saddr.c_str(), signal_data.c_str());
                    return;
                }
            }
        }
    }
    else if(signal_type == "in")
    {
        LOG_E("signal_type is in, not support set_signal_data, saddr = %s");
        return;
    }
    else if(signal_type == "yk")
    {
        p_signals = &g_ws_yk_signals;

        uint8_t ctrl_status = atoi(signal_data.c_str());

        for(uint32_t i = 0; i < p_signals->size(); i++)
        {
            stru_ws_signal *p_signal = &p_signals->at(i);
            if(p_signal->saddr == saddr)
            {
                if(p_signal->ctrl_type == SIGNAL_CTRL_TYPE::DIRECT_NORMAL)
                {
                    if(0 != dc_signal_yk_set_status(saddr, SIGNAL_CTRL_STEP::DIRECT, p_signal->data_type, (void *)&ctrl_status))
                    {
                        LOG_E("set_signal_data: dc_signal_yk_set_status direct failed, saddr = %s, signal_data = %s", saddr.c_str(), signal_data.c_str());
                        return;
                    }

                    LOG_I("saddr %s, direct, value %d success !!!", saddr.c_str(), ctrl_status);
                    return;
                }
                else if(p_signal->ctrl_type == SIGNAL_CTRL_TYPE::SBO_NORMAL)
                {
                    // LOG_I("saddr %s, select, value %d", saddr.c_str(), ctrl_status);
                    if(0 != dc_signal_yk_set_status(saddr, SIGNAL_CTRL_STEP::SELECT, p_signal->data_type, (void *)&ctrl_status))
                    {
                        LOG_E("set_signal_data: dc_signal_yk_set_status select failed, saddr = %s, signal_data = %s", saddr.c_str(), signal_data.c_str());
                        return;
                    }

                    task_sleep_ms(1000);
                    MY_LOG_I("saddr %s, select value %d success !!!", saddr.c_str(), ctrl_status);
                    // LOG_I("saddr %s, direct, value %d", saddr.c_str(), ctrl_status);
                    if(0 != dc_signal_yk_set_status(saddr, SIGNAL_CTRL_STEP::DIRECT, p_signal->data_type, (void *)&ctrl_status))
                    {
                        LOG_E("set_signal_data: dc_signal_yk_set_status direct failed, saddr = %s, signal_data = %s", saddr.c_str(), signal_data.c_str());
                        return;
                    }

                    MY_LOG_I("saddr %s, direct value %d success", saddr.c_str(), ctrl_status);
                    return;
                }
                else
                {
                    LOG_E("p_signal->ctrl_type is invalid, p_signal->ctrl_type = %d");
                    return;
                }
            }
        }
    }
    else if(signal_type == "param")
    {
        p_signals = &g_ws_param_signals;

        for(uint32_t i = 0; i < p_signals->size(); i++)
        {
            stru_ws_signal *p_signal = &p_signals->at(i);
            if(p_signal->saddr == saddr)
            {   
                if(p_signal->ctrl_type == SIGNAL_CTRL_TYPE::DIRECT_NORMAL)
                {
                    if(0 != dc_signal_param_set_val(saddr, SIGNAL_CTRL_STEP::DIRECT, p_signal->data_type, (void *)signal_data.c_str()))
                    {
                        LOG_E("set_signal_data: dc_signal_param_set_val direct failed, saddr = %s, signal_data = %s", saddr.c_str(), signal_data.c_str());
                        return;
                    }

                    LOG_I("saddr %s, direct, value %s success !!!", saddr.c_str(), signal_data.c_str());
                    return;
                }
                else if(p_signal->ctrl_type == SIGNAL_CTRL_TYPE::SBO_NORMAL)
                {
                    if(0 != dc_signal_param_set_val(saddr, SIGNAL_CTRL_STEP::SELECT, p_signal->data_type, (void *)signal_data.c_str()))
                    {
                        LOG_E("set_signal_data: dc_signal_param_set_val select failed, saddr = %s, signal_data = %s", saddr.c_str(), signal_data.c_str());
                        return;
                    }

                    LOG_I("saddr %s, select, value %s success !!!", saddr.c_str(), signal_data.c_str());

                    if(0 != dc_signal_param_set_val(saddr, SIGNAL_CTRL_STEP::DIRECT, p_signal->data_type, (void *)signal_data.c_str()))
                    {
                        LOG_E("set_signal_data: dc_signal_param_set_val direct failed, saddr = %s, signal_data = %s", saddr.c_str(), signal_data.c_str());
                        return;
                    }

                    LOG_I("saddr %s, direct, value %s success", saddr.c_str(), signal_data.c_str());
                    return;
                }
                else
                {
                    LOG_E("p_signal->ctrl_type is invalid, p_signal->ctrl_type = %d");
                    return;
                }
            }
        }
    }
    else
    {
        LOG_E("set_signal_data: invalid signal_type = %s", signal_type.c_str());
        return;
    }


    LOG_E("set_signal_data: not found saddr = %s", saddr.c_str());
}


void ws_recv(const char* p_rx, uint16_t rx_len)
{
    if(NULL == p_rx || 0 == rx_len)
    {
        LOG_E("ws_recv: invalid parameter, p_rx = %p, rx_len = %d", p_rx, rx_len);
        return;
    }

    cJSON* root = cJSON_Parse(p_rx);
    if(NULL == root)
    {
        LOG_E("ws_recv: cJSON_Parse failed, p_rx = %s", p_rx);
        return;
    }
    
    if(!cJSON_IsObject(root))
    {
        LOG_E("ws_recv: root is not an object, p_rx = %s", p_rx);
        cJSON_Delete(root);
        return;
    }
    
    cJSON *saddr = cJSON_GetObjectItem(root, "saddr");
    cJSON *signal_type = cJSON_GetObjectItem(root, "signal_type");
    cJSON *signal_data = cJSON_GetObjectItem(root, "signal_data");
    cJSON *curd = cJSON_GetObjectItem(root, "curd");


    std::string saddr_str = "", signal_type_str = "", signal_data_str = "", curd_str = "";

    if(saddr && cJSON_IsString(saddr) && saddr->valuestring)
    {
        saddr_str = saddr->valuestring;
    }

    if(signal_type && cJSON_IsString(signal_type) && signal_type->valuestring)
    {
        signal_type_str = signal_type->valuestring;
    }

    if(signal_data && cJSON_IsString(signal_data) && signal_data->valuestring)
    {
        signal_data_str = signal_data->valuestring;
    }

    if(curd && cJSON_IsString(curd) && curd->valuestring)
    {
        curd_str = curd->valuestring;
    }

    cJSON_Delete(root);
    if(saddr_str.empty())
    {
        LOG_E("ws_recv: saddr is empty, p_rx = %s", p_rx);
        return;
    }
    
    if(signal_type_str.empty())
    {
        LOG_E("ws_recv: signal_type is empty, p_rx = %s", p_rx);
        return;
    }

    if(curd_str.empty())
    {
        LOG_E("ws_recv: curd is empty, p_rx = %s", p_rx);
        return;
    }

    if(0 == curd_str.compare("add"))
    {
        add_signal(saddr_str, signal_type_str);
    }
    else if(0 == curd_str.compare("del"))
    {
        del_signal(saddr_str, signal_type_str);
    }
    else if(0 == curd_str.compare("set"))
    {
        set_signal_data(saddr_str, signal_type_str, signal_data_str);
    }
    
}


void ws_task()
{
    if(g_ws_out_signals.empty() && g_ws_in_signals.empty() && g_ws_yk_signals.empty() && g_ws_param_signals.empty())
    {
        return;
    }

    const char ctrl_type_str[][16] = {"NONE", "DIRECT_NORMAL", "SBO_NORMAL"};

    cJSON *root = cJSON_CreateObject();
    if(NULL == root)
    {
        LOG_E("ws_task: cJSON_CreateObject failed");
        return;
    }

    cJSON *out_arr = cJSON_CreateArray();
    cJSON_AddItemToObject(root, "out", out_arr);

    for(uint32_t i = 0; i < g_ws_out_signals.size(); i++)
    {
        stru_ws_signal *p_signal = &g_ws_out_signals.at(i);
        cJSON *item = cJSON_CreateObject();
        if(NULL == item)
        {
            LOG_E("ws_task: cJSON_CreateObject failed");
            cJSON_Delete(root);
            return;
        }

        cJSON_AddItemToObject(item, "saddr", cJSON_CreateString(p_signal->saddr.c_str()));
        cJSON_AddItemToObject(item, "desc", cJSON_CreateString(p_signal->desc.c_str()));
        cJSON_AddItemToObject(item, "type", cJSON_CreateString(dc_get_data_type_str_by_id(p_signal->data_type).c_str()));

        std::string val = dc_get_signal_val(p_signal->p_data, p_signal->data_type);
        cJSON_AddItemToObject(item, "val", cJSON_CreateString(val.c_str()));
        cJSON_AddItemToArray(out_arr, item);
    }

    cJSON *in_arr = cJSON_CreateArray();
    cJSON_AddItemToObject(root, "in", in_arr);

    for(uint32_t i = 0; i < g_ws_in_signals.size(); i++)
    {
        stru_ws_signal *p_signal = &g_ws_in_signals.at(i);
        cJSON *item = cJSON_CreateObject();
        if(NULL == item)
        {
            LOG_E("ws_task: cJSON_CreateObject failed");
            cJSON_Delete(root);
            return;
        }

        cJSON_AddItemToObject(item, "saddr", cJSON_CreateString(p_signal->saddr.c_str()));
        cJSON_AddItemToObject(item, "desc", cJSON_CreateString(p_signal->desc.c_str()));
        cJSON_AddItemToObject(item, "type", cJSON_CreateString(dc_get_data_type_str_by_id(p_signal->data_type).c_str()));
        std::string val = dc_get_signal_val(p_signal->p_data, p_signal->data_type);
        cJSON_AddItemToObject(item, "val", cJSON_CreateString(val.c_str()));
        cJSON_AddItemToArray(in_arr, item);
    }

    cJSON *yk_arr = cJSON_CreateArray();
    cJSON_AddItemToObject(root, "yk", yk_arr);
    for(uint32_t i = 0; i < g_ws_yk_signals.size(); i++)
    {
        stru_ws_signal *p_signal = &g_ws_yk_signals.at(i);
        cJSON *item = cJSON_CreateObject();
        if(NULL == item)
        {
            LOG_E("ws_task: cJSON_CreateObject failed");
            cJSON_Delete(root);
            return;
        }

        cJSON_AddItemToObject(item, "saddr", cJSON_CreateString(p_signal->saddr.c_str()));
        cJSON_AddItemToObject(item, "desc", cJSON_CreateString(p_signal->desc.c_str()));
        cJSON_AddItemToObject(item, "type", cJSON_CreateString(dc_get_data_type_str_by_id(p_signal->data_type).c_str()));
        std::string val = dc_get_signal_val(p_signal->p_data, p_signal->data_type);
        cJSON_AddItemToObject(item, "val", cJSON_CreateString(val.c_str()));
        cJSON_AddItemToObject(item, "ctrl_type", cJSON_CreateString(ctrl_type_str[p_signal->ctrl_type]));
        cJSON_AddItemToArray(yk_arr, item);
    }

    cJSON *param_arr = cJSON_CreateArray();
    cJSON_AddItemToObject(root, "param", param_arr);

    for(uint32_t i = 0; i < g_ws_param_signals.size(); i++)
    {
        stru_ws_signal *p_signal = &g_ws_param_signals.at(i);
        cJSON *item = cJSON_CreateObject();
        if(NULL == item)
        {
            LOG_E("ws_task: cJSON_CreateObject failed");
            cJSON_Delete(root);
            return;
        }

        cJSON_AddItemToObject(item, "saddr", cJSON_CreateString(p_signal->saddr.c_str()));
        cJSON_AddItemToObject(item, "desc", cJSON_CreateString(p_signal->desc.c_str()));
        cJSON_AddItemToObject(item, "type", cJSON_CreateString(dc_get_data_type_str_by_id(p_signal->data_type).c_str()));
        std::string val = dc_get_signal_val(p_signal->p_data, p_signal->data_type);
        cJSON_AddItemToObject(item, "val", cJSON_CreateString(val.c_str()));
        cJSON_AddItemToObject(item, "ctrl_type", cJSON_CreateString(ctrl_type_str[p_signal->ctrl_type]));
        if(p_signal->p_param)
        {
            cJSON_AddItemToObject(item, "min", cJSON_CreateString(std::to_string(p_signal->p_param->min).c_str()));
            cJSON_AddItemToObject(item, "max", cJSON_CreateString(std::to_string(p_signal->p_param->max).c_str()));
            cJSON_AddItemToObject(item, "step", cJSON_CreateString(std::to_string(p_signal->p_param->step).c_str()));
            cJSON_AddItemToObject(item, "unit", cJSON_CreateString(p_signal->p_param->unit.c_str()));
            if(p_signal->p_param->p_default)
            {
                cJSON_AddItemToObject(item, "default", cJSON_CreateString(dc_get_signal_val(p_signal->p_param->p_default, p_signal->data_type).c_str()));
            }
            else
            {
                cJSON_AddItemToObject(item, "default", cJSON_CreateString(""));
            }
        }
        cJSON_AddItemToArray(param_arr, item);
    }

    char *p_tx = cJSON_Print(root);
    if(NULL == p_tx)
    {
        LOG_E("ws_task: cJSON_Print failed");
        cJSON_Delete(root);
        return;
    }

    ws_send(p_tx, strlen(p_tx));

    cJSON_Delete(root);
    free(p_tx);

}