
#include "xmodem.h"
#include "serial.h"
#include "trigger.h"
#include "shell.h"
#include "delay.h"

//---------------------------------------------------------

#define	DEBUG_OK		0

#if (DEBUG_OK == 1)

#define	DEBUG_OUT(...)	do \
{\
	printf(__VA_ARGS__);	\
}while(0)

void OutDebugBufInfo(void){};

#elif (DEBUG_OK == 2)

char g_debugbuf[200][50];
int g_debugidx = 0;

#define	DEBUG_OUT(...)	do \
{\
	if (g_debugidx < 200) \
	{ \
		sprintf((char*)&(g_debugbuf[g_debugidx][0]), __VA_ARGS__);	\
		g_debugidx++; \
	} \
}while(0)


#if (DEBUG_OK == 2)
void OutDebugBufInfo(void)
{
	printf("\r\nDebug buff info (%d):\r\n", g_debugidx);
	for (int i = 0; i < g_debugidx; i++)
	{
		printf("%s", (char*)(&g_debugbuf[i][0]));
		DelayMs(10);
	}
	printf("\r\nDebug buff info end\r\n");

	g_debugidx = 0;
}
#endif


#else

#define	DEBUG_OUT(...)	do {}while(0)

void OutDebugBufInfo(void){};

#endif

//---------------------------------------------------------

// 计算xmodem的crc值
u16 CalcXModemCrc(u8 * pDat, int count)
{
	u16 crc;
	int i;

	crc = 0;
	while (--count >= 0)
	{
		crc = crc ^ (u16) (*pDat++ << 8);
		i = 8;
		do
		{
			if (crc & 0x8000)
			{
				crc = crc << 1 ^ 0x1021;		// X16+X12+X5+X0
			}
			else
			{
				crc = crc << 1;
			}
		} while (--i);
	}
 
	return (crc);
}

// 检验xmodem数据包的正确性
int CheckXModemDat(int crcflag, u8 * pBuf, int size)
{  
	if(crcflag != 0)
	{
		u16 crc = CalcXModemCrc(pBuf, size);
		u16 tcrc = ((u16)(pBuf[size])<<8) + pBuf[size+1];
 
		if (crc == tcrc)
		{
			return 1;
		}
	}
	else
	{
		int i = 0;
		u8 cks = 0;

		for(i = 0; i < size; i++)
		{
			cks += pBuf[i]; 
		}
		
		if (cks == pBuf[size])
		{
			return 1;
		}
	}

	return 0; 
}

// 接收xmodem数据
int XmodemReceive(XmodemCtrl * pCtrl)
{
	int rslt, err, retry, recvsize, exsize;
	int timer;
	u8 packid, tmpid, temp;
	u8 trychar, svchar;
	u8 xbuff[XMODEM1K_FRAME_C];
	u8 ch;
	int crcflag;
	int datidx;

	trychar = 'C';		// 先用crc校验方式
	svchar = trychar;
	recvsize = 0;
	packid = 1;
	err = 0;
	datidx = 0;

	pCtrl->InitSave();

	DEBUG_OUT("Start XmodemReceive\r\n");

#define	MAX_RETRY		32

	// 启动传输
	for (retry = 0; retry < MAX_RETRY; retry++)		// 尝试启动发送
	{
		// 1. 向发送方发送同步字节
		if(trychar != 0)
		{
			xbuff[0] = trychar;
			pCtrl->XmodemOutBuff(xbuff, 1);
		}

		// 2. 等待回复数据包
		timer = GetMsSoftTimer();	// 记录时间
		recvsize = 0;
		do
		{
			rslt = pCtrl->XmodemInBuff(xbuff, 1);
			if (rslt != 1)		// 等到字符
			{
				if (GetMsSoftTimer() - timer > 1000)		// 判断是否超时
				{
					break;
				}
				continue;
			}

			ch = xbuff[0];
			switch(ch)
			{
				case XMODEM_SOH:		// Xmodem数据包头
				{
					DEBUG_OUT("get soh\r\n");
					recvsize = XMODEM_F_SIZE;
					break;
				}
				case XMODEM_STX:		// 1K-Xmodem数据包头
				{
					DEBUG_OUT("get stx\r\n");
					recvsize = XMODEM1K_F_SIZE;
					break;
				}
				case XMODEM_EOT:		// 传输结束
				{
					DEBUG_OUT("get eot\r\n");

					pCtrl->XmodemCleanBuff();			// 清除已经接收数据
					xbuff[0] = XMODEM_ACK;			// 确认回复
					pCtrl->XmodemOutBuff(xbuff, 1);
					err = 1;
					break;
				}
				case XMODEM_CAN:		// 取消传输
				{
					DEBUG_OUT("get can\r\n");

					pCtrl->XmodemCleanBuff();			// 清除已经接收数据
					xbuff[0] = XMODEM_ACK;			// 确认回复
					pCtrl->XmodemOutBuff(xbuff, 1);
					err = -2;
					break;
				}

				default:
				{
					DEBUG_OUT("get others\r\n");
					break;
				}
			}

			break;
		}while(1);

		if (err == 0)
		{
			// 3. 判断回复是否正确
			if (recvsize == 0)	// 同步字超时
			{
				if (trychar == 'C')
				{
					if (retry >= MAX_RETRY-1)
					{
						trychar = XMODEM_NAK;		// 改为使用和校验方式
						retry = 0;
					}
					continue;					// 重新开始
				}
				else
				{
					if (retry >= MAX_RETRY-1)
					{
						err = -1;	// 真超时，退出
					}
					else
					{
						continue;
					}
				}
			}
			if (trychar != 0)
			{
				DEBUG_OUT("save trychar\r\n");
				svchar = trychar;
			}
			trychar = 0;		// 已经启动了传输
		}

		// 传输文件内容
		if (err == 0 && recvsize != 0)
		{
			if (svchar == 'C')
			{
				DEBUG_OUT("exsize = 4\r\n");
				exsize = XMODEM_HEAD_SIZE+XMODEM_CRC_SIZE-1;
				crcflag = 1;
			}
			else
			{
				DEBUG_OUT("exsize = 3\r\n");
				exsize = XMODEM_HEAD_SIZE+XMODEM_CHECK_SIZE-1;
				crcflag = 0;
			}

			// 接收数据包
			timer = GetMsSoftTimer();	// 记录时间
			DEBUG_OUT("timer=%d\r\n", timer);

			do
			{
				rslt = pCtrl->XmodemInBuff(&(xbuff[1]), recvsize+exsize);
				if (rslt == recvsize+exsize)		// 等到字符
				{
					DEBUG_OUT("get exsize+recvsize=%d+%d\r\n", exsize, recvsize);

					tmpid = xbuff[1];
					temp = ~(xbuff[2]);

					if ((tmpid == temp) &&
						(CheckXModemDat(crcflag, &(xbuff[3]), recvsize) != 0))
					{
						xbuff[0] = XMODEM_ACK;			// 回复ACK，确认接收到，请求发送下一个数据包
						pCtrl->XmodemOutBuff(xbuff, 1);

						DEBUG_OUT("check ok, send ack\r\n");

						if (tmpid == packid)			// 保存数据
						{
							int svlen;
							int svflag = (datidx == 0 ? 0:1);
							svlen = pCtrl->SaveData(svflag, &(xbuff[3]), recvsize, pCtrl->savedsize);		// 保存数据
							if (svlen > 0)
							{
								pCtrl->savedsize += svlen;
							}
							datidx++;
							packid++;
						}
						retry = 0;
					}
					else
					{
						xbuff[0] = XMODEM_NAK;			// 回复 NAK，需要重新传输该数据包
						pCtrl->XmodemOutBuff(xbuff, 1);

						DEBUG_OUT("crc err, send NAK\r\n");
					}
					break;
				}
				else
				{
					int tptimer;

					// 接收
					tptimer = GetMsSoftTimer();
					if (tptimer - timer > recvsize*1)		// 判断是否超时
					{
						DEBUG_OUT("tptimer=%d, timer=%d, gap=%d\r\n", tptimer, timer, tptimer - timer);

						rslt = pCtrl->XmodemGetInBuffLen();
						if (rslt > 0)
						{
							pCtrl->XmodemCleanBuff();			// 清除已经接收数据
							xbuff[0] = XMODEM_NAK;				// 回复 NAK，需要重新传输该数据包
							pCtrl->XmodemOutBuff(xbuff, 1);
							DEBUG_OUT("resvlen = %d, send NAK\r\n", rslt);
						}
						else
						{
							DEBUG_OUT("not data, error\r\n");
							err = -1;						// 接收超时，退出
						}

						DEBUG_OUT("wait timeout\r\n");
						break;
					}
					else
					{
						// 继续等待
					}
				}
			}while(1);
		}
		else
		{
			DEBUG_OUT("err != 0 || recvsize == 0\r\n");
			break;
		}

		if (err < 0)
		{
			DEBUG_OUT("err < 0 sanc cancel\r\n");

			xbuff[0] = XMODEM_CAN;			// 取消发送
			pCtrl->XmodemOutBuff(xbuff, 1);
			pCtrl->XmodemOutBuff(xbuff, 1);
			pCtrl->XmodemOutBuff(xbuff, 1);
			break;
		}
	} // end of for

	return err;
}


