/**
 * @file    icp67_frame.c
 * @brief   ICP67 帧操作 — 帧定界、CRC校验、帧构造、调试打印
 */

#include "icp67.h"
#include "myLog.h"
#include <string.h>
#include <stdio.h>

/* =========================== CRC 校验 =========================== */

uint8_t crc_check_sum(uint8_t *p_data, uint16_t len)
{
	uint8_t crc = 0;
	uint16_t i;

	for(i = 0; i < len; i++)
	{
		crc += p_data[i];
	}

	return crc;
}

/* =========================== 帧搜索 =========================== */

int icp67_search_frame(uint8_t *p_data, uint16_t len, uint16_t *p_pos, uint16_t *p_valid_len)
{
	uint16_t i;

	if(NULL == p_data || NULL == p_pos || NULL == p_valid_len || 0 == len)
	{
		return -1;
	}

	for(i = 0; i < len; i++)
	{
		stru_head *p_head = (stru_head *)(p_data + i);

		if(i + sizeof(stru_head) > len)
		{
			break;
		}

		if(ICP67_HEAD != p_head->head1 || ICP67_HEAD != p_head->head2)
		{
			continue;
		}

		if((p_head->len + ICP67_FRAME_OVERHEAD) > (len - i))
		{
			break;
		}

		if(p_data[i + p_head->len + ICP67_HEAD_OVERHEAD] != crc_check_sum(p_data + i + ICP67_HEAD_OVERHEAD, p_head->len))
		{
			continue;
		}

		if(p_data[i + p_head->len + ICP67_HEAD_OVERHEAD + 1] != ICP67_TAIL)
		{
			continue;
		}

		(*p_pos) = i;
		(*p_valid_len) = p_head->len + ICP67_FRAME_OVERHEAD;

		return 0;
	}

	return -1;
}

/* =========================== 环形缓冲区帧搜索 (零拷贝) =========================== */

int icp67_search_frame_in_ring(const uint8_t *buf, uint16_t size,
	uint16_t rptr, uint16_t cnt,
	uint16_t *p_pos, uint16_t *p_valid_len)
{
	uint16_t i;

	if(NULL == buf || NULL == p_pos || NULL == p_valid_len || 0 == cnt)
	{
		return -1;
	}

	for(i = 0; i < cnt; i++)
	{
		uint16_t idx         = (rptr + i) % size;
		stru_head *p_head     = (stru_head *)(buf + idx);

		/* 剩余数据不足一个帧头 */
		if(i + sizeof(stru_head) > cnt)
		{
			break;
		}

		/* 帧头检测: 0x67 0x67 */
		if(ICP67_HEAD != buf[idx] || ICP67_HEAD != buf[(idx + 3) % size])
		{
			continue;
		}

		/*
		 * 读取 len 字段: 跨环边界需逐字节拼装
		 * len 是 uint16_t，在 p_head->len 中 (offset=1,2)
		 */
		uint16_t len_val;
		uint8_t  b1 = buf[(idx + 1) % size];
		uint8_t  b2 = buf[(idx + 2) % size];

		len_val = ((uint16_t)b2 << 8) | b1;

		/* 帧不完整: 长度不够 */
		if(len_val + ICP67_FRAME_OVERHEAD > cnt - i)
		{
			break;
		}

		/* CRC 校验: 校验和覆盖 head_overhead 之后的数据 */
		{
			uint8_t  calc_crc = 0;
			uint16_t j;

			for(j = 0; j < len_val; j++)
			{
				calc_crc += buf[(idx + ICP67_HEAD_OVERHEAD + j) % size];
			}

			if(buf[(idx + ICP67_HEAD_OVERHEAD + len_val) % size] != calc_crc)
			{
				continue;
			}
		}

		/* 帧尾校验 */
		if(buf[(idx + ICP67_HEAD_OVERHEAD + len_val + 1) % size] != ICP67_TAIL)
		{
			continue;
		}

		*p_pos       = i;
		*p_valid_len = len_val + ICP67_FRAME_OVERHEAD;

		return 0;
	}

	return -1;
}

/* =========================== 帧构造 =========================== */

void icp67_make_head(uint8_t dst, uint8_t ti, uint8_t cot, uint16_t inf, uint8_t *p_tx, uint16_t *tx_len)
{
	stru_head *p_head = (stru_head *)p_tx;

	p_head->head1     = ICP67_HEAD;
	p_head->head2     = ICP67_HEAD;
	p_head->src       = ENUM_DEV_TYPE_MAINTENANCE;
	p_head->dst       = dst;
	p_head->dir       = ENUM_DIR_MASTER;
	p_head->ti        = ti;
	p_head->cot       = cot;
	p_head->info_addr = inf;
	p_head->dev_addr  = DEV_RK3562_ADDR;

	*tx_len = sizeof(stru_head);
}

void icp67_make_tail(uint8_t *p_tx, uint16_t *tx_len)
{
	stru_head *p_head = (stru_head *)p_tx;

	p_head->len = *tx_len - ICP67_HEAD_OVERHEAD;
	p_tx[(*tx_len)++] = crc_check_sum(p_tx + ICP67_HEAD_OVERHEAD, p_head->len);
	p_tx[(*tx_len)++] = ICP67_TAIL;
}

/* =========================== 调试打印 =========================== */

void icp67_show_frame(uint8_t *p_data, uint16_t len)
{
	const char *str[5] = {"维护软件", "主板", "采样板", "LCD", "XTU"};
	stru_head *p_head;
	uint16_t i;

	if(NULL == p_data || 0 == len)
	{
		return;
	}

	p_head = (stru_head *)p_data;

	if(p_head->src >= 1 && p_head->src <= 5 && p_head->dst >= 1 && p_head->dst <= 5)
	{
		printf("%s -> %s, len = %d, dev_addr %d\n",
			str[p_head->src - 1], str[p_head->dst - 1], len, p_head->dev_addr);
	}
	else
	{
		printf("src=%d -> dst=%d, len = %d, dev_addr %d\n",
			p_head->src, p_head->dst, len, p_head->dev_addr);
	}

	for(i = 0; i < len; i++)
	{
		printf("%02X ", p_data[i]);
	}
	printf("\n");
}
