/**
 * @file    myDatacenter.h
 * @brief   数据中心模块 — 纯 C 对外接口
 * @details 信号注册/控制/查询 + 事件队列 + 异步执行 + 参数配置。
 *          内部通过 libmy_xxhash 做 O(1) 哈希查找，
 *          参数配置通过 libxml C 接口解耦。
 *          对齐 RTU 原工程 libdatacenter v1 + v2 全部功能。
 */

#ifndef _MY_DATACENTER_H_
#define _MY_DATACENTER_H_

#include <stdint.h>
#include <stddef.h>
#include "xxhash.h"

#ifdef __cplusplus
extern "C"
{
#endif

/* ===== 地址/描述/模块/错误信息最大长度 ===== */
#define DC_SADDR_MAX_LEN   128
#define DC_DESC_MAX_LEN    256
#define DC_UNIT_MAX_LEN    32
#define DC_MODULE_MAX_LEN  64
#define DC_ERR_MSG_MAX_LEN 256

/* ===== 容量限制 ===== */
#define DC_MAX_CALLBACKS   16
#define DC_MAX_LINK_SADDRS 32
#define DC_MAX_DATA_ZONES  8
#define DC_MAX_PENDING     64

/** datacenter 自身模块标识 */
#define MODULE_DATACENTER  "datacenter"

/* ===== 控制类型枚举 ===== */
typedef enum
{
    DC_CTRL_NONE   = 0,   /**< 无控制 */
    DC_CTRL_DIRECT = 1,   /**< 直控（直接执行，无需选择-确认） */
    DC_CTRL_SBO    = 2    /**< 选控（Select-Before-Operate 两阶段） */
} dc_ctrl_type_t;

/* ===== 控制步骤枚举 ===== */
typedef enum
{
    DC_STEP_READY  = 0,   /**< 就绪/空闲 */
    DC_STEP_SELECT = 1,   /**< 选择（SBO 第一步：锁住信号并暂存选择值） */
    DC_STEP_DIRECT = 2,   /**< 执行（SBO 第二步确认写入，直控直接执行） */
    DC_STEP_CANCEL = 3    /**< 取消（回退到 READY） */
} dc_ctrl_step_t;

/* ===== 错误码枚举 ===== */
typedef enum
{
    DC_OK            =  0,   /**< 成功 */
    DC_ERR_PARAM     = -1,   /**< 参数错误（NULL/越界） */
    DC_ERR_NOTFOUND  = -2,   /**< 信号不存在 */
    DC_ERR_TYPE      = -3,   /**< 数据类型不匹配 */
    DC_ERR_STEP      = -4,   /**< SBO 步骤错误 */
    DC_ERR_VAL       = -5,   /**< 值校验失败（越界/未变化） */
    DC_ERR_TIMEOUT   = -6,   /**< 异步请求超时 */
    DC_ERR_BUSY      = -7,   /**< 忙 */
    DC_ERR_MEM       = -8,   /**< 内存不足 */
    DC_ERR_FULL      = -9,   /**< 请求队列已满 */
    DC_ERR_LOCKED    = -10,  /**< 已锁定 */
    DC_ERR_EXISTS    = -11,  /**< 信号已存在（重复注册） */
    DC_ERR_CANCEL    = -12   /**< 异步请求已取消 */
} dc_error_t;

/* ===== 控制上下文 ===== */
typedef struct
{
    uint8_t step;       /**< 当前控制步骤（dc_ctrl_step_t） */
    uint8_t type;       /**< 控制类型（dc_ctrl_type_t） */
    uint8_t data_type;  /**< 数据类型（DATA_TYPE_*） */
    uint8_t reserved;   /**< 保留 */
    void   *p_data;     /**< SELECT 时暂存选择值，DIRECT 时用于返校比对 */
} dc_ctrl_t;

/* ===== 信号参数元数据（min/max/step/unit） ===== */
typedef struct
{
    float min;          /**< 最小值 */
    float max;          /**< 最大值 */
    float step;         /**< 步长 */
    char  unit[DC_UNIT_MAX_LEN];  /**< 单位（如 "kV", "A", "Hz"） */
} dc_param_meta_t;

/* ===== 回调函数类型 ===== */

/**
 * @brief out 信号值变更回调（延迟触发，100ms 周期批量检测后调用）
 * @param saddr     信号地址
 * @param data_type 数据类型
 * @param p_new     新值指针
 * @param p_old     旧值指针
 */
typedef void (*dc_out_change_cb_t)(const char *saddr, uint8_t data_type,
                                    const void *p_new, const void *p_old);

/**
 * @brief yk/ao/param 信号控制变更回调（同步触发，控制场景即时响应）
 * @param saddr     信号地址
 * @param step      控制步骤
 * @param data_type 数据类型
 * @param zone      定值区号（param 信号使用，其他为 0）
 * @param p_data    数据指针
 */
typedef void (*dc_signal_change_cb_t)(const char *saddr, dc_ctrl_step_t step,
                                       uint8_t data_type, uint8_t zone,
                                       const void *p_data);

/** @brief 事件队列消费回调 */
typedef void (*dc_queue_pop_cb_t)(void *p_data);

/**
 * @brief 异步请求完成回调（通知发起方模块A）
 * @param req_id     请求 ID
 * @param result     结果码（DC_OK / DC_ERR_*）
 * @param err        错误信息（成功时为空）
 * @param arg        用户参数
 */
typedef void (*dc_async_result_cb_t)(uint32_t req_id, int result,
                                      const char *err, void *arg);

/**
 * @brief 异步执行回调（datacenter 调用模块B执行实际操作）
 * @param req_id     请求 ID
 * @param saddr      信号地址
 * @param step       控制步骤
 * @param data_type  数据类型
 * @param zone       定值区号
 * @param p_data     操作数据
 * @param ctx        模块上下文
 * @return 0 已接受（稍后通过 dc_set_result 返回结果），-1 拒绝
 */
typedef int  (*dc_async_exec_cb_t)(uint32_t req_id, const char *saddr,
                                    dc_ctrl_step_t step, uint8_t data_type,
                                    uint8_t zone, const void *p_data, void *ctx);

/* ===== 内部数据结构（对调用方透明，通过 API 访问） ===== */

/** 回调条目 */
typedef struct
{
    char                  module_id[DC_MODULE_MAX_LEN];
    dc_out_change_cb_t    out_cb;
    dc_signal_change_cb_t ctrl_cb;
} dc_cb_entry_t;

/** 信号节点 */
typedef struct
{
    uint32_t      id;
    XXH128_hash_t hash;
    char          saddr[DC_SADDR_MAX_LEN];
    char          desc[DC_DESC_MAX_LEN];
    uint8_t       data_type;
    uint8_t       ctrl_type;

    void        **vec_p_data;
    int           vec_p_data_count;
    void         *p_last_data;

    char          link_saddr[DC_SADDR_MAX_LEN];
    char        **link_saddrs;
    int           link_count;

    dc_param_meta_t param;

    dc_cb_entry_t   cb_list[DC_MAX_CALLBACKS];
    int             cb_count;

    char             last_caller_module[DC_MODULE_MAX_LEN];
    int              dirty_by_multi;       /**< >0 表示多模块同时修改, 禁用回路阻断 */

    dc_async_exec_cb_t exec_cb;
    void              *module_ctx;
} dc_signal_t;

/* ==================================================================
 * 模块生命周期
 * ================================================================== */

/**
 * @brief 初始化数据中心
 * @param cfg_dir 配置文件目录（用于查找 param.xml）
 * @return DC_OK 成功，<0 失败
 */
int  dc_init(const char *cfg_dir);

/** @brief 清理数据中心，释放所有信号和队列资源 */
void dc_cleanup(void);

/**
 * @brief 每 100ms 周期调用
 * @details out 信号变化检测 + 批量触发回调 + 事件队列弹出 + 异步超时检测
 */
void dc_run_100ms(void);

/**
 * @brief 每 1000ms 周期调用
 * @details 检测参数配置变更标志，有变更则写 self_param.xml
 */
void dc_run_1000ms(void);

/* ==================================================================
 * Out 信号注册（遥测/遥信输出）
 * ================================================================== */

/**
 * @brief 注册输出信号
 * @param saddr     信号地址（唯一标识）
 * @param desc      信号描述
 * @param data_type 数据类型（DATA_TYPE_*）
 * @param p_data    数据指针
 * @param module_id 模块标识（用于回路阻断）
 * @return DC_OK 成功，DC_ERR_EXISTS 重复注册
 */
int dc_signal_out(const char *saddr, const char *desc, uint8_t data_type,
                  void *p_data, const char *module_id);

/**
 * @brief 注册输出信号并绑定变化回调
 * @param cb 值变更回调（延迟触发，100ms 批量检测后调用）
 */
int dc_signal_out_with_callback(const char *saddr, const char *desc,
                                 uint8_t data_type, void *p_data,
                                 dc_out_change_cb_t cb, const char *module_id);

/**
 * @brief 链接已有 out 信号，获取数据指针并注册回调
 * @param p_data 输出：out 信号的数据指针
 */
int dc_signal_out_link_with_callback(const char *saddr, void **p_data,
                                      dc_out_change_cb_t cb, const char *module_id);

/**
 * @brief 设置输出信号值（标记脏，100ms 后触发变更检测和延迟回调）
 * @param module_id 调用方模块标识（自身不会被回调——回路阻断）
 */
int dc_set_out_signal_val(const char *saddr, const void *set_data,
                          const char *module_id);

/* ==================================================================
 * YK 信号（遥控）
 * ================================================================== */

/**
 * @brief 注册遥控信号
 * @param ctrl_type 控制类型（DC_CTRL_DIRECT 直控 / DC_CTRL_SBO 选控）
 * @param cb        控制变更回调（同步触发）
 */
int dc_signal_yk(const char *saddr, const char *desc,
                 uint8_t data_type, uint8_t ctrl_type, void *p_data,
                 dc_signal_change_cb_t cb, const char *module_id);

/** @brief 链接已有 YK 信号，获取数据指针并注册回调 */
int dc_signal_yk_link_with_callback(const char *saddr, void **p_data,
                                     dc_signal_change_cb_t cb, const char *module_id);

/**
 * @brief YK 同步设置（处理 SBO 流程：SELECT→DIRECT/CANCEL）
 * @param step  控制步骤
 * @param ctrl  控制上下文（进入时携带 type/data_type/p_data，执行完毕后回写当前 step）
 * @param p_data 操作数据
 */
int dc_signal_yk_set_status(const char *saddr, dc_ctrl_step_t step,
                             dc_ctrl_t *ctrl, const void *p_data,
                             const char *module_id);

/**
 * @brief YK 异步远程执行
 * @param result_cb  结果回调
 * @param timeout_ms 超时时间（毫秒），0 使用默认值
 * @param request_id 输出：请求 ID
 */
int dc_yk_exec_async(const char *saddr, dc_ctrl_step_t step,
                      const dc_ctrl_t *ctrl, const void *p_data,
                      const char *module_id,
                      dc_async_result_cb_t result_cb, void *user_arg,
                      uint32_t timeout_ms, uint32_t *request_id);

/** @brief 设置 YK 异步执行结果（模块B回调） */
int dc_yk_set_result(uint32_t request_id, int result_code, const char *err_msg);

/** @brief 取消 YK 异步请求 */
int dc_yk_cancel_async(uint32_t request_id);

/** @brief 注册 YK 异步执行回调（模块B初始化时调用） */
int dc_yk_register_exec_cb(const char *saddr, dc_async_exec_cb_t exec_cb,
                            void *module_ctx);

/* ==================================================================
 * AO 信号（模拟输出）
 * ================================================================== */

/**
 * @brief 注册 AO 信号
 * @param ctrl_type 控制类型
 * @param cb        控制变更回调（同步触发）
 */
int dc_signal_ao(const char *saddr, const char *desc,
                 uint8_t data_type, uint8_t ctrl_type, void *p_data,
                 dc_signal_change_cb_t cb, const char *module_id);

/** @brief 链接已有 AO 信号 */
int dc_signal_ao_link_with_callback(const char *saddr, void **p_data,
                                     dc_signal_change_cb_t cb, const char *module_id);

/**
 * @brief AO 同步设置（SBO 流程 + 值校验）
 * @details DIRECT 执行后 ctrl->step 自动复位到 READY
 */
int dc_signal_ao_set_val(const char *saddr, dc_ctrl_step_t step,
                         dc_ctrl_t *ctrl, const void *p_data,
                         const char *module_id);

/** @brief AO 无校验设置（跳过 SBO 流程和值校验，用于初始化恢复） */
int dc_signal_ao_set_val_without_check(const char *saddr, uint8_t data_type,
                                        const void *p_data, const char *module_id);

/** @brief AO 异步执行 */
int dc_ao_exec_async(const char *saddr, dc_ctrl_step_t step,
                      const dc_ctrl_t *ctrl, const void *p_data,
                      const char *module_id,
                      dc_async_result_cb_t result_cb, void *user_arg,
                      uint32_t timeout_ms, uint32_t *request_id);

/** @brief 设置 AO 异步执行结果 */
int dc_ao_set_result(uint32_t request_id, int result_code, const char *err_msg);

/** @brief 取消 AO 异步请求 */
int dc_ao_cancel_async(uint32_t request_id);

/** @brief 注册 AO 异步执行回调 */
int dc_ao_register_exec_cb(const char *saddr, dc_async_exec_cb_t exec_cb,
                            void *module_ctx);

/* ==================================================================
 * Param 信号（参数/定值，多定值区）
 * ================================================================== */

/**
 * @brief 注册参数信号
 * @param p_data    数据指针数组（每个 zone 一个指针）
 * @param num_zones 定值区数量
 */
int dc_signal_param(const char *saddr, const char *desc,
                    uint8_t data_type, uint8_t ctrl_type,
                    void **p_data, int num_zones,
                    dc_signal_change_cb_t cb, const char *module_id);

/** @brief 链接已有参数信号 */
int dc_signal_param_link_with_callback(const char *saddr, void **p_data,
                                        int num_zones,
                                        dc_signal_change_cb_t cb,
                                        const char *module_id);

/**
 * @brief Param 同步设置（SBO + zone 值校验）
 * @param setting_zone 定值区号（0-based）
 */
int dc_signal_param_set_val(const char *saddr, dc_ctrl_step_t step,
                             dc_ctrl_t *ctrl, uint8_t setting_zone,
                             const void *p_data, const char *module_id);

/** @brief Param 无校验设置 */
int dc_signal_param_set_val_without_check(const char *saddr, uint8_t data_type,
                                           uint8_t setting_zone,
                                           const void *p_data,
                                           const char *module_id);

/** @brief Param 异步执行 */
int dc_param_exec_async(const char *saddr, dc_ctrl_step_t step,
                         const dc_ctrl_t *ctrl, uint8_t setting_zone,
                         const void *p_data, const char *module_id,
                         dc_async_result_cb_t result_cb, void *user_arg,
                         uint32_t timeout_ms, uint32_t *request_id);

/** @brief 设置 Param 异步执行结果 */
int dc_param_set_result(uint32_t request_id, int result_code,
                         const char *err_msg);

/** @brief 取消 Param 异步请求 */
int dc_param_cancel_async(uint32_t request_id);

/** @brief 注册 Param 异步执行回调 */
int dc_param_register_exec_cb(const char *saddr, dc_async_exec_cb_t exec_cb,
                               void *module_ctx);

/* ==================================================================
 * 信号查询
 * ================================================================== */

/** @brief 查询 out 信号信息 */
int dc_get_out_signal_info(const char *saddr, char *desc, int desc_len,
                           uint8_t *data_type, void **p_data);

/** @brief 查询 AO 信号信息（含参数元数据） */
int dc_get_ao_signal_info(const char *saddr, char *desc, int desc_len,
                          uint8_t *data_type, dc_param_meta_t *p_meta,
                          uint8_t *ctrl_type, void **p_data);

/**
 * @brief 查询 Param 信号信息（含多区数据指针列表）
 * @param p_data_array 输出：数据指针数组（调用方提供缓冲区）
 * @param p_count      输出：定值区数量
 */
int dc_get_param_signal_info(const char *saddr, char *desc, int desc_len,
                              uint8_t *data_type, dc_param_meta_t *p_meta,
                              uint8_t *ctrl_type,
                              void **p_data_array, int *p_count);

/** @brief 查询 YK 信号信息 */
int dc_get_yk_signal_info(const char *saddr, char *desc, int desc_len,
                          uint8_t *data_type, uint8_t *ctrl_type, void **p_data);

/** @brief 获取信号值的字符串表示 */
int dc_get_signal_val(const void *p_data, uint8_t data_type,
                      char *buf, int buf_len);

/**
 * @brief 按类型统计信号数量
 * @param type_str 类型字符串："out"/"in"/"yk"/"ao"/"param"
 * @return 信号数量
 */
int dc_get_signal_count(const char *type_str);

/**
 * @brief 按类型和 ID 查询信号基本信息（供前端遍历）
 * @param type_str      "out"/"in"/"yk"/"ao"/"param"
 * @param id            信号 ID
 * @param saddr/saddr_len  输出：信号地址
 * @param desc/desc_len    输出：描述
 * @param data_type_str/type_str_len  输出：数据类型名称
 * @param ctrl_type     输出：控制类型
 * @param link_str/link_len  输出：链接信号的逗号分隔列表
 */
int dc_get_signal_info_by_id(const char *type_str, uint32_t id,
                              char *saddr, int saddr_len,
                              char *desc, int desc_len,
                              char *data_type_str, int type_str_len,
                              uint8_t *ctrl_type,
                              char *link_str, int link_len);

/* ==================================================================
 * 事件/扰动/故障队列
 * ================================================================== */

/**
 * @brief SOE 事件数据 (推入 dc_event_queue)
 * @field saddr  信号短地址
 * @field status 状态值
 * @field tm_sec 时间戳 (秒)
 * @field tm_ms  时间戳 (毫秒)
 * @field attr   属性 (扩展字段)
 */
typedef struct
{
	char     saddr[DC_SADDR_MAX_LEN];
	uint8_t  status;
	uint32_t tm_sec;
	uint16_t tm_ms;
	uint8_t  attr;
} dc_soe_event_t;

/**
 * @brief 扰动录波数据 (推入 dc_disturb_dd_queue)
 * @field saddr  信号短地址
 * @field f_val  扰动值
 * @field tm_sec 时间戳 (秒)
 * @field tm_ms  时间戳 (毫秒)
 */
typedef struct
{
	char     saddr[DC_SADDR_MAX_LEN];
	float    f_val;
	uint32_t tm_sec;
	uint16_t tm_ms;
} dc_disturb_event_t;

/**
 * @brief 故障事件数据 (推入 dc_fault_queue)
 * @field saddr  信号短地址
 * @field fault_id 故障编号
 * @field st_count 包含的遥信数量
 * @field mx_count 包含的遥测数量
 * @field tm_sec   时间戳 (秒)
 * @field tm_ms    时间戳 (毫秒)
 */
typedef struct
{
	char     saddr[DC_SADDR_MAX_LEN];
	uint32_t fault_id;
	uint8_t  st_count;
	uint8_t  mx_count;
	uint32_t tm_sec;
	uint16_t tm_ms;
} dc_fault_event_t;

/** @brief SOE 事件入队 */
int dc_event_queue_push(void *p_soe);

/** @brief 注册 SOE 事件消费回调（支持多消费者） */
int dc_event_register_queue_pop(dc_queue_pop_cb_t cb);

/** @brief 扰动数据入队 */
int dc_disturb_dd_queue_push(void *p_dd);

/** @brief 注册扰动数据消费回调 */
int dc_disturb_dd_register_queue_pop(dc_queue_pop_cb_t cb);

/** @brief 故障数据入队 */
int dc_fault_queue_push(void *p_fault);

/** @brief 注册故障数据消费回调 */
int dc_fault_register_queue_pop(dc_queue_pop_cb_t cb);

/* ==================================================================
 * 类型工具函数
 * ================================================================== */

/** @brief 数据类型 ID → 名称（如 35 → "uint32_t"） */
const char *dc_get_type_name(uint8_t data_type);

/** @brief 名称 → 数据类型 ID（如 "float" → 38） */
uint8_t     dc_get_type_id_by_name(const char *name);

/** @brief 获取数据类型的字节长度 */
uint8_t     dc_get_type_len(uint8_t data_type);

/** @brief 根据类型分配并清零内存 */
void       *dc_create_data(uint8_t data_type);

/** @brief 根据类型释放内存 */
void        dc_destroy_data(void *p_data, uint8_t data_type);

/** @brief 类型感知的值复制 */
int         dc_copy_val(void *dst, const void *src, uint8_t data_type);

/** @brief 类型感知的值比较（0 = 相等，-1 = 不等） */
int         dc_compare_val(const void *p1, const void *p2, uint8_t data_type);

/** @brief 字符串 → 值（如 "3.14" → 3.14f） */
int         dc_set_val_from_str(void *p_data, uint8_t data_type, const char *str);

/* ==================================================================
 * 参数配置与异步管理
 * ================================================================== */

/** @brief 查询参数配置是否有未保存的变更（0=无，1=有） */
int         dc_param_cfg_changed(void);

/** @brief 获取当前异步请求 pending 数量 */
int         dc_async_pending_count(void);

/** @brief 设置全局默认异步超时时间（毫秒） */
void        dc_async_set_default_timeout(uint32_t timeout_ms);

/** @brief 设置 YK 遥控默认异步超时（0=使用全局默认） */
void        dc_async_set_timeout_yk(uint32_t timeout_ms);

/** @brief 设置 AO 调节默认异步超时（0=使用全局默认） */
void        dc_async_set_timeout_ao(uint32_t timeout_ms);

/** @brief 设置 Param 定值默认异步超时（0=使用全局默认） */
void        dc_async_set_timeout_param(uint32_t timeout_ms);

/* ==================================================================
 * 导出
 * ================================================================== */

/**
 * @brief 保存所有已注册 out 信号到 XML
 * @param path 目标文件路径
 * @return DC_OK 成功，<0 失败
 */
int dc_save_out_signals_xml(const char *path);

#ifdef __cplusplus
}
#endif
#endif /* _MY_DATACENTER_H_ */
