#include "iec61850m.h"
#include "myLog.h"
#include "myCmd.h"
#include "iec61850_client.h"


LOCAL void mms_event_back(void *arg, int ret);

LOCAL const char *g_61850m_prj_path = "/mnt/RTU/test/file/MMS/";

typedef struct 
{
    int fd;
    const char *prj_name;
    int debug;
    uint32_t connectionTimeout; // 61850库默认10000
    stru_mms_m_event mms_event;
    stru_mms_m_config mms_config;
}stru_iec61850m_info;;


std::vector<stru_iec61850m_info> g_vec_iec61850m_info = 
{
    {
        .fd = -1, 
        .prj_name = "prj.xml",
        .debug = MMS_M_DEBUG_PRINT_OFF,
        .connectionTimeout = 10000,
        .mms_event = {
            .p_func = mms_event_back,
        }
    }
};


LOCAL void mms_send_control(int fd, const char *saddr, int val, int type)
{
    stru_mms_m_event *p_event = NULL;

    for(int i = 0; i < g_vec_iec61850m_info.size(); i++)
    {
        if(fd == g_vec_iec61850m_info[i].fd)
        {
            p_event = &g_vec_iec61850m_info[i].mms_event;
            break;
        }
    }

    if(NULL == p_event)
    {
        LOG_E("fd %d not found");
        return;
    }

    p_event->app_fd = fd;
    memset(p_event->ied, 0, MMS_M_STR_LEN);
    snprintf(p_event->name, MMS_M_STR_LEN, "%s", saddr);
    p_event->ctrl_type = type;
    // p_event->val.b = val == 0 ? false : true;
    uint8_t val_b = (val == 0 ? 0 : 1);

    MY_LOG_I("mms_send_control val_b %d, ctrl_type %d", val_b, p_event->ctrl_type);
    // p_event->p_val = (void *)&val_b;
    std::string val_str = std::to_string(val_b);
    strncpy(p_event->val, val_str.c_str(), 128);
    // mms_m_set_data_value((void *)p_event->val, (void *)&val_b, _MMS_M_data_type_bool);

    MY_LOG_I("p_event->val %s",  p_event->val);

    mms_m_out_do_set_yk_or_param(p_event);
}

LOCAL void mms_event_back(void *arg, int ret)
{
    stru_mms_m_event *p_event = (stru_mms_m_event *)arg;
    if(NULL == p_event)
    {
        printf("mms_event_back arg null\n");
        return;
    }

    printf("fd %d, ied %s, name %s, ctrl_type %d, ret %d\n", 
        p_event->app_fd, p_event->ied, p_event->name, p_event->ctrl_type, ret);
}

LOCAL void mms_data_back(stru_mms_m_out_value *p)
{
    if(NULL == p)
    {
        printf("input data is NULL\n");
        return;
    }

    if(p->reason != IEC61850_REASON_DATA_CHANGE)
    {
        return;
    }

    printf("fd %d: name %s, desc %s, ref %s, quality %d, type %d, reason %s(%d), ", p->app_fd, p->name, p->desc, p->reference, p->quality, p->type, mms_m_out_reason_str(p->reason), p->reason);

    switch (p->type)
    {
        case MMS_BOOLEAN:
            printf("value %d, ", MY_GET_DATA_WITH_TYPE(p->p_value, uint8_t));
            break;
        case MMS_INTEGER:
            printf("value %d, ", MY_GET_DATA_WITH_TYPE(p->p_value, int32_t));
            break;
        case MMS_FLOAT:
            printf("value %f, ", MY_GET_DATA_WITH_TYPE(p->p_value, float));
            break;
        default:
            break;
    }
    stru_mms_m_time *t = &p->time;
    printf("%d-%d-%d %02d:%02d:%02d:%d\n", t->year, t->mon, t->day, t->hour, t->min, t->sec, t->msec);
}

LOCAL void iec61850m_connect_status(int fd, int status)
{
    std::string prj_name = "";
    for(int i = 0; i < g_vec_iec61850m_info.size(); i++)
    {
        if(fd == g_vec_iec61850m_info[i].fd)
        {
            prj_name = g_vec_iec61850m_info[i].prj_name;
            break;
        }
    }

    if(prj_name.empty())
    {
        LOG_E("iec61850m_connect_status, fd %d, status %s, prj_name not found", fd, status == MMS_M_ON_LINE? "ON_LINE" : "OFF_LINE");
        return;
    }

    LOG_I("iec61850m_connect_status, fd %d, prj_name %s, status %s", fd, prj_name.c_str(), status == MMS_M_ON_LINE? "ON_LINE" : "OFF_LINE");
}

void iec61850m_init()
{
    for(int i = 0; i < g_vec_iec61850m_info.size(); i++)
    {
        stru_iec61850m_info *p_info = &g_vec_iec61850m_info[i];

        std::string cfg_path = g_61850m_prj_path + std::string(p_info->prj_name);
        p_info->fd = mms_m_out_init(cfg_path.c_str(), p_info->debug, p_info->connectionTimeout);
        if(p_info->fd < 0)
        {
            LOG_E("mms_m_out_init failed, fd %d, no %d, prj_name %s", p_info->fd, i, p_info->prj_name);
            continue;
        }

        LOG_I("mms_m_out_init success, fd %d, no %d, prj_name %s", p_info->fd, i, p_info->prj_name);
        mms_m_out_get_value(p_info->fd, mms_data_back);
        mms_m_out_get_connect_status(p_info->fd, iec61850m_connect_status);
    }
}

void iec61850m_signal_change_callback(std::string saddr, SIGNAL_CTRL_STEP step, uint8_t data_type, void *p_data)
{
    std::string value = "";
    if(p_data)
    {   
        value = dc_get_signal_val(p_data, data_type);
    }

    uint8_t ctrl_type = 0;

    std::string step_str = "";
    switch(step)
    {
        case SIGNAL_CTRL_STEP::READY:
            step_str = "READY";
            break;
        case SIGNAL_CTRL_STEP::SELECT:
            step_str = "SELECT";
            ctrl_type = _MMS_M_EVENT_CO_SELECT;
            break;
        case SIGNAL_CTRL_STEP::DIRECT:
            step_str = "DIRECT";
            ctrl_type = _MMS_M_EVENT_CO_DIRECT;
            break;
        case SIGNAL_CTRL_STEP::CANCEL:
            step_str = "CANCEL";
            ctrl_type = _MMS_M_EVENT_CO_CANCEL;
            break;
    }

    std::string data_type_str = "";
    data_type_str = dc_get_data_type_str_by_id(data_type);

    MY_LOG_I("iec61850m_signal_change_callback saddr %s, step %s, data_type %s, value %s", saddr.c_str(), step_str.c_str(), data_type_str.c_str(), value.c_str());

    for(int i = 0; i < g_vec_iec61850m_info.size(); i++)
    {
        stru_iec61850m_info *p_info = &g_vec_iec61850m_info[i];
        stru_mms_m_config *p_config = &p_info->mms_config;

        for(uint16_t j = 0; j < p_config->co_num; j++)
        {
            if(p_config->p_co_sig[j].sig.saddr == saddr)
            {
                int val = (int)(*(uint8_t *)p_data);
                MY_LOG_I("iec61850m_signal_change_callback val %d, ctrl_type %d", val, ctrl_type);
                mms_send_control(p_info->fd, saddr.c_str(), val, ctrl_type);
            }
        }
    }
}

std::map<uint8_t, uint8_t> g_mms_m_type_to_local_type = {
    {MMS_BOOLEAN, DATA_TYPE_U8},
    {MMS_INTEGER, DATA_TYPE_S32},
    {MMS_UNSIGNED, DATA_TYPE_U32},
    {MMS_FLOAT, DATA_TYPE_F32},
    {MMS_STRING, DATA_TYPE_C64},
};

std::map<uint8_t, uint8_t> g_mms_m_ctrl_type_to_local_ctrl_type = {
    {CONTROL_MODEL_STATUS_ONLY, SIGNAL_CTRL_TYPE::NONE},
    {CONTROL_MODEL_DIRECT_NORMAL, SIGNAL_CTRL_TYPE::DIRECT_NORMAL},
    {CONTROL_MODEL_SBO_NORMAL, SIGNAL_CTRL_TYPE::SBO_NORMAL},
    {CONTROL_MODEL_DIRECT_ENHANCED, SIGNAL_CTRL_TYPE::DIRECT_NORMAL},
    {CONTROL_MODEL_SBO_ENHANCED, SIGNAL_CTRL_TYPE::SBO_NORMAL}
};

int iec61850m_signal_init(stru_mms_m_config &cfg)
{
    int ret = 0;

    for(uint16_t i = 0; i < cfg.st_num; i++)
    {
        uint8_t type_local = g_mms_m_type_to_local_type[cfg.p_st_sig[i].data_type];
        ret |= dc_signal_out(cfg.p_st_sig[i].saddr, cfg.p_st_sig[i].desc, type_local, cfg.p_st_sig[i].p_data);
    }

    if(ret != 0)
    {
        LOG_E("dc_signal_out failed, ret %d", ret);
        return -1;
    }

    for(uint16_t i = 0; i < cfg.mx_num; i++)
    {
        uint8_t type_local = g_mms_m_type_to_local_type[cfg.p_mx_sig[i].data_type];
        ret |= dc_signal_out(cfg.p_mx_sig[i].saddr, cfg.p_mx_sig[i].desc, type_local, cfg.p_mx_sig[i].p_data);
    }

    if(ret != 0)
    {
        LOG_E("dc_signal_out failed, ret %d", ret);
        return -1;
    }
    
    for(uint16_t i = 0; i < cfg.co_num; i++)
    {
        uint8_t type_local = g_mms_m_type_to_local_type[cfg.p_co_sig[i].sig.data_type];
        uint8_t ctrl_type_local = g_mms_m_ctrl_type_to_local_ctrl_type[cfg.p_co_sig[i].ctrl_type];
        ret |= dc_signal_yk(cfg.p_co_sig[i].sig.saddr, cfg.p_co_sig[i].sig.desc, type_local, ctrl_type_local, cfg.p_co_sig[i].sig.p_data, iec61850m_signal_change_callback);
    }

    if(ret != 0)
    {
        LOG_E("dc_signal_yk failed, ret %d", ret);
        return -1;
    }

    for(uint16_t i = 0; i < cfg.ao_num; i++)
    {
        stru_signal_param param;
        param.min = cfg.p_ao_sig[i].min;
        param.max = cfg.p_ao_sig[i].max;
        param.step = cfg.p_ao_sig[i].step;
        param.unit = cfg.p_ao_sig[i].unit;
        param.p_default = NULL;

        uint8_t type_local = g_mms_m_type_to_local_type[cfg.p_ao_sig[i].sig.data_type];
        ret |= dc_signal_param(cfg.p_ao_sig[i].sig.saddr, cfg.p_ao_sig[i].sig.desc, type_local, param, cfg.p_ao_sig[i].ctrl_type, cfg.p_ao_sig[i].sig.p_data, iec61850m_signal_change_callback);
    }

    if(ret != 0)
    {
        LOG_E("dc_signal_param failed, ret %d", ret);
        return -1;
    }

    return 0;
}

static uint32_t *p_iec_run_cnt = NULL;

int app_iec61850m_init1(void *arg)
{
    iec61850m_init();
    stru_app *p_app = (stru_app *)arg;

    dc_signal_out("iec61850m.run_cnt", "iec61850m.run_cnt", DATA_TYPE_U32, &p_app->run_cnt);

    for(int i = 0; i < g_vec_iec61850m_info.size(); i++)
    {
        stru_iec61850m_info *p_info = &g_vec_iec61850m_info[i];
        stru_mms_m_config *p_config = &p_info->mms_config;

        if(0 != mms_m_out_get_signal_info(p_info->fd, p_config))
        {
            LOG_E("mms_m_out_get_signal_info failed, fd %d, no %d, prj_name %s", p_info->fd, i, p_info->prj_name);
            return -1;
        }

        if(0 != iec61850m_signal_init(*p_config))
        {
            LOG_E("iec61850m_signal_init failed, fd %d, no %d, prj_name %s", p_info->fd, i, p_info->prj_name);
            return -1;
        }
    }


    return 0;
}

int app_iec61850m_init2(void *arg)
{

    dc_signal_in("iec61850m.iec_run_cnt_in", "iec61850m.iec_run_cnt_in", "iec.run_cnt", DATA_TYPE_U32, (void **)&p_iec_run_cnt);

    MY_LOG_I("p_iec_run_cnt %p", p_iec_run_cnt);
    return 0;
}

void *app_iec61850m(void *arg)
{
    if(NULL == arg)
    {
        LOG_E("app_com_scan arg null");
        return NULL;
    }

    stru_app *p_app = (stru_app *)arg;

    uint32_t event;
    stru_app_msg_queue *p_mq = NULL;
    stru_msg_head *p_head = NULL;
    stru_rtx_data *p_rtx = &p_app->rtx;



    while (1)
    {
        task_event_recv(p_app->p_event, 
            EV_TIMER1 | EV_TIMER2 | EV_TIMER3 ,  
            TASK_EVENT_FLAG_OR | TASK_EVENT_FLAG_CLEAR,
            TASK_EVENT_WAIT_FOREVER,
            &event);


        if(event & EV_TIMER1)
        {
            ;
        }

        if(event & EV_TIMER2)
        {
            ;
        }

        if(event & EV_TIMER3)
        {
            p_app->run_cnt++;
        }
    }
    
}



LOCAL void iec61850m_show_info()
{
    for(int i = 0; i < g_vec_iec61850m_info.size(); i++)
    {
        stru_iec61850m_info *p_info = &g_vec_iec61850m_info[i];
        printf("fd %d, prj_name %s\n", p_info->fd, p_info->prj_name);
    }
}


LOCAL void cmd_iec61850m(int argc, char *argv[])
{
    const char *str = "\
        iec61850m info ----------> 查看iec61850m信息\n\
        iec61850m yk <fd> <saddr> <val> <ctrl_type> ------遥控，fd对应61850m的fd, 短地址，值，控制类型\n\
        iec61850m set <fd> <saddr> <val> <set_type> ------修改定值，fd对应61850m的fd, 短地址，值，设置类型\n";

    stru_app *p_app = app_get_ptr(ENUM_APP_IEC61850M);
    if(NULL == p_app)
    {
        LOG_E("app_iec61850m null");
        return;
    }

    if(argc < 2)
    {
        printf("%s", str);
        return;
    }

    if(0 == strcmp(argv[1], "info"))
    {
        iec61850m_show_info();
    }
    else if(0 == strcmp(argv[1], "yk"))
    {
        if(argc < 6)
        {
            printf("iec61850m yk <fd> <saddr> <val> <ctrl_type>\n");
            return;
        }

        int fd = atoi(argv[2]);
        std::string saddr = argv[3];
        int val = atoi(argv[4]);
        int ctrl_type = atoi(argv[5]);

        mms_send_control(fd, saddr.c_str(), val, ctrl_type);
    }
}

CMD_REGISTER("iec61850m", cmd_iec61850m, "iec61850客户端线程的控制命令");