/** @file dc_param_cfg.cpp — 参数配置解析与保存（直接调用 tinyxml2 C++ API） */
#include "dc_internal.h"
#include "tinyxml2.h"
#include <unordered_map>

/**
 * @brief 存储信号参数元数据到全局表
 * @param saddr 信号地址
 * @param param 参数元数据（min/max/step/unit）
 */
void dc_param_metadata_store(const char *saddr, const dc_param_meta_t &param)
{
    g_param_metadata[saddr] = param;
}

/**
 * @brief 查找信号参数元数据
 * @param saddr 信号地址
 * @param out_param 输出参数元数据
 * @return true 找到，false 未找到
 */
bool dc_param_metadata_lookup(const char *saddr, dc_param_meta_t &out_param)
{
    auto it = g_param_metadata.find(saddr);

    if (it != g_param_metadata.end())
    {
        out_param = it->second;

        return true;
    }

    return false;
}

/**
 * @brief 解析 param.xml 中的 Ao/Param 元数据（min/max/step/unit）
 * @param path param.xml 文件路径
 * @return 0 成功，-1 失败
 */
static int dc_param_parse_metadata(const char *path)
{
    tinyxml2::XMLDocument doc;

    if (doc.LoadFile(path) != tinyxml2::XML_SUCCESS)
    {
        LOG_E("load %s failed\n", path);

        return -1;
    }

    tinyxml2::XMLElement *root = doc.RootElement();

    if (!root)
    {
        return -1;
    }

    const char *sections[] = {"Ao", "Param"};

    for (int si = 0; si < 2; si++)
    {
        tinyxml2::XMLElement *elem = root->FirstChildElement(sections[si]);

        for (tinyxml2::XMLElement *sig = (elem ? elem->FirstChildElement("Signal") : NULL);
             sig;
             sig = sig->NextSiblingElement("Signal"))
        {
            const char *saddr = sig->Attribute("saddr");

            if (!saddr)
            {
                continue;
            }

            dc_param_meta_t m;

            m.min = sig->FloatAttribute("min", 0.0f);
            m.max = sig->FloatAttribute("max", 0.0f);
            m.step = sig->FloatAttribute("step", 0.0f);
            const char *u = sig->Attribute("unit");

            strncpy(m.unit, u ? u : "", DC_UNIT_MAX_LEN - 1);
            g_param_metadata[saddr] = m;
        }
    }

    return 0;
}

/**
 * @brief 加载 self_param.xml 已保存的值
 * @param path self_param.xml 文件路径
 * @return saddr→value 映射表
 */
static std::unordered_map<std::string, std::string> dc_self_param_load(const char *path)
{
    std::unordered_map<std::string, std::string> vals;
    tinyxml2::XMLDocument doc;

    if (doc.LoadFile(path) != tinyxml2::XML_SUCCESS)
    {
        return vals;
    }

    tinyxml2::XMLElement *root = doc.RootElement();

    if (!root)
    {
        return vals;
    }

    const char *sections[] = {"Ao", "Param"};

    for (int si = 0; si < 2; si++)
    {
        tinyxml2::XMLElement *elem = root->FirstChildElement(sections[si]);

        for (tinyxml2::XMLElement *sig = (elem ? elem->FirstChildElement("Signal") : NULL);
             sig;
             sig = sig->NextSiblingElement("Signal"))
        {
            const char *sa = sig->Attribute("saddr");

            if (!sa)
            {
                continue;
            }

            if (si == 0)
            {
                const char *v = sig->Attribute("value");

                if (v)
                {
                    vals[sa] = v;
                }
            }
            else
            {
                for (tinyxml2::XMLElement *item = sig->FirstChildElement("Item");
                     item;
                     item = item->NextSiblingElement("Item"))
                {
                    int idx = item->IntAttribute("index", -1);
                    const char *v = item->Attribute("value");

                    if (idx >= 1 && v)
                    {
                        vals[std::string(sa) + "_" + std::to_string(idx)] = v;
                    }
                }
            }
        }
    }

    return vals;
}

/**
 * @brief 解析 param.xml + self_param.xml，注册 Ao 和 Param 信号
 * @param path param.xml 文件路径
 * @return 0 成功，-1 失败
 */
int dc_param_cfg_parse(const char *path)
{
    if (0 != dc_param_parse_metadata(path))
    {
        LOG_E("parse metadata failed\n");

        return -1;
    }

    std::string self_path = path;
    size_t pos = self_path.rfind("param.xml");

    if (pos != std::string::npos)
    {
        self_path.replace(pos, 9, "self_param.xml");
    }

    auto self_vals = dc_self_param_load(self_path.c_str());

    LOG_I("loaded %zu saved values from self_param.xml\n", self_vals.size());

    /* 注册 Ao 和 Param 信号 */
    tinyxml2::XMLDocument doc;

    if (doc.LoadFile(path) != tinyxml2::XML_SUCCESS)
    {
        return -1;
    }

    tinyxml2::XMLElement *root = doc.RootElement();

    if (!root)
    {
        return -1;
    }

    /* Ao 段 */
    tinyxml2::XMLElement *ao_elem = root->FirstChildElement("Ao");

    if (ao_elem)
    {
        for (tinyxml2::XMLElement *sig = ao_elem->FirstChildElement("Signal");
             sig;
             sig = sig->NextSiblingElement("Signal"))
        {
            const char *sa = sig->Attribute("saddr");
            const char *de = sig->Attribute("desc");
            const char *ty = sig->Attribute("type");

            if (!sa || !de || !ty)
            {
                continue;
            }

            uint8_t dt = dc_util_type_id_by_name(ty);

            if (!dt)
            {
                continue;
            }

            auto it = self_vals.find(sa);
            std::string rv = (it != self_vals.end()) ? it->second : "";

            if (rv.empty())
            {
                const char *va = sig->Attribute("value");

                if (va)
                {
                    rv = va;
                }
            }

            void *pd = dc_util_create_data(dt);

            if (pd && !rv.empty())
            {
                dc_util_val_from_string(pd, dt, rv);
            }

            if (pd)
            {
                dc_signal_ao(sa, de, dt, DC_CTRL_SBO, pd, NULL, MODULE_DATACENTER);
            }
        }
    }

    /* Param 段 */
    tinyxml2::XMLElement *param_elem = root->FirstChildElement("Param");

    if (param_elem)
    {
        for (tinyxml2::XMLElement *sig = param_elem->FirstChildElement("Signal");
             sig;
             sig = sig->NextSiblingElement("Signal"))
        {
            const char *sa = sig->Attribute("saddr");
            const char *de = sig->Attribute("desc");
            const char *ty = sig->Attribute("type");

            if (!sa || !de || !ty)
            {
                continue;
            }

            uint8_t dt = dc_util_type_id_by_name(ty);

            if (!dt)
            {
                continue;
            }

            int num = sig->IntAttribute("num", -1);

            if (num < 1)
            {
                continue;
            }

            std::vector<void *> vec;

            for (tinyxml2::XMLElement *item = sig->FirstChildElement("Item");
                 item;
                 item = item->NextSiblingElement("Item"))
            {
                int idx = item->IntAttribute("index", -1);
                std::string key = std::string(sa) + "_" + std::to_string(idx);
                auto it = self_vals.find(key);
                std::string rv = (it != self_vals.end()) ? it->second : "";
                const char *va = item->Attribute("value");

                if (rv.empty() && va)
                {
                    rv = va;
                }

                void *pd = dc_util_create_data(dt);

                if (pd && !rv.empty())
                {
                    dc_util_val_from_string(pd, dt, rv);
                }

                if (pd)
                {
                    vec.push_back(pd);
                }
            }

            if (!vec.empty())
            {
                dc_signal_param(sa, de, dt, DC_CTRL_SBO, vec.data(), num, NULL,
                                MODULE_DATACENTER);
            }
        }
    }

    g_param_cfg_changed = true;

    return 0;
}

/**
 * @brief 检测参数配置变更标志，有变更则写 self_param.xml
 * @param path 参数配置文件目录（用于构造 self_param.xml 路径）
 */
void dc_param_cfg_check(const char *path)
{
    if (!g_param_cfg_changed || !path || !path[0])
    {
        return;
    }

    tinyxml2::XMLDocument doc;

    doc.InsertEndChild(doc.NewDeclaration());

    tinyxml2::XMLElement *root = doc.NewElement("Root");

    doc.InsertEndChild(root);

    /* Ao 段 */
    tinyxml2::XMLElement *ao_elem = doc.NewElement("Ao");

    for (uint32_t i = 0; i < g_dc_ao.signal_id; i++)
    {
        dc_signal_t *ps = dc_core_find_by_id(i, g_dc_ao);

        if (!ps || !ps->vec_p_data || !ps->vec_p_data[0])
        {
            continue;
        }

        std::string v = dc_util_val_to_string(ps->vec_p_data[0], ps->data_type);
        tinyxml2::XMLElement *sig = doc.NewElement("Signal");

        sig->SetAttribute("saddr", ps->saddr);
        sig->SetAttribute("value", v.c_str());
        ao_elem->InsertEndChild(sig);
    }

    root->InsertEndChild(ao_elem);

    /* Param 段 */
    tinyxml2::XMLElement *param_elem = doc.NewElement("Param");

    for (uint32_t i = 0; i < g_dc_param.signal_id; i++)
    {
        dc_signal_t *ps = dc_core_find_by_id(i, g_dc_param);

        if (!ps || !ps->vec_p_data)
        {
            continue;
        }

        tinyxml2::XMLElement *sig = doc.NewElement("Signal");

        sig->SetAttribute("saddr", ps->saddr);

        for (int z = 0; z < ps->vec_p_data_count; z++)
        {
            if (!ps->vec_p_data[z])
            {
                continue;
            }

            std::string v = dc_util_val_to_string(ps->vec_p_data[z], ps->data_type);
            tinyxml2::XMLElement *item = doc.NewElement("Item");

            item->SetAttribute("index", z + 1);
            item->SetAttribute("value", v.c_str());
            sig->InsertEndChild(item);
        }

        param_elem->InsertEndChild(sig);
    }

    root->InsertEndChild(param_elem);

    std::string self_path = path;
    size_t pos = self_path.rfind("param.xml");

    if (pos != std::string::npos)
    {
        self_path.replace(pos, 9, "self_param.xml");
    }

    if (doc.SaveFile(self_path.c_str()) == tinyxml2::XML_SUCCESS)
    {
        LOG_I("param saved to %s\n", self_path.c_str());
    }
    else
    {
        LOG_E("failed to save param to %s\n", self_path.c_str());
    }

    g_param_cfg_changed = false;
}
