/**
 * @file    self_ptl_cfg.cpp
 * @brief   自协议 XML 点表配置解析 — 解析 self_ptl.xml 中的 St/Mx/Co/Dd/Ao/Param 信号
 */

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

using namespace tinyxml2;
#include "self_ptl_cfg.h"

#include <string>
#include <vector>
#include <cstdlib>
#include <cstring>

__attribute__((unused)) static const char *ele_Root   = "Root";

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

static const char *ele_Signal = "Signal";

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

static const char *attr_saddr    = "saddr";
static const char *attr_desc     = "desc";
static const char *attr_inf      = "inf";
static const char *attr_type     = "type";
static const char *attr_unit     = "unit";
static const char *attr_min      = "min";
static const char *attr_max      = "max";
static const char *attr_step     = "step";
static const char *attr_value    = "value";
static const char *attr_default  = "default";

static stru_self_ptl_cfg g_self_ptl_cfg = {false};

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

static 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(void)
{
	if(!g_self_ptl_cfg.init)
	{
		LOG_E("self_ptl_cfg not parsed yet");
		return nullptr;
	}

	return &g_self_ptl_cfg;
}

/* ---- 解析 St/Mx/Co/Dd (base 类型) ---- */

static int parse_base(XMLElement *root)
{
	if(nullptr == root)
	{
		LOG_E("parse_base: root is null");
		return -1;
	}

	for(const auto &local_parse : g_local_parse_vec)
	{
		XMLElement *parent_ele = root->FirstChildElement(local_parse.type.c_str());

		if(nullptr == parent_ele)
		{
			LOG_E("parse_base: not found element %s", local_parse.type.c_str());
			continue;
		}

		const char *count_str = parent_ele->Attribute(attr_count);
		int count = 0;
		(void)count;

		if(nullptr != count_str)
		{
			count = atoi(count_str);
		}
		else
		{
			LOG_E("parse_base: no count attr in %s", local_parse.type.c_str());
			continue;
		}

		XMLElement *signal_ele = parent_ele->FirstChildElement(ele_Signal);

		while(nullptr != signal_ele)
		{
			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(XML_SUCCESS != signal_ele->QueryIntAttribute(attr_inf, &inf))
			{
				LOG_E("parse_base: inf parse failed in %s", local_parse.type.c_str());
				signal_ele = signal_ele->NextSiblingElement(ele_Signal);
				continue;
			}

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

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

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

	return 0;
}

/* ---- 解析 Ao (参数信号, 含 meta) ---- */

static int parse_ao(XMLElement *root)
{
	XMLElement *parent_ele = root->FirstChildElement(ele_Ao);

	if(nullptr == parent_ele)
	{
		LOG_E("parse_ao: not found element %s", ele_Ao);
		return -1;
	}

	const char *count_str = parent_ele->Attribute(attr_count);
	int count = 0;

	if(nullptr != count_str)
	{
		count = atoi(count_str);
	}
	else
	{
		LOG_E("parse_ao: no count attr");
		return -1;
	}

	const char *md5_str = parent_ele->Attribute(attr_md5);

	if(nullptr == md5_str)
	{
		LOG_E("parse_ao: no md5 attr");
		return -1;
	}
	else
	{
		if(strlen(md5_str) != 32)
		{
			LOG_E("parse_ao: invalid md5 length");
			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);
		}
	}

	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(XML_SUCCESS != signal_ele->QueryIntAttribute(attr_inf, &inf))
		{
			LOG_E("parse_ao: inf parse failed");
			signal_ele = signal_ele->NextSiblingElement(ele_Signal);
			continue;
		}

		const char *type   = signal_ele->Attribute(attr_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 *dflt   = signal_ele->Attribute(attr_default);

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

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

		g_self_ptl_cfg.ao_vec.push_back(cfg_ao);

		signal_ele = signal_ele->NextSiblingElement(ele_Signal);
	}

	return 0;
}

/* ---- 解析 Param (定值参数, 复用 Ao 的结构) ---- */

static int parse_param(XMLElement *root)
{
	XMLElement *parent_ele = root->FirstChildElement(ele_Param);

	if(nullptr == parent_ele)
	{
		LOG_E("parse_param: not found element %s", ele_Param);
		return -1;
	}

	const char *count_str = parent_ele->Attribute(attr_count);
	int count = 0;

	if(nullptr != count_str)
	{
		count = atoi(count_str);
	}
	else
	{
		LOG_E("parse_param: no count attr");
		return -1;
	}

	/* Param count 可以为 0 (当前配置即为 0) */
	if(0 == count)
	{
		return 0;
	}

	XMLElement *signal_ele = parent_ele->FirstChildElement(ele_Signal);

	while(nullptr != signal_ele)
	{
		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(XML_SUCCESS != signal_ele->QueryIntAttribute(attr_inf, &inf))
		{
			LOG_E("parse_param: inf parse failed");
			signal_ele = signal_ele->NextSiblingElement(ele_Signal);
			continue;
		}

		const char *type   = signal_ele->Attribute(attr_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 *dflt   = signal_ele->Attribute(attr_default);

		cfg_param.base.saddr = (nullptr == saddr) ? "" : saddr;
		cfg_param.base.desc  = (nullptr == desc)  ? "" : desc;
		cfg_param.base.inf   = inf;
		cfg_param.type       = dc_get_type_id_by_name((nullptr == type) ? "" : type);
		cfg_param.unit       = (nullptr == unit)  ? "" : unit;
		cfg_param.min        = (nullptr == min)   ? "" : min;
		cfg_param.max        = (nullptr == max)   ? "" : max;
		cfg_param.step       = (nullptr == step)  ? "" : step;

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

		g_self_ptl_cfg.param_vec.push_back(cfg_param);

		signal_ele = signal_ele->NextSiblingElement(ele_Signal);
	}

	return 0;
}

/* ---- 对外入口 ---- */

int self_ptl_cfg_init(const char *xml_path)
{
	if(nullptr == xml_path)
	{
		LOG_E("self_ptl_cfg_init: xml_path is NULL");
		return -1;
	}

	XMLDocument doc;

	if(XML_SUCCESS != doc.LoadFile(xml_path))
	{
		LOG_E("self_ptl_cfg_init: failed to load XML file %s", xml_path);
		return -1;
	}

	XMLElement *root = doc.RootElement();

	if(nullptr == root)
	{
		LOG_E("self_ptl_cfg_init: no root element");
		return -1;
	}

	if(0 != parse_base(root))
	{
		LOG_E("self_ptl_cfg_init: parse_base failed");
		return -1;
	}

	if(0 != parse_ao(root))
	{
		LOG_E("self_ptl_cfg_init: parse_ao failed");
		return -1;
	}

	if(0 != parse_param(root))
	{
		LOG_E("self_ptl_cfg_init: parse_param failed");
		return -1;
	}

	g_self_ptl_cfg.init = true;

	LOG_I("self_ptl_cfg_init: St=%d, Mx=%d, Co=%d, Dd=%d, Ao=%d, Param=%d",
		(int)g_self_ptl_cfg.st_vec.size(),
		(int)g_self_ptl_cfg.mx_vec.size(),
		(int)g_self_ptl_cfg.co_vec.size(),
		(int)g_self_ptl_cfg.dd_vec.size(),
		(int)g_self_ptl_cfg.ao_vec.size(),
		(int)g_self_ptl_cfg.param_vec.size());

	return 0;
}
