#include "myBase.h"
#include "mySystem.h"
#include "tinyxml2.h"

LOCAL const char *ele_Root = "Root";

LOCAL const char *ele_St = "St";
LOCAL const char *ele_Mx = "Mx";
LOCAL const char *ele_Co = "Co";
LOCAL const char *ele_Dd = "Dd";
LOCAL const char *ele_Ao = "Ao";
LOCAL const char *ele_Param = "Param";

LOCAL const char *ele_Signal = "Signal";
LOCAL const char *ele_Item = "Item";
// LOCAL const char *ele_SubItem = "SubItem";

LOCAL const char *attr_count = "count";
LOCAL const char *attr_md5 = "md5";

LOCAL const char *attr_saddr = "saddr";
LOCAL const char *attr_desc = "desc";
LOCAL const char *attr_inf = "inf";
LOCAL const char *attr_type = "type";
LOCAL const char *attr_dc_type = "dc_type";
LOCAL const char *attr_unit = "unit";
LOCAL const char *attr_min = "min";
LOCAL const char *attr_max = "max";
LOCAL const char *attr_step = "step";
LOCAL const char *attr_num = "num";

LOCAL const char *attr_value = "value";
LOCAL const char *attr_default = "default";


LOCAL stru_self_ptl_cfg g_self_ptl_cfg = {.init = false};


typedef struct
{
    std::string type;
    std::vector<stru_self_ptl_cfg_base> *p_vec;
}stru_local_parse_base;

std::vector<stru_local_parse_base> g_local_parse_vec = 
{
    {ele_St, &g_self_ptl_cfg.st_vec},
    {ele_Mx, &g_self_ptl_cfg.mx_vec},
    {ele_Co, &g_self_ptl_cfg.co_vec},
    {ele_Dd, &g_self_ptl_cfg.dd_vec},
};

stru_self_ptl_cfg* self_ptl_cfg_get()
{
    if(!g_self_ptl_cfg.init)
    {
        MY_LOG_E("self_ptl_cfg not parsed yet, returning pointer with uninit data");
        return nullptr;
    }
    return &g_self_ptl_cfg;
}

LOCAL int parse_base(tinyxml2::XMLElement *root) 
{
    if (root == nullptr)
    {
        MY_LOG_E("parse failed, root element is null");
        return -1;
    }
    
    for (const auto &local_parse : g_local_parse_vec)
    {
        tinyxml2::XMLElement *parent_ele = root->FirstChildElement(local_parse.type.c_str());
        if (parent_ele == nullptr)
        {
            MY_LOG_E("not found element %s", local_parse.type.c_str());
            continue;
        }

        const char *count_str = parent_ele->Attribute(attr_count);
        int count = 0;
        if (count_str != nullptr)        
        {
            count = atoi(count_str);    
        }
        else
        {
            MY_LOG_E("not found attribute %s in element %s", attr_count, local_parse.type.c_str());
            continue;
        }

        tinyxml2::XMLElement *signal_ele = parent_ele->FirstChildElement(ele_Signal);
        while (signal_ele != nullptr)
        {
            stru_self_ptl_cfg_base cfg_base;
            const char *saddr = signal_ele->Attribute(attr_saddr);
            const char *desc = signal_ele->Attribute(attr_desc);
            int inf = 0;
            if(tinyxml2::XMLError::XML_SUCCESS != signal_ele->QueryIntAttribute(attr_inf, &inf))
            {
                MY_LOG_E("failed to parse attribute %s in element %s, inf get failed", attr_inf, local_parse.type.c_str());
                continue;
            }

            if (saddr != nullptr)
            {
                cfg_base.saddr = saddr;
            }
            if (desc != nullptr)
            {
                cfg_base.desc = desc;
            }
            cfg_base.inf = inf;

            local_parse.p_vec->push_back(cfg_base);
            signal_ele = signal_ele->NextSiblingElement(ele_Signal);
        }

        if(local_parse.p_vec->size() != count)
        {
            MY_LOG_E("count mismatch for element %s, expected %d, actual %d", 
                    local_parse.type.c_str(), count, (int)local_parse.p_vec->size());
            return -1;
        }
    }

    return 0;
}

int parse_ao(tinyxml2::XMLElement *root)
{
    tinyxml2::XMLElement *parent_ele = root->FirstChildElement(ele_Ao);
    if (parent_ele == nullptr)
    {
        MY_LOG_E("not found element %s", ele_Ao);
        return -1;
    }

    const char *count_str = parent_ele->Attribute(attr_count);
    int count = 0;
    if (count_str != nullptr)        
    {
        count = atoi(count_str);    
    }
    else
    {
        MY_LOG_E("not found attribute %s in element %s", attr_count, ele_Ao);
        return -1;
    }

    const char *md5_str = parent_ele->Attribute(attr_md5);
    if(nullptr == md5_str)
    {
        MY_LOG_E("not found attribute %s in element %s", attr_md5, ele_Ao);
        return -1;
    }
    else
    {
        if(strlen(md5_str) != 32)
        {
            MY_LOG_E("invalid md5 string %s in element %s", md5_str, ele_Ao);
            return -1;
        }
        for(int i = 0; i < 16; i++)
        {
            std::string byte_str = md5_str + i*2;
            byte_str[2] = '\0';
            g_self_ptl_cfg.ao_md5[i] = (uint8_t)strtoul(byte_str.c_str(), nullptr, 16);
        }
    }

    tinyxml2::XMLElement *signal_ele = parent_ele->FirstChildElement(ele_Signal);
    while(nullptr != signal_ele)
    {
        stru_self_ptl_cfg_param cfg_ao;
        const char *saddr = signal_ele->Attribute(attr_saddr);
        const char *desc = signal_ele->Attribute(attr_desc);
        int inf = 0;
        if(tinyxml2::XMLError::XML_SUCCESS != signal_ele->QueryIntAttribute(attr_inf, &inf))
        {
            MY_LOG_E("failed to parse attribute %s in element %s, inf get failed", attr_inf, ele_Ao);
            continue;
        }
        const char *type = signal_ele->Attribute(attr_type);
        // const char *dc_type = signal_ele->Attribute(attr_dc_type);
        const char *unit = signal_ele->Attribute(attr_unit);
        const char *min = signal_ele->Attribute(attr_min);
        const char *max = signal_ele->Attribute(attr_max);
        const char *step = signal_ele->Attribute(attr_step);
        const char *value = signal_ele->Attribute(attr_value);
        const char *default_value = signal_ele->Attribute(attr_default);

        cfg_ao.base.saddr = saddr == nullptr ? "" : saddr;
        cfg_ao.base.desc = desc == nullptr ? "" : desc;
        cfg_ao.base.inf = inf;
        cfg_ao.type = dc_get_data_type_id_by_str(type == nullptr ? "" : type);
        // cfg_ao.dc_type = dc_get_data_type_id_by_str(dc_type == nullptr ? "" : dc_type);
        cfg_ao.unit = unit == nullptr ? "" : unit;
        cfg_ao.min = min == nullptr ? "" : min;
        cfg_ao.max = max == nullptr ? "" : max;
        cfg_ao.step = step == nullptr ? "" : step;


        cfg_ao.val.push_back(value == nullptr ? "" : value);
        cfg_ao.default_value.push_back(default_value == nullptr ? "" : default_value);

        g_self_ptl_cfg.ao_vec.push_back(cfg_ao);

        signal_ele = signal_ele->NextSiblingElement(ele_Signal);
    }

    return 0;
}

int parse_param(tinyxml2::XMLElement *root)
{
    tinyxml2::XMLElement *parent_ele = root->FirstChildElement(ele_Param);
    if (parent_ele == nullptr)
    {
        MY_LOG_E("not found element %s", ele_Param);
        return -1;
    }

    const char *count_str = parent_ele->Attribute(attr_count);
    int count = 0;
    if (count_str != nullptr)        
    {
        count = atoi(count_str);    
    }
    else
    {
        MY_LOG_E("not found attribute %s in element %s", attr_count, ele_Param);
        return -1;
    }

    const char *md5_str = parent_ele->Attribute(attr_md5);
    if(nullptr == md5_str)
    {
        MY_LOG_E("not found attribute %s in element %s", attr_md5, ele_Param);
        return -1;
    }
    else
    {
        if(strlen(md5_str) != 32)
        {
            MY_LOG_E("invalid md5 string %s in element %s", md5_str, ele_Param);
            return -1;
        }
        for(int i = 0; i < 16; i++)
        {
            std::string byte_str = md5_str + i*2;
            byte_str[2] = '\0';
            g_self_ptl_cfg.param_md5[i] = (uint8_t)strtoul(byte_str.c_str(), nullptr, 16);
        }
    }

    tinyxml2::XMLElement *signal_ele = parent_ele->FirstChildElement(ele_Signal);
    while (signal_ele != nullptr)
    {
        stru_self_ptl_cfg_param cfg_param;
        const char *saddr = signal_ele->Attribute(attr_saddr);
        const char *desc = signal_ele->Attribute(attr_desc);
        int inf = 0;
        if(tinyxml2::XMLError::XML_SUCCESS != signal_ele->QueryIntAttribute(attr_inf, &inf))
        {
            MY_LOG_E("failed to parse attribute %s in element %s, inf get failed", attr_inf, ele_Param);
            continue;
        }
        const char *type = signal_ele->Attribute(attr_type);
        // const char *dc_type = signal_ele->Attribute(attr_dc_type);
        const char *unit = signal_ele->Attribute(attr_unit);
        const char *min = signal_ele->Attribute(attr_min);
        const char *max = signal_ele->Attribute(attr_max);
        const char *step = signal_ele->Attribute(attr_step);
        const char *num = signal_ele->Attribute(attr_num);

        cfg_param.base.saddr = saddr != nullptr ? saddr : "";
        cfg_param.base.desc = desc != nullptr ? desc : "";
        cfg_param.base.inf = inf;
        cfg_param.type = dc_get_data_type_id_by_str(type != nullptr ? type : "");
        // cfg_param.dc_type = dc_get_data_type_id_by_str(dc_type != nullptr ? dc_type : "");
        cfg_param.num = num != nullptr ? atoi(num) : 0;
        cfg_param.unit = unit != nullptr ? unit : "";   
        cfg_param.min = min != nullptr ? min : "";
        cfg_param.max = max != nullptr ? max : "";
        cfg_param.step = step != nullptr ? step : "";

        g_self_ptl_cfg.param_vec.push_back(cfg_param);

        tinyxml2::XMLElement *item_ele = signal_ele->FirstChildElement(ele_Item);
        while(item_ele != nullptr)
        {
            const char *val = item_ele->Attribute(attr_value);
            const char *default_val = item_ele->Attribute(attr_default);
            if(val != nullptr)
            {
                g_self_ptl_cfg.param_vec.back().val.push_back(val);
            }
            if(default_val != nullptr)
            {
                g_self_ptl_cfg.param_vec.back().default_value.push_back(default_val);
            }
            item_ele = item_ele->NextSiblingElement(ele_Item);
        }

        signal_ele = signal_ele->NextSiblingElement(ele_Signal);
    }

    if(g_self_ptl_cfg.param_vec.size() != count)
    {
        MY_LOG_E("count mismatch for element %s, expected %d, actual %d", 
                ele_Signal, count, (int)g_self_ptl_cfg.param_vec.size());
        return -1;
    }   
    
    return 0;
}

int self_ptl_cfg_parse(const std::string &path)
{
    if(g_self_ptl_cfg.init)
    {
        MY_LOG_I("self_ptl_cfg already parsed, skip");
        return 0;
    }

    // 解析XML文件
    tinyxml2::XMLDocument doc;
    if (doc.LoadFile(path.c_str()) != tinyxml2::XML_SUCCESS)
    {
        MY_LOG_E("Failed to load XML file: %s", path.c_str());
        return -1;
    }

    tinyxml2::XMLElement *root = doc.RootElement();
    if (root == nullptr)
    {
        MY_LOG_E("Failed to get root element");
        return -1;
    }

    // 解析st、mx、co、dd
    if(parse_base(root) != 0)
    {
        MY_LOG_E("Failed to parse base elements");
        return -1;
    }

    if(parse_ao(root) != 0)
    {
        MY_LOG_E("Failed to parse ao elements");
        return -1;
    }

    // 解析param
    if(parse_param(root) != 0)
    {
        MY_LOG_E("Failed to parse param elements");
        return -1;
    }

    g_self_ptl_cfg.init = true;

    return 0;
}
