/**
 * @file    self_ptl_cb.cpp
 * @brief   自协议回调实现 — ICP67 decode 输出 → 数据中心 + 文件操作
 */

#include "myBase.h"
#include "myLog.h"
#include "myFunc.h"
#include "myMd5.h"
#include "myIcp67.h"
#include "myDatacenter.h"
#include "self_ptl.h"

#include <string>
#include <vector>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include <pthread.h>

/* =========================== 文件操作辅助 =========================== */

static std::string g_file_base_path;
static std::string g_file_path;
static std::string g_zip_path;
static int g_paths_ready = 0;

/* 文件操作全局状态互斥锁 (防止多 FTU 并发传输竞态) */
static pthread_mutex_t g_file_mutex = PTHREAD_MUTEX_INITIALIZER;

static void file_paths_init(void)
{
	char proc_dir[512] = {0};

	if(g_paths_ready) return;

	if(0 != func_proc_self_dir(proc_dir, sizeof(proc_dir)))
	{
		LOG_E("file_paths_init: func_proc_self_dir failed");
		return;
	}

	g_file_base_path = std::string(proc_dir);
	g_file_path      = g_file_base_path + "/file/";
	g_zip_path       = g_file_base_path + "/zip/";

	if(0 != func_make_dirs(g_file_path.c_str()))
	{
		LOG_E("file_paths_init: func_make_dirs(%s) failed", g_file_path.c_str());
		return;
	}
	if(0 != func_make_dirs(g_zip_path.c_str()))
	{
		LOG_E("file_paths_init: func_make_dirs(%s) failed", g_zip_path.c_str());
		return;
	}

	g_paths_ready = 1;
	LOG_I("file_paths_init: file=%s, zip=%s", g_file_path.c_str(), g_zip_path.c_str());
}

static FILE *file_open(const char *file_name, const char *mode)
{
	if(NULL == file_name || NULL == mode) return NULL;
	return fopen(file_name, mode);
}

static int file_close(FILE *fp)
{
	if(NULL == fp) return -1;
	return fclose(fp);
}

static int file_write(const char *file_name, uint32_t offset, uint8_t *p_data, uint16_t len)
{
	FILE *fp;

	if(NULL == file_name || NULL == p_data) return -1;

	fp = file_open(file_name, "ab");
	if(NULL == fp) return -1;

	if(0 != fseek(fp, offset, SEEK_SET))
	{
		LOG_E("file_write: seek failed");
		file_close(fp);
		return -1;
	}

	if(len != fwrite(p_data, 1, len, fp))
	{
		LOG_E("file_write: write failed");
		file_close(fp);
		return -1;
	}

	file_close(fp);
	return 0;
}

static int file_read(const char *file_name, uint32_t offset, uint8_t *p_data, uint16_t len)
{
	FILE *fp;

	if(NULL == file_name || NULL == p_data) return -1;

	fp = file_open(file_name, "rb");
	if(NULL == fp) return -1;

	if(0 != fseek(fp, offset, SEEK_SET))
	{
		LOG_E("file_read: seek failed");
		file_close(fp);
		return -1;
	}

	if(len != fread(p_data, 1, len, fp))
	{
		LOG_E("file_read: read failed");
		file_close(fp);
		return -1;
	}

	file_close(fp);
	return 0;
}

static int file_size_get(const char *file_name, uint32_t *p_size)
{
	FILE *fp;

	if(NULL == file_name || NULL == p_size) return -1;

	fp = file_open(file_name, "rb");
	if(NULL == fp) return -1;

	if(0 != fseek(fp, 0, SEEK_END))
	{
		LOG_E("file_size_get: seek failed");
		file_close(fp);
		return -1;
	}

	*p_size = (uint32_t)ftell(fp);
	file_close(fp);

	return 0;
}

/* =========================== 文件操作状态 =========================== */

static std::string              g_dir_name     = "";
static std::string              g_file_name    = "";
static std::string              g_zip_name     = "";
static std::vector<stru_dir_info> g_dir_info_vec;
static stru_md5_ctx              g_file_md5_ctx;
static int                       g_md5_started  = 0;

/* =========================== 回调: 内部参数读取出 =========================== */

void ao_pop_out(stru_icp67 *p_icp67, uint16_t addr, uint8_t type, uint8_t len, uint8_t *p_data)
{
	(void)p_icp67;

	{
		char saddr[64];
		snprintf(saddr, sizeof(saddr), "self_ptl.ao.%d", addr);
		dc_set_out_signal_val(saddr, p_data, MODULE_SELF_PTL);
	}

	LOG_I("ao_pop_out: addr=%d, type=%d, len=%d", addr, type, len);
}

/* ---- 回调: IEC 点表读取出 ---- */

void iec_point_tbl_pop_out(stru_icp67 *p_icp67, uint16_t info_addr, uint16_t num, uint8_t *p_data)
{
	(void)p_icp67;
	(void)p_data;
	LOG_I("iec_point_tbl_pop_out: info_addr=%d, num=%d", info_addr, num);
}

/* ---- 回调: 自检读取出 ---- */

void self_check_pop_out(stru_icp67 *p_icp67, stru_data_info *p_data, uint16_t addr)
{
	(void)p_icp67;
	if(NULL == p_data) return;

	{
		char saddr[64];
		snprintf(saddr, sizeof(saddr), "self_ptl.st.%d", addr);
		dc_set_out_signal_val(saddr, p_data->data, MODULE_SELF_PTL);
	}
}

/* =========================== 文件操作回调 =========================== */

/* ---- 目录读取出 ---- */

void dir_pop_out(stru_icp67 *p_icp67, uint8_t *p_name, uint8_t len,
	uint32_t attr, uint32_t size, uint8_t follow)
{
	stru_dir_info dir_info;

	if(NULL == p_icp67 || NULL == p_name) return;

	pthread_mutex_lock(&g_file_mutex);

	memcpy(dir_info.name, p_name, len);
	dir_info.name[len] = '\0';
	dir_info.len  = len;
	dir_info.attr = attr;
	dir_info.size = size;
	g_dir_info_vec.push_back(dir_info);

	if(0 == follow)
	{
		LOG_I("dir_pop_out: %d files in %s", (int)g_dir_info_vec.size(), g_dir_name.c_str());
		for(uint16_t i = 0; i < g_dir_info_vec.size(); i++)
		{
			LOG_I("  %s (size=%d)", g_dir_info_vec[i].name, g_dir_info_vec[i].size);
		}
		g_dir_info_vec.clear();
	}

	pthread_mutex_unlock(&g_file_mutex);
}

/* ---- 读文件激活确认 ---- */

void file_read_act_confirm(stru_icp67 *p_icp67, uint8_t *p_name, uint8_t len,
	uint32_t id, uint32_t size, uint16_t crc)
{
	if(NULL == p_icp67 || NULL == p_name) return;

	file_paths_init();

	pthread_mutex_lock(&g_file_mutex);
	g_file_name = std::string((char *)p_name, len);

	LOG_I("file_read_act_confirm: name=%s, id=%d, size=%d, crc=0x%04X",
		g_file_name.c_str(), id, size, crc);

	/* 删除旧文件, 准备接收新数据 */
	{
		std::string fpath = g_file_path + g_dir_name + "/" + g_file_name;
		func_make_dirs((g_file_path + g_dir_name).c_str());
		unlink(fpath.c_str());
	}
	pthread_mutex_unlock(&g_file_mutex);
}

/* ---- 读文件内容 (FTU → RTU 本地写入) ---- */

void file_read(stru_icp67 *p_icp67, uint32_t id, uint32_t offset,
	uint32_t follow, uint8_t *p_data, uint16_t len)
{
	if(NULL == p_icp67 || NULL == p_data) return;

	pthread_mutex_lock(&g_file_mutex);

	if(0 == offset)
	{
		LOG_I("file_read: %s start", g_file_name.c_str());
	}

	{
		std::string fpath = g_file_path + g_dir_name + "/" + g_file_name;
		file_write(fpath.c_str(), offset, p_data, len);
	}

	if(0 == follow)
	{
		LOG_I("file_read: %s complete", g_file_name.c_str());
	}

	/* 确认接收 */
	if(p_icp67->method.file_read_confirm_cb)
	{
		p_icp67->method.file_read_confirm_cb(p_icp67, 0, id, offset + len);
	}

	pthread_mutex_unlock(&g_file_mutex);
}

/* ---- 写文件激活确认 (RTU → FTU 发送文件) ---- */

void file_write_act_confirm(stru_icp67 *p_icp67, uint8_t result,
	uint8_t *p_name, uint8_t len, uint32_t id, uint32_t size)
{
	if(NULL == p_icp67 || NULL == p_name) return;

	if(FILE_WRITE_ACT_0 != result)
	{
		LOG_E("file_write_act_confirm: result=%d, abort", result);
		return;
	}

	file_paths_init();

	pthread_mutex_lock(&g_file_mutex);
	g_file_name = std::string((char *)p_name, len);

	{
		std::string fpath = g_file_path + g_dir_name + "/" + g_file_name;
		uint8_t follow   = 0;
		uint16_t frame_len = 0;

		if(size > FILE_TRANS_FRMAE_LEN)
		{
			follow    = 1;
			frame_len = FILE_TRANS_FRMAE_LEN;
		}
		else
		{
			frame_len = (uint16_t)size;
		}

		uint8_t file_data[FILE_TRANS_FRMAE_LEN] = {0};
		file_read(fpath.c_str(), 0, file_data, frame_len);

		if(p_icp67->method.file_write_cb)
		{
			p_icp67->method.file_write_cb(p_icp67, id, 0, follow, file_data, frame_len);
		}

		md5_start(&g_file_md5_ctx);
		md5_update(&g_file_md5_ctx, file_data, frame_len);
		g_md5_started = 1;
	}

	pthread_mutex_unlock(&g_file_mutex);
}

/* ---- 写文件确认 (继续发送后续帧) ---- */

void file_write_confirm(stru_icp67 *p_icp67, uint8_t result,
	uint32_t id, uint32_t offset)
{
	if(NULL == p_icp67) return;

	/* 检查 FTU 返回的错误结果 */
	(void)result; /* TODO: FTU error handling — if result != FILE_WRITE_CONT_1/0, abort transfer */
	if(FILE_WRITE_CONT_0 != result && FILE_WRITE_CONT_1 != result)
	{
		LOG_E("file_write_confirm: FTU error result=%d, abort transfer", result);
		g_md5_started = 0;
		return;
	}

	pthread_mutex_lock(&g_file_mutex);

	{
		std::string fpath = g_file_path + g_dir_name + "/" + g_file_name;
		uint32_t size    = 0;
		uint8_t  follow  = 0;
		uint16_t frame_len;

		file_size_get(fpath.c_str(), &size);

		if((size - offset) > FILE_TRANS_FRMAE_LEN)
		{
			follow    = 1;
			frame_len = FILE_TRANS_FRMAE_LEN;
		}
		else
		{
			frame_len = (uint16_t)(size - offset);
		}

		uint8_t file_data[FILE_TRANS_FRMAE_LEN] = {0};
		file_read(fpath.c_str(), offset, file_data, frame_len);

		if(p_icp67->method.file_write_cb)
		{
			p_icp67->method.file_write_cb(p_icp67, id, offset, follow, file_data, frame_len);
		}

		if(g_md5_started)
		{
			md5_update(&g_file_md5_ctx, file_data, frame_len);
		}
	}

	pthread_mutex_unlock(&g_file_mutex);
}

/* ---- 写文件结束 (MD5 校验) ---- */

void file_write_end(stru_icp67 *p_icp67, uint8_t result, uint32_t id,
	uint32_t offset, uint8_t *md5)
{
	uint8_t file_md5[16] = {0};
	uint8_t final_result = result;

	if(NULL == p_icp67 || NULL == md5) return;

	pthread_mutex_lock(&g_file_mutex);

	if(g_md5_started)
	{
		md5_final(&g_file_md5_ctx, file_md5);

		if(0 != memcmp(md5, file_md5, 16))
		{
			LOG_E("file_write_end: MD5 mismatch");
			final_result = 1;
		}

		g_md5_started = 0;
	}

	if(0 == final_result)
	{
		LOG_I("file_write_end: %s success", g_file_name.c_str());
	}
	else
	{
		LOG_E("file_write_end: %s failed", g_file_name.c_str());
	}

	pthread_mutex_unlock(&g_file_mutex);

	if(p_icp67->method.file_write_end_confirm_cb)
	{
		p_icp67->method.file_write_end_confirm_cb(p_icp67, final_result, id, file_md5);
	}

	(void)offset;
}

/* ---- 压缩目录读取出 ---- */

void zip_dir_pop_out(stru_icp67 *p_icp67, uint8_t zip_type,
	uint8_t *zip_name, uint8_t len, uint8_t file_num,
	stru_zip_file_info *p_file_info)
{
	if(NULL == p_icp67 || NULL == zip_name || NULL == p_file_info) return;

	pthread_mutex_lock(&g_file_mutex);
	g_zip_name = std::string((char *)zip_name, len);

	LOG_I("zip_dir_pop_out: type=%d, name=%s, files=%d", zip_type, g_zip_name.c_str(), file_num);
	for(uint8_t i = 0; i < file_num; i++)
	{
		LOG_I("  [%d] %s (size=%d)", p_file_info[i].id, p_file_info[i].name, p_file_info[i].size);
	}
	pthread_mutex_unlock(&g_file_mutex);
}

/* ---- 压缩文件内容 (FTU → RTU 本地写入 zip/) ---- */

void zip_file_pop_out(stru_icp67 *p_icp67, uint8_t id, uint32_t offset,
	uint8_t *p_data, uint16_t len)
{
	if(NULL == p_icp67 || NULL == p_data) return;

	file_paths_init();

	pthread_mutex_lock(&g_file_mutex);
	{
		std::string zip_fpath = g_zip_path + g_zip_name;
		file_write(zip_fpath.c_str(), offset, p_data, len);

		if(0 == offset) LOG_I("zip_file_pop_out: %s start", g_zip_name.c_str());
	}
	pthread_mutex_unlock(&g_file_mutex);
}

/* =========================== 实时数据 + 事件队列 =========================== */

/* ---- SOE 事件 → dc_soe_event_t → 数据中心事件队列 ---- */

void soe_trans(stru_icp67 *p_icp67, stru_st_info *p_data)
{
	(void)p_icp67;
	if(NULL == p_data) return;

	{
		dc_soe_event_t *p_ev = new dc_soe_event_t();

		snprintf(p_ev->saddr, sizeof(p_ev->saddr), "self_ptl.st.%d", p_data->addr);
		p_ev->status = p_data->status;
		p_ev->attr   = p_data->attr;
		p_ev->tm_sec = 0; /* TODO: 解析 ICP67 stru_tm_6bit 时间戳 */
		p_ev->tm_ms  = 0;

		dc_event_queue_push((void *)p_ev);
	}

	LOG_I("soe_trans: addr=%d, status=%d", p_data->addr, p_data->status);
}

/* ---- 故障事件 → dc_fault_event_t → 数据中心故障队列 ---- */

void fault_trans(stru_icp67 *p_icp67, uint16_t num, stru_fault_info *p_data)
{
	(void)p_icp67; (void)num;
	if(NULL == p_data) return;

	{
		dc_fault_event_t *p_ev = new dc_fault_event_t();

		snprintf(p_ev->saddr, sizeof(p_ev->saddr), "self_ptl.fault.%d", num);
		p_ev->fault_id = num;
		p_ev->st_count = p_data->st_num;
		p_ev->mx_count = p_data->mx_num;
		p_ev->tm_sec   = 0;
		p_ev->tm_ms    = 0;

		dc_fault_queue_push((void *)p_ev);
	}

	LOG_I("fault_trans: st_count=%d, mx_count=%d", p_data->st_num, p_data->mx_num);
}

/* ---- 遥测扰动 → dc_disturb_event_t → 数据中心扰动队列 ---- */

void mx_change(stru_icp67 *p_icp67, stru_mx_info *p_data)
{
	(void)p_icp67;
	if(NULL == p_data) return;

	{
		dc_disturb_event_t *p_ev = new dc_disturb_event_t();

		snprintf(p_ev->saddr, sizeof(p_ev->saddr), "self_ptl.mx.%d", p_data->addr);
		p_ev->f_val  = (float)p_data->data;
		p_ev->tm_sec = 0;
		p_ev->tm_ms  = 0;

		dc_disturb_dd_queue_push((void *)p_ev);
	}

	LOG_I("mx_change: addr=%d, data=%d", p_data->addr, p_data->data);
}

void mx_trans(stru_icp67 *p_icp67, uint16_t info_addr, float p_data)
{
	(void)p_icp67;
	char saddr[128];
	snprintf(saddr, sizeof(saddr), "self_ptl.mx.%d", info_addr);
	dc_set_out_signal_val(saddr, &p_data, MODULE_SELF_PTL);
}

void st_trans(stru_icp67 *p_icp67, uint16_t info_addr, uint8_t p_data)
{
	(void)p_icp67;
	char saddr[128];
	snprintf(saddr, sizeof(saddr), "self_ptl.st.%d", info_addr);
	dc_set_out_signal_val(saddr, &p_data, MODULE_SELF_PTL);
}

void dd_trans(stru_icp67 *p_icp67, uint16_t info_addr, float p_data)
{
	(void)p_icp67;
	char saddr[128];
	snprintf(saddr, sizeof(saddr), "self_ptl.dd.%d", info_addr);
	dc_set_out_signal_val(saddr, &p_data, MODULE_SELF_PTL);
}

