/**
 * @file    iec104_point.cpp
 * @brief   IEC-104 数据中心桥接 — 信号变更回调 → 环形缓冲区 + 事件队列消费
 */

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

#include <math.h>
#include <time.h>
#include <string.h>

#define IEC_MODULE_NAME "iec104"

/* 来自 iec104.cpp 的回调写入函数 */
extern int cb_soe_write(uint16_t idx, uint8_t status, uint32_t sec, uint16_t ms);
extern int cb_yc_disturb_write(uint16_t idx, float val);
extern int cb_dd_disturb_write(uint16_t idx, float val, uint32_t sec, uint16_t ms);
extern int cb_fault_write(iec_soe_info_t *p_soe, uint8_t yx_n, iec_yc_info_t *p_yc, uint8_t yc_n);

/* =========================== 信号变更回调 =========================== */

LOCAL void on_st_change(const char *p_saddr, uint8_t data_type, const void *p_val, const void *p_prev)
{
	std::vector<stru_iec_sig> &vec = iec_cfg_ptr_get()->vec_st;
	uint32_t                    i;
	uint8_t                     val;
	uint8_t                     last;

	(void)data_type;

	if (NULL == p_saddr || NULL == p_val || NULL == p_prev)
	{
		return;
	}

	std::string link(p_saddr);

	for (i = 0; i < vec.size(); i++)
	{
		if (vec[i].link != link)
		{
			continue;
		}

		val  = *(const uint8_t *)p_val;
		last = *(const uint8_t *)p_prev;

		if (val != last)
		{
			cb_soe_write(i, val, (uint32_t)time(NULL), 0);
			*(uint8_t *)const_cast<void *>(p_prev) = val;
		}
	}
}

LOCAL void on_mx_change(const char *p_saddr, uint8_t data_type, const void *p_val, const void *p_prev)
{
	std::vector<stru_iec_sig> &vec = iec_cfg_ptr_get()->vec_mx;
	uint32_t                    i;
	float                       val;
	float                       last;

	(void)data_type;

	if (NULL == p_saddr || NULL == p_val || NULL == p_prev)
	{
		return;
	}

	std::string link(p_saddr);

	for (i = 0; i < vec.size(); i++)
	{
		if (vec[i].link != link)
		{
			continue;
		}

		val  = *(const float *)p_val;
		last = *(const float *)p_prev;

		if (fabsf(val - last) > 0.1f)
		{
			cb_yc_disturb_write(i, val);
		}
	}
}

LOCAL void on_dd_change(const char *p_saddr, uint8_t data_type, const void *p_val, const void *p_prev)
{
	std::vector<stru_iec_sig> &vec = iec_cfg_ptr_get()->vec_dd;
	uint32_t                    i;
	float                       val;
	float                       last;

	(void)data_type;

	if (NULL == p_saddr || NULL == p_val || NULL == p_prev)
	{
		return;
	}

	std::string link(p_saddr);

	for (i = 0; i < vec.size(); i++)
	{
		if (vec[i].link != link)
		{
			continue;
		}

		val  = *(const float *)p_val;
		last = *(const float *)p_prev;

		if (fabsf(val - last) > 0.1f)
		{
			cb_dd_disturb_write(i, val, (uint32_t)time(NULL), 0);
			*(float *)const_cast<void *>(p_prev) = val;
		}
	}
}

LOCAL void on_yk_change(const char *p_saddr, dc_ctrl_step_t step, uint8_t data_type, uint8_t zone, const void *p_val)
{
	/* YK 由 CS10x SetYkInfor 回调直接写入 datacenter, 此处预留 */
	(void)p_saddr;
	(void)step;
	(void)data_type;
	(void)zone;
	(void)p_val;
}

LOCAL void on_ao_change(const char *p_saddr, dc_ctrl_step_t step, uint8_t data_type, uint8_t zone, const void *p_val)
{
	/* AO 由 CS10x 参数回调写入 datacenter, 此处预留 */
	(void)p_saddr;
	(void)step;
	(void)data_type;
	(void)zone;
	(void)p_val;
}

/* =========================== 数据中心事件队列消费 =========================== */

LOCAL void on_dc_soe(void *p_data)
{
	dc_soe_event_t           *p_event;
	std::vector<stru_iec_sig> &vec = iec_cfg_ptr_get()->vec_st;
	uint32_t                   i;

	if (NULL == p_data)
	{
		return;
	}

	p_event = (dc_soe_event_t *)p_data;
	std::string link(p_event->saddr);

	for (i = 0; i < vec.size(); i++)
	{
		if (vec[i].link == link)
		{
			cb_soe_write(i, p_event->status, p_event->tm_sec, p_event->tm_ms);
			return;
		}
	}
}

LOCAL void on_dc_fault(void *p_data)
{
	dc_fault_event_t         *p_event;
	std::vector<stru_iec_sig> &vec_st = iec_cfg_ptr_get()->vec_st;
	iec_soe_info_t            soe;
	uint32_t                   i;

	if (NULL == p_data)
	{
		return;
	}

	p_event = (dc_fault_event_t *)p_data;
	std::string link(p_event->saddr);

	/* 查找对应 YX 在 vec_st 中的索引 */
	memset(&soe, 0, sizeof(soe));

	for (i = 0; i < vec_st.size(); i++)
	{
		if (vec_st[i].link == link)
		{
			soe.info_addr = i;
			break;
		}
	}

	soe.status = 1;
	soe.sec    = p_event->tm_sec;
	soe.ms     = p_event->tm_ms;

	cb_fault_write(&soe, 1, NULL, 0);
}

LOCAL void on_dc_disturb_dd(void *p_data)
{
	dc_disturb_event_t       *p_event;
	std::vector<stru_iec_sig> &vec = iec_cfg_ptr_get()->vec_dd;
	uint32_t                   i;

	if (NULL == p_data)
	{
		return;
	}

	p_event = (dc_disturb_event_t *)p_data;
	std::string link(p_event->saddr);

	for (i = 0; i < vec.size(); i++)
	{
		if (vec[i].link == link)
		{
			cb_dd_disturb_write(i, p_event->f_val, p_event->tm_sec, p_event->tm_ms);
			return;
		}
	}
}

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

/**
 * @brief 初始化 IEC-104 数据中心桥接
 *
 * @details 完成三件事:
 *          1. 注册 datacenter 事件队列消费回调 (SOE/Fault/扰动DD)
 *          2. 为 XML 配置的 ST/MX/DD/CO/AO 信号注册变更回调
 *          3. Param 信号注册到 datacenter
 */
int iec104_point_init(void)
{
	stru_iec_cfg *p_cfg;
	void         *p_dummy;

	/* 1. 注册事件队列消费 */
	dc_event_register_queue_pop(on_dc_soe);
	dc_fault_register_queue_pop(on_dc_fault);
	dc_disturb_dd_register_queue_pop(on_dc_disturb_dd);

	/* 2. 获取配置 */
	p_cfg = iec_cfg_ptr_get();

	if (NULL == p_cfg)
	{
		LOG_E("iec104_point_init: cfg is NULL");
		return -1;
	}

	/* 3. 注册信号 link & 变更回调 */
	for (auto &sig : p_cfg->vec_st)
	{
		dc_signal_out_link_with_callback(sig.link.c_str(), &sig.p_data, on_st_change, IEC_MODULE_NAME);
	}

	for (auto &sig : p_cfg->vec_mx)
	{
		dc_signal_out_link_with_callback(sig.link.c_str(), &sig.p_data, on_mx_change, IEC_MODULE_NAME);
	}

	for (auto &sig : p_cfg->vec_dd)
	{
		dc_signal_out_link_with_callback(sig.link.c_str(), &sig.p_data, on_dd_change, IEC_MODULE_NAME);
	}

	for (auto &sig : p_cfg->vec_co)
	{
		dc_signal_yk_link_with_callback(sig.link.c_str(), &sig.p_data, on_yk_change, IEC_MODULE_NAME);
	}

	for (auto &sig : p_cfg->vec_ao)
	{
		dc_signal_ao_link_with_callback(sig.link.c_str(), &sig.p_data, on_ao_change, IEC_MODULE_NAME);
	}

	/* Param: 无变更回调, 仅注册信号 */
	p_dummy = NULL;

	for (auto &sig : p_cfg->vec_param)
	{
		dc_signal_param(sig.link.c_str(), sig.desc.c_str(), sig.type, DC_CTRL_SBO, &p_dummy, 1, NULL, IEC_MODULE_NAME);
	}

	LOG_I("iec104_point_init: ST=%d MX=%d CO=%d DD=%d AO=%d Param=%d",
		(int)p_cfg->vec_st.size(), (int)p_cfg->vec_mx.size(), (int)p_cfg->vec_co.size(),
		(int)p_cfg->vec_dd.size(), (int)p_cfg->vec_ao.size(), (int)p_cfg->vec_param.size());
	return 0;
}
