/**
*@file    sr_crc.c
*@brief   crc校验接口
*@details 提供crc校验接口
*@copyright Copyright (c) 2024 lierda. All rights reserved.
*@author  Lierda-RDC
*@date    2024-05-07
*@example ZhaoyangSDK\example\component\crypto\crypto_example.c
*/

#include "sr_crc.h"

typedef enum {
	REF_4BIT = 4,
	REF_5BIT = 5,
	REF_6BIT = 6,
	REF_7BIT = 7,
	REF_8BIT = 8,
	REF_16BIT = 16,
	REF_32BIT = 32
}REFLECTED_MODE;

uint32_t ReflectedData(uint32_t data, REFLECTED_MODE mode)
{
	data = ((data & 0xffff0000) >> 16) | ((data & 0x0000ffff) << 16);
	data = ((data & 0xff00ff00) >> 8) | ((data & 0x00ff00ff) << 8);
	data = ((data & 0xf0f0f0f0) >> 4) | ((data & 0x0f0f0f0f) << 4);
	data = ((data & 0xcccccccc) >> 2) | ((data & 0x33333333) << 2);
	data = ((data & 0xaaaaaaaa) >> 1) | ((data & 0x55555555) << 1);

	switch (mode)
	{
	case REF_32BIT:
		return data;
	case REF_16BIT:
		return (data >> 16) & 0xffff;
	case REF_8BIT:
		return (data >> 24) & 0xff;
	case REF_7BIT:
		return (data >> 25) & 0x7f;
	case REF_6BIT:
		return (data >> 26) & 0x7f;
	case REF_5BIT:
		return (data >> 27) & 0x1f;
	case REF_4BIT:
		return (data >> 28) & 0x0f;
	}
	return 0;
}

uint8_t CheckCrc4(uint8_t poly, uint8_t init, srBool_t refIn, srBool_t refOut, uint8_t xorOut,
	const uint8_t *buffer, uint32_t length)
{
	uint8_t i;
	uint8_t crc;

	if (refIn == SR_TRUE)
	{
		crc = init;
		poly = ReflectedData(poly, REF_4BIT);

		while (length--)
		{
			crc ^= *buffer++;
			for (i = 0; i < 8; i++)
			{
				if (crc & 0x01)
				{
					crc >>= 1;
					crc ^= poly;
				}
				else
				{
					crc >>= 1;
				}
			}
		}

		return crc ^ xorOut;
	}
	else
	{
		crc = init << 4;
		poly <<= 4;

		while (length--)
		{
			crc ^= *buffer++;
			for (i = 0; i < 8; i++)
			{
				if (crc & 0x80)
				{
					crc <<= 1;
					crc ^= poly;
				}
				else
				{
					crc <<= 1;
				}
			}
		}

		return (crc >> 4) ^ xorOut;
	}
}

uint8_t CheckCrc5(uint8_t poly, uint8_t init, srBool_t refIn, srBool_t refOut, uint8_t xorOut,
	const uint8_t *buffer, uint32_t length)
{
	uint8_t i;
	uint8_t crc;

	if (refIn == SR_TRUE)
	{
		crc = init;
		poly = ReflectedData(poly, REF_5BIT);

		while (length--)
		{
			crc ^= *buffer++;
			for (i = 0; i < 8; i++)
			{
				if (crc & 0x01)
				{
					crc >>= 1;
					crc ^= poly;
				}
				else
				{
					crc >>= 1;
				}
			}
		}

		return crc ^ xorOut;
	}
	else
	{
		crc = init << 3;
		poly <<= 3;

		while (length--)
		{
			crc ^= *buffer++;
			for (i = 0; i < 8; i++)
			{
				if (crc & 0x80)
				{
					crc <<= 1;
					crc ^= poly;
				}
				else
				{
					crc <<= 1;
				}
			}
		}

		return (crc >> 3) ^ xorOut;
	}
}

uint8_t CheckCrc6(uint8_t poly, uint8_t init, srBool_t refIn, srBool_t refOut, uint8_t xorOut,
	const uint8_t *buffer, uint32_t length)
{
	uint8_t i;
	uint8_t crc;

	if (refIn == SR_TRUE)
	{
		crc = init;
		poly = ReflectedData(poly, REF_6BIT);

		while (length--)
		{
			crc ^= *buffer++;
			for (i = 0; i < 8; i++)
			{
				if (crc & 0x01)
				{
					crc >>= 1;
					crc ^= poly;
				}
				else
				{
					crc >>= 1;
				}
			}
		}

		return crc ^ xorOut;
	}
	else
	{
		crc = init << 2;
		poly <<= 2;

		while (length--)
		{
			crc ^= *buffer++;
			for (i = 0; i < 8; i++)
			{
				if (crc & 0x80)
				{
					crc <<= 1;
					crc ^= poly;
				}
				else
				{
					crc <<= 1;
				}
			}
		}

		return (crc >> 2) ^ xorOut;
	}
}

uint8_t CheckCrc7(uint8_t poly, uint8_t init, srBool_t refIn, srBool_t refOut, uint8_t xorOut,
	const uint8_t *buffer, uint32_t length)
{
	uint8_t i;
	uint8_t crc;

	if (refIn == SR_TRUE)
	{
		crc = init;
		poly = ReflectedData(poly, REF_7BIT);

		while (length--)
		{
			crc ^= *buffer++;
			for (i = 0; i < 8; i++)
			{
				if (crc & 0x01)
				{
					crc >>= 1;
					crc ^= poly;
				}
				else
				{
					crc >>= 1;
				}
			}
		}

		return crc ^ xorOut;
	}
	else
	{
		crc = init << 1;
		poly <<= 1;

		while (length--)
		{
			crc ^= *buffer++;
			for (i = 0; i < 8; i++)
			{
				if (crc & 0x80)
				{
					crc <<= 1;
					crc ^= poly;
				}
				else
				{
					crc <<= 1;
				}
			}
		}

		return (crc >> 1) ^ xorOut;
	}
}

uint8_t CheckCrc8(uint8_t poly, uint8_t init, srBool_t refIn, srBool_t refOut, uint8_t xorOut,
	const uint8_t *buffer, uint32_t length)
{
	uint32_t i = 0;
	uint8_t crc = init;

	while (length--)
	{
		if (refIn == SR_TRUE)
		{
			crc ^= ReflectedData(*buffer++, REF_8BIT);
		}
		else
		{
			crc ^= *buffer++;
		}

		for (i = 0; i < 8; i++)
		{
			if (crc & 0x80)
			{
				crc <<= 1;
				crc ^= poly;
			}
			else
			{
				crc <<= 1;
			}
		}
	}

	if (refOut == SR_TRUE)
	{
		crc = ReflectedData(crc, REF_8BIT);
	}

	return crc ^ xorOut;
}

uint16_t CheckCrc16(uint16_t poly, uint16_t init, srBool_t refIn, srBool_t refOut, uint16_t xorOut,
	const uint8_t *buffer, uint32_t length)
{
	uint32_t i = 0;
	uint16_t crc = init;

	while (length--)
	{
		if (refIn == SR_TRUE)
		{
			crc ^= ReflectedData(*buffer++, REF_8BIT) << 8;
		}
		else
		{
			crc ^= (*buffer++) << 8;
		}

		for (i = 0; i < 8; i++)
		{
			if (crc & 0x8000)
			{
				crc <<= 1;
				crc ^= poly;
			}
			else
			{
				crc <<= 1;
			}
		}
	}

	if (refOut == SR_TRUE)
	{
		crc = ReflectedData(crc, REF_16BIT);
	}

	return crc ^ xorOut;
}

uint32_t CheckCrc32(uint32_t poly, uint32_t init, srBool_t refIn, srBool_t refOut, uint32_t xorOut,const uint8_t *buffer, uint32_t length)
{
	uint32_t i = 0;
	uint32_t crc = init;

	while (length--)
	{
		if (refIn == SR_TRUE)
		{
			crc ^= ReflectedData(*buffer++, REF_8BIT) << 24;
		}
		else
		{
			crc ^= (*buffer++) << 24;
		}

		for (i = 0; i < 8; i++)
		{
			if (crc & 0x80000000)
			{
				crc <<= 1;
				crc ^= poly;
			}
			else
			{
				crc <<= 1;
			}
		}
	}

	if (refOut == SR_TRUE)
	{
		crc = ReflectedData(crc, REF_32BIT);
	}

	return crc ^ xorOut;
}


const SR_CRC_PARAM_T g_crcParam[] ={
	CRC4_ITU_PARAMS,         
	CRC5_EPC_PARAMS,         
	CRC5_ITU_PARAMS,         
	CRC5_USB_PARAMS,         
	CRC6_ITU_PARAMS,         
	CRC7_MMC_PARAMS,         
	CRC8_PARAMS,             
	CRC8_ITU_PARAMS,         
	CRC8_ROHC_PARAMS,        
	CRC8_MAXIM_PARAMS,      
	CRC16_IBM_PARAMS,        
	CRC16_MAXIM_PARAMS,      
	CRC16_USB_PARAMS,       
	CRC16_MODBUS_PARAMS,    
	CRC16_CCITT_PARAMS,     
	CRC16_CCITT_FALSE_PARAMS,
	CRC16_X25_PARAMS,       
	CRC16_XMODEM_PARAMS,    
	CRC16_DNP_PARAMS,       
	CRC32_PARAMS,           
	CRC32_MPEG2_PARAMS
};
	
uint32_t CrcCheck(SR_CRC_PARAM_T crcParam, const uint8_t *buffer, uint32_t length)
{
	switch (crcParam.width)
	{
		case 4:
			return CheckCrc4(crcParam.poly, crcParam.init, crcParam.refIn, crcParam.refOut,
				crcParam.xorOut, buffer, length);
		case 5:
			return CheckCrc5(crcParam.poly, crcParam.init, crcParam.refIn, crcParam.refOut,
				crcParam.xorOut, buffer, length);
		case 6:
			return CheckCrc6(crcParam.poly, crcParam.init, crcParam.refIn, crcParam.refOut,
				crcParam.xorOut, buffer, length);
		case 7:
			return CheckCrc7(crcParam.poly, crcParam.init, crcParam.refIn, crcParam.refOut,
				crcParam.xorOut, buffer, length);
		case 8:
			return CheckCrc8(crcParam.poly, crcParam.init, crcParam.refIn, crcParam.refOut,
				crcParam.xorOut, buffer, length);
		case 16:
			return CheckCrc16(crcParam.poly, crcParam.init, crcParam.refIn, crcParam.refOut,
				crcParam.xorOut, buffer, length);
		case 32:
			return CheckCrc32(crcParam.poly, crcParam.init, crcParam.refIn, crcParam.refOut,
			crcParam.xorOut, buffer, length);
	}
	return 0;
}


/**  
* @brief CRC校验
*  
* @param[in] crcType  CRC算法类型,SR_CRC_TYPE_E类型
* @param[in] inp    输入数据
* @param[in] inLen  输入数据的长度（字节）  
* @param[out] out   计算结果
*
* @return 0:成功   负值：失败   
*/ 
int SR_CrcCheck(SR_CRC_TYPE_E type, uint8_t *in, uint32_t inLen,uint32_t *out)
{
	if(type >= CRC_TYPE_MAX) return -1;
		
	SR_CRC_PARAM_T crcParam = g_crcParam[type];
	
	*out = CrcCheck(crcParam, in,inLen);

	return 0;
}


/*
*后续改进：
*1、g_crcParam 添加裁剪配置宏
**/
