/**
 * @file    iec104_cfg.cpp
 * @brief   IEC-104 点表 XML 配置解析 — tinyxml2 解析 iec1014.xml
 */

#include "iec104_cfg.h"
#include "myLog.h"
#include "myDatacenter.h"

#include <tinyxml2.h>
using namespace tinyxml2;

/* =========================== XML 元素 & 属性常量 =========================== */

LOCAL const char *xml_el_st      = "St";
LOCAL const char *xml_el_mx      = "Mx";
LOCAL const char *xml_el_co      = "Co";
LOCAL const char *xml_el_dd      = "Dd";
LOCAL const char *xml_el_ao      = "Ao";
LOCAL const char *xml_el_param   = "Param";
LOCAL const char *xml_el_signal  = "Signal";
LOCAL const char *xml_attr_desc  = "desc";
LOCAL const char *xml_attr_link  = "link";
LOCAL const char *xml_attr_inf   = "inf";
LOCAL const char *xml_attr_type  = "type";
LOCAL const char *xml_attr_invert = "invert";
LOCAL const char *xml_attr_factor = "factor";
LOCAL const char *xml_attr_offset = "offset";

/* =========================== 全局配置实例 =========================== */

static stru_iec_cfg g_cfg;

/**
 * @brief 解析规则 — 将 XML 标签映射到配置向量及默认数据类型
 */
typedef struct
{
	const char                 *xml_tag;
	std::vector<stru_iec_sig>  *p_vec;
	uint8_t                     default_type;
} st_parse_rule;

LOCAL st_parse_rule g_rules[] =
{
	{xml_el_st,    &g_cfg.vec_st,    DATA_TYPE_U8},
	{xml_el_mx,    &g_cfg.vec_mx,    DATA_TYPE_F32},
	{xml_el_co,    &g_cfg.vec_co,    DATA_TYPE_U8},
	{xml_el_dd,    &g_cfg.vec_dd,    DATA_TYPE_F32},
	{xml_el_ao,    &g_cfg.vec_ao,    0},
	{xml_el_param, &g_cfg.vec_param, 0},
};

#define RULE_COUNT (sizeof(g_rules) / sizeof(g_rules[0]))

/* =========================== XML 解析 =========================== */

/**
 * @brief 解析 XML 中各信号类型的 <Signal> 节点
 */
LOCAL int parse_signals(XMLElement *p_root)
{
	uint32_t rule_idx;

	for (rule_idx = 0; rule_idx < RULE_COUNT; rule_idx++)
	{
		XMLElement *p_parent = p_root->FirstChildElement(g_rules[rule_idx].xml_tag);

		if (NULL == p_parent)
		{
			continue;
		}

		XMLElement *p_sig = p_parent->FirstChildElement(xml_el_signal);

		while (NULL != p_sig)
		{
			stru_iec_sig entry         = {};
			const char  *p_attr_desc   = p_sig->Attribute(xml_attr_desc);
			const char  *p_attr_link   = p_sig->Attribute(xml_attr_link);
			const char  *p_attr_inf    = p_sig->Attribute(xml_attr_inf);
			const char  *p_attr_type   = p_sig->Attribute(xml_attr_type);
			const char  *p_attr_invert = p_sig->Attribute(xml_attr_invert);
			const char  *p_attr_factor = p_sig->Attribute(xml_attr_factor);
			const char  *p_attr_offset = p_sig->Attribute(xml_attr_offset);

			entry.desc   = (NULL != p_attr_desc)   ? p_attr_desc   : "";
			entry.link   = (NULL != p_attr_link)   ? p_attr_link   : "";
			entry.inf    = (NULL != p_attr_inf)    ? (uint16_t)atoi(p_attr_inf) : 0;
			entry.type   = (NULL != p_attr_type)   ? dc_get_type_id_by_name(p_attr_type) : 0;
			entry.invert = (NULL != p_attr_invert) ? (uint8_t)atoi(p_attr_invert) : 0;
			entry.factor = (NULL != p_attr_factor) ? (float)atof(p_attr_factor) : 0.0f;
			entry.offset = (NULL != p_attr_offset) ? (float)atof(p_attr_offset) : 0.0f;

			/* 如果 XML 未指定 type, 使用规则默认值 */
			if (0 == entry.type)
			{
				entry.type = g_rules[rule_idx].default_type;
			}

			g_rules[rule_idx].p_vec->push_back(entry);
			p_sig = p_sig->NextSiblingElement(xml_el_signal);
		}
	}

	return 0;
}

/* =========================== 对外 API =========================== */

stru_iec_cfg *iec_cfg_ptr_get(void)
{
	return &g_cfg;
}

int iec_cfg_parse(const char *xml_path)
{
	XMLDocument doc;
	XMLElement *p_root;

	if (NULL == xml_path)
	{
		LOG_E("iec_cfg_parse: NULL path");
		return -1;
	}

	if (XML_SUCCESS != doc.LoadFile(xml_path))
	{
		LOG_E("iec_cfg_parse: LoadFile failed for %s", xml_path);
		return -1;
	}

	p_root = doc.RootElement();

	if (NULL == p_root)
	{
		LOG_E("iec_cfg_parse: no root element");
		return -1;
	}

	if (0 != parse_signals(p_root))
	{
		return -1;
	}

	LOG_I("iec_cfg_parse: %s loaded — ST=%d MX=%d CO=%d DD=%d AO=%d Param=%d",
		xml_path, (int)g_cfg.vec_st.size(), (int)g_cfg.vec_mx.size(),
		(int)g_cfg.vec_co.size(), (int)g_cfg.vec_dd.size(),
		(int)g_cfg.vec_ao.size(), (int)g_cfg.vec_param.size());
	return 0;
}
