#include "mms_m_parse_cfg.h"
#include "mms_m.h"
#include "tinyxml2.h"


static const char *ELE_Root     = "Root";
static const char *ELE_Para     = "Para";
static const char *ELE_Point    = "Point";
static const char *ELE_St       = "St";
static const char *ELE_Mx       = "Mx";
static const char *ELE_Co       = "Co";
static const char *ELE_Ao       = "Ao";
static const char *ELE_Item     = "Item";

static const char *KEY_NO           = "no";
static const char *KEY_NAME         = "name";
static const char *KEY_DESC         = "desc";
static const char *KEY_HOST_IP      = "host_ip";
static const char *KEY_HOST_PORT    = "host_port";
static const char *KEY_IED          = "ied";
static const char *KEY_TYPE         = "type";
static const char *KEY_CTRL_MODEL   = "ctlModel";
static const char *KEY_LDEV         = "LDev";
static const char *KEY_LNODE        = "LNode";
static const char *KEY_DONAME       = "DoName";
static const char *KEY_FC           = "fc";
static const char *KEY_MIN_VAL      = "minVal";
static const char *KEY_MAX_VAL      = "maxVal";
static const char *KEY_STEP_VAL     = "stepVal";
static const char *KEY_UNIT         = "unit";
static const char *KEY_DEFAULT_VAL  = "default";


/***************************************************************************************/







void parse_cfg_item(tinyxml2::XMLElement *item, std::vector<stru_point_item> &item_list, const std::string &ied)
{
    if(NULL == item)
    {
        LOG_E("item is NULL");
        return;
    }

    const char *str = NULL;
    if(NULL != (str = item->Attribute(KEY_NO)))
    {
        ;
    }
    else
    {
        LOG_E("no attribute %s in item", KEY_NO);
        return;
    }

    for(std::vector<stru_point_item>::iterator it = item_list.begin(); it!= item_list.end(); ++it)
    {
        if(it->no == atoi(str))
        {
            LOG_E("item no %s already exists", str);
            return;
        }
    }

    stru_point_item item_info;
    item_info.no = atoi(str);

    if(NULL != (str = item->Attribute(KEY_NAME)))
    {
        item_info.name = str;
    }
    else
    {
        LOG_E("no attribute %s in item", KEY_NAME);
        return;
    }

    if(NULL != (str = item->Attribute(KEY_DESC)))
    {
        item_info.desc = str;
    }

    if(NULL != (str = item->Attribute(KEY_LDEV)))
    {
        item_info.ldev = str;
    }
    else
    {
        LOG_E("no attribute %s in item", KEY_LDEV);
        return;
    }
    
    if(NULL != (str = item->Attribute(KEY_LNODE)))
    {
        item_info.lnode = str;
    }
    else
    {
        LOG_E("no attribute %s in item", KEY_LNODE);
        return;
    }
    
    if(NULL != (str = item->Attribute(KEY_DONAME)))
    {
        item_info.doname = str;
    }
    else
    {
        LOG_E("no attribute %s in item", KEY_DONAME);
        return;
    }
    
    if(NULL != (str = item->Attribute(KEY_FC)))
    {
        item_info.fc = atoi(str);
    }
    else
    {
        LOG_E("no attribute %s in item", KEY_FC);
        return;
    }

    if(NULL != (str = item->Attribute(KEY_TYPE)))
    {
        item_info.type = atoi(str);
    }
    else
    {
        LOG_E("no attribute %s in item", KEY_TYPE);
        return;
    }

    if(NULL != (str = item->Attribute(KEY_CTRL_MODEL)))
    {
        item_info.ctrl_model = atoi(str);
    }

    if(NULL != (str = item->Attribute(KEY_MIN_VAL)))
    {
        item_info.min_value = atof(str);
    }

    if(NULL != (str = item->Attribute(KEY_MAX_VAL)))
    {
        item_info.max_value = atof(str);
    }

    if(NULL != (str = item->Attribute(KEY_STEP_VAL)))
    {
        item_info.step_value = atof(str);
    }

    if(NULL != (str = item->Attribute(KEY_UNIT)))
    {
        item_info.unit = str;
    }



    item_info.reference = ied + item_info.ldev + "/" + item_info.lnode + "." + item_info.doname;

    item_list.push_back(item_info);    

    item_list.back().set_value = NULL;
    item_list.back().control = NULL;

    stru_point_item *p_item = &item_list.back();

    if(NULL != (str = item->Attribute(KEY_DEFAULT_VAL)))
    {
        p_item->p_default = mms_m_create_data_ptr(p_item->type);
        if(NULL != p_item->p_default)
        {
            mms_m_set_data_value(p_item->p_default, (void *)str, p_item->type);
        }
    }

    p_item->value.p_val = mms_m_create_data_ptr(p_item->type);
}





static int parse_cfg_para(tinyxml2::XMLElement *para, stru_para_page &para_page, std::string &ied)
{
    if (NULL == para)
    {
        LOG_E("para is NULL");
        return -1;
    }
    const char *str = NULL;

    if(NULL != (str = para->Attribute(KEY_DESC)))
    {
        para_page.desc = str;
    }

    if(NULL != (str = para->Attribute(KEY_HOST_IP)))
    {
        para_page.host_ip = str;
    }
    else
    {
        LOG_E("no attribute %s in para", KEY_HOST_IP);
        return -1;
    }
    
    if(NULL != (str = para->Attribute(KEY_HOST_PORT)))
    {
        para_page.host_port = atoi(str);
    }
    else
    {
        LOG_E("no attribute %s in para", KEY_HOST_PORT);
        return -1;
    }
    
    if(NULL != (str = para->Attribute(KEY_IED)))
    {
        para_page.ied = str;
    }
    else
    {
        LOG_E("no attribute %s in para", KEY_IED);
        return -1;
    }

    ied = para_page.ied;

    return 0;
}

static int parse_cfg_point(tinyxml2::XMLElement *point, stru_point_page &point_page, const std::string &ied)
{
    if(NULL == point)
    {
        LOG_E("point is NULL");
        return -1;
    }
    
    tinyxml2::XMLElement *st, *mx, *co, *ao, *item;

    if(NULL != (st = point->FirstChildElement(ELE_St)))
    {
        for(item = st->FirstChildElement(ELE_Item); item; item = item->NextSiblingElement(ELE_Item))
        {
            parse_cfg_item(item, point_page.st, ied);
        }
    }

    if(NULL != (mx = point->FirstChildElement(ELE_Mx)))
    {
        for(item = mx->FirstChildElement(ELE_Item); item; item = item->NextSiblingElement(ELE_Item))
        {
            parse_cfg_item(item, point_page.mx, ied);
        }
    }

    if(NULL != (co = point->FirstChildElement(ELE_Co))) 
    {
        for(item = co->FirstChildElement(ELE_Item); item; item = item->NextSiblingElement(ELE_Item))
        {
            parse_cfg_item(item, point_page.co, ied);
        }
    }

    if(NULL != (ao = point->FirstChildElement(ELE_Ao)))
    {
        for(item = ao->FirstChildElement(ELE_Item); item; item = item->NextSiblingElement(ELE_Item))
        {
            parse_cfg_item(item, point_page.ao, ied);
        }
    }

    return 0;
}



stru_cfg *parse_cfg(const std::string &cfg_file)
{
    if (cfg_file.empty())
    {
        LOG_E("cfg_file is empty");
        return NULL;
    }

    stru_cfg *p_cfg = new stru_cfg;
    if (NULL == p_cfg)
    {
        LOG_E("create cfg struct failed");
        return NULL;
    }

    memset(p_cfg, 0, sizeof(stru_cfg));

    if(NULL == strstr(cfg_file.c_str(), ".xml"))
    {
        LOG_E("cfg_file is not an xml file");
        return NULL;
    }

    std::string ied = "";
    tinyxml2::XMLDocument doc;
    tinyxml2::XMLElement *root, *para, *point;
    tinyxml2::XMLError err = doc.LoadFile(cfg_file.c_str());
    if (err != tinyxml2::XML_SUCCESS)
    {
        LOG_E("load cfg file %s failed, error code: %d", cfg_file.c_str(), err);
        goto EXIT;
    }

    root = doc.FirstChildElement(ELE_Root);
    if (NULL == root)
    {
        LOG_E("no Root element in cfg file");
        goto EXIT;
    }
    
    para = root->FirstChildElement(ELE_Para);
    if (NULL == para)
    {
        LOG_E("no Para element in cfg file");
        goto EXIT;
    }
    
    if(0 != parse_cfg_para(para, p_cfg->para, ied))
    {
        LOG_E("parse Para element in cfg file failed");
        goto EXIT;
    }

    point = root->FirstChildElement(ELE_Point);
    if (NULL == point)
    {
        LOG_E("no Point element in cfg file");
        goto EXIT;
    }

    if(0 != parse_cfg_point(point, p_cfg->point, ied))
    {
        LOG_E("parse Point element in cfg file failed");
        goto EXIT;
    }

    doc.Clear();
    return p_cfg;

EXIT:
    delete p_cfg;

    return NULL;
}

void parse_show_cfg(stru_cfg *p_cfg)
{
    if (NULL == p_cfg)
    {
        LOG_E("p_cfg is NULL");
        return;
    }

    printf("Para-%s: ip %s, port %d, ied %s\n", 
        p_cfg->para.desc.c_str(), p_cfg->para.host_ip.c_str(), p_cfg->para.host_port, p_cfg->para.ied.c_str());

    printf("Point-%s:\n", p_cfg->point.desc.c_str());
    printf("St-num %ld:\n", p_cfg->point.st.size());
    std::vector<stru_point_item>::iterator it;
    for(it = p_cfg->point.st.begin(); it!= p_cfg->point.st.end(); ++it)
    {
        printf("  no %d, type %d, fc %d, name %s, desc %s, ldev %s, lnode %s, doname %s, reference %s\n", 
            it->no, it->type, it->fc, it->name.c_str(), it->desc.c_str(), it->ldev.c_str(), it->lnode.c_str(), it->doname.c_str(), it->reference.c_str());
    }

    printf("Mx-num %ld:\n", p_cfg->point.mx.size());
    for(it = p_cfg->point.mx.begin(); it!= p_cfg->point.mx.end(); ++it)
    {
        printf("  no %d, type %d, fc %d, name %s, desc %s, ldev %s, lnode %s, doname %s, reference %s\n", 
            it->no, it->type, it->fc, it->name.c_str(), it->desc.c_str(), it->ldev.c_str(), it->lnode.c_str(), it->doname.c_str(), it->reference.c_str());
    }

    printf("Co-num %ld:\n", p_cfg->point.co.size());
    for(it = p_cfg->point.co.begin(); it!= p_cfg->point.co.end(); ++it)
    {
        printf("  no %d, type %d, fc %d, ctrl_model %d, name %s, desc %s, ldev %s, lnode %s, doname %s, reference %s\n", 
            it->no, it->type, it->fc, it->ctrl_model, it->name.c_str(), it->desc.c_str(), it->ldev.c_str(), it->lnode.c_str(), it->doname.c_str(), it->reference.c_str());
    }

    printf("Ao-num %ld:\n", p_cfg->point.ao.size());
    for(it = p_cfg->point.ao.begin(); it != p_cfg->point.ao.end(); ++it)
    {
        printf("  no %d, type %d, fc %d, name %s, desc %s, ldev %s, lnode %s, doname %s, reference %s\n", 
            it->no, it->type, it->fc, it->name.c_str(), it->desc.c_str(), it->ldev.c_str(), it->lnode.c_str(), it->doname.c_str(), it->reference.c_str());
    }
}

