/*-------------------------------------------------------------------------
   Serial function copy right information

   Copyright (C) 2007-2009. Lin, Rongrong <woody@palmmicro.com>

   This program is free software; you can redistribute it and/or
   modify it under the terms of the GNU Lesser General Public
   License as published by the Free Software Foundation; either
   version 2.1 of the License, or (at your option) any later version.

   This software is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   Lesser General Public License for more details.

   You should have received a copy of the GNU Lesser General Public
   License along with this library; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
-------------------------------------------------------------------------*/

#include "version.h"
#include "type.h"
#include "core.h"
#include "apps.h"

#include "bank1.h"

#if defined SYS_UART || defined SYS_SPI
volatile UCHAR Serial_iRecvHead, Serial_iRecvTail;
volatile UCHAR Serial_pRecv[SERIAL_BUF_SIZE_RX];
#endif

#ifdef SERIAL_UI

#define SERIAL_STRING_SIZE	64		// max string length to handle

UCHAR Serial_iSendHead, Serial_iSendTail;
UCHAR Serial_pSend[SERIAL_BUF_SIZE_TX];

PCHAR _pUnHandledData;
UCHAR _iUnHandledLen;

void SerialInit()
{
	Serial_iSendHead = 0;
	Serial_iSendTail = SERIAL_BUF_SIZE_TX - 1;
	Serial_iRecvHead = 0;
	Serial_iRecvTail = SERIAL_BUF_SIZE_RX - 1;

	_pUnHandledData = NULL;
	_iUnHandledLen = 0;

#ifdef SYS_UART
	UART_Init(78, FALSE);	// 19200 bps, 8 bit data, 1 bit stop, no parity
#endif
}

#define SERIAL_COMMAND_LEN	4

void _SerialProcessString(PCHAR pStr)
{
	// Simply call UdpDebugString();
//	UdpDebugString(pStr);
	if (!memcmp_str(pStr, "LOOP"))
	{
		UdpDebugString(pStr);
	}
	else if (!memcmp_str(pStr, "KEY "))
	{
		UdpDebugString("Serial KEY");
		UI_HandleKeys(pStr[SERIAL_COMMAND_LEN]);
	}
}

BOOLEAN _SerialHandleString(PCHAR pBuf, UCHAR iLen)
{
	UCHAR iTotal;
	PCHAR p;

	if (_iUnHandledLen)
	{	// Has old unhandled data, we do not support remalloc, so need to malloc a larger buffer again
		iTotal = _iUnHandledLen + iLen;
		if (iTotal >= SERIAL_STRING_SIZE)
		{	// must be something wrong
			free(_pUnHandledData);
			_iUnHandledLen = 0;
			return FALSE;
		}
		p = malloc(iTotal);
		if (p)	// fail silently when malloc failed
		{
			memcpy(p, _pUnHandledData, _iUnHandledLen);
			memcpy((PCHAR)(p + _iUnHandledLen), pBuf, iLen);
			free(_pUnHandledData);
			_iUnHandledLen = 0;

			_SerialProcessString(p);
			free(p);
		}
		else
		{
			return FALSE;
		}
	}
	else
	{
		if (iLen > 1)
		{
			_SerialProcessString(pBuf);
		}
	}
	return TRUE;
}

BOOLEAN _SerialSaveUnHandledData(PCHAR pBuf, UCHAR iLen)
{
	UCHAR iTotal;
	PCHAR p;

	if (_iUnHandledLen)
	{	// Has old unhandled data, we do not support remalloc, so need to malloc a larger buffer again
		iTotal = _iUnHandledLen + iLen;
		if (iTotal >= SERIAL_STRING_SIZE)
		{	// must be something wrong
			free(_pUnHandledData);
			_iUnHandledLen = 0;
			return FALSE;
		}
		p = malloc(iTotal);
		if (p)	// fail silently when malloc failed
		{
			memcpy(p, _pUnHandledData, _iUnHandledLen);
			memcpy((PCHAR)(p + _iUnHandledLen), pBuf, iLen);
			free(_pUnHandledData);
			_pUnHandledData = p;
			_iUnHandledLen = iTotal;
		}
		else
		{
			return FALSE;
		}
	}
	else
	{
		_pUnHandledData = malloc(iLen);
		if (_pUnHandledData)	// fail silently when malloc failed
		{
			memcpy(_pUnHandledData, pBuf, iLen);
			_iUnHandledLen = iLen;
		}
		else
		{
			return FALSE;
		}
	}
//	UdpDebugVal(_iUnHandledLen, 10);
	return TRUE;
}

// Handle UART received data here, as an loop back example, we simply use UDP debug output
void _SerialHandleData(PCHAR pBuf, UCHAR iLen)	
{
	UCHAR i, iStrLen, iZeroNum;
	PCHAR pCur;

	iZeroNum = 0;

	// get "real" data by minus 1
	for (i = 0; i < iLen; i ++)
	{
//		pBuf[i] --;
		if (!pBuf[i])
		{
			iZeroNum ++;
		}
	}

	pCur = pBuf;
	for (i = 0; i < iZeroNum; i ++)
	{
		iStrLen = strlen(pCur) + 1;
		if (!_SerialHandleString(pCur, iStrLen))
		{
			UdpDebugString("UART data unhanded");
		}
		pCur += iStrLen;
		iLen -= iStrLen;
	}
	if (iLen)
	{
		if (!_SerialSaveUnHandledData(pCur, iLen))
		{
			UdpDebugString("UART data lost");
		}
	}
}

void _SerialSendData()
{
	UCHAR iTail;

//	DI;

	iTail = Serial_iSendTail + 1;
	if (iTail == SERIAL_BUF_SIZE_TX)
	{
		iTail = 0;
	}

	while (iTail != Serial_iSendHead)
	{
#ifdef SYS_UART
		if (UART_SendByte(Serial_pSend[iTail]))
		{
			Serial_iSendTail = iTail;
			iTail ++;
			if (iTail == SERIAL_BUF_SIZE_TX)
			{
				iTail = 0;
			}
		}
		else
		{
			break;
		}
#endif
	}

//	EI;
}

void SerialRun()
{
	UCHAR iLen;
	UCHAR pBuf[SERIAL_BUF_SIZE_RX];
	UCHAR iTail;

	_SerialSendData();

	iLen = 0;

	DI;

	iTail = Serial_iRecvTail + 1;
	if (iTail == SERIAL_BUF_SIZE_RX)
	{
		iTail = 0;
	}

	while (iTail != Serial_iRecvHead)
	{
		pBuf[iLen] = Serial_pRecv[iTail];
		iLen ++;
		Serial_iRecvTail = iTail;
		iTail ++;
		if (iTail == SERIAL_BUF_SIZE_RX)
		{
			iTail = 0;
		}
	}

	EI;

	if (iLen)
	{
		_SerialHandleData(pBuf, iLen);
	}

	_SerialSendData();
}

void SerialSend(PCHAR pBuf, UCHAR iLen)
{
	UCHAR i;

	_SerialSendData();
	for (i = 0; i < iLen; i ++)
	{
		if (Serial_iSendTail != Serial_iSendHead)
		{
			Serial_pSend[Serial_iSendHead] = pBuf[i];
			Serial_iSendHead ++;
			if (Serial_iSendHead == SERIAL_BUF_SIZE_TX)
			{
				Serial_iSendHead = 0;
			}
		}
	}
	_SerialSendData();
}

void SerialSendString(PCHAR pStr)
{
	UCHAR pZero[4];

	memset(pZero, 0, 4);
	SerialSend(pStr, strlen(pStr) + 1);
	SerialSend(pZero, 4);
}

#else	// SERIAL_UI

#ifdef SYS_SPI

// SPI serial template

// ToDo: Add user globals here

//	Initialize serial communication (has to be called before/after[...])
void SerialInit()
{
	Serial_iRecvHead = 0;
	Serial_iRecvTail = SERIAL_BUF_SIZE_RX - 1;
	SPI_Init();
//	UdpDebugString("SPI init");
}

// Serial handler (main-loop)
void SerialRun()
{
	UCHAR i, iLen, iTail;
	UCHAR pBuf[SERIAL_BUF_SIZE_RX];

	iLen = 0;

//	UdpDebugString("SPI run");

	// read received data
	DI;

	iTail = Serial_iRecvTail + 1;
	if (iTail == SERIAL_BUF_SIZE_RX)
	{
		iTail = 0;
	}

	while (iTail != Serial_iRecvHead)
	{
		pBuf[iLen] = Serial_pRecv[iTail];
		iLen ++;
		Serial_iRecvTail = iTail;
		iTail ++;
		if (iTail == SERIAL_BUF_SIZE_RX)
		{
			iTail = 0;
		}
	}

	EI;

	// debug 
	if (iLen)
	{
		UdpDebugVal(iLen, 10);
		for (i = 0; i < iLen; i ++)
		{
			UdpDebugVal(pBuf[i], 16);
		}
	}
}

#elif defined SYS_UART

// UART serial template

// ToDo: Add user globals here

//	Initialize serial communication (has to be called before/after[...])
void SerialInit()
{
	// ToDo: Insert user specific initiaization here

	Serial_iRecvHead = 0;
	Serial_iRecvTail = SERIAL_BUF_SIZE_RX - 1;

	UART_Init(78, FALSE);	// 19200 bps, 8 bit data, 1 bit stop, no parity
}

// Serial handler (main-loop)
void SerialRun()
{
	UCHAR i, iLen, iTail;
	UCHAR pBuf[SERIAL_BUF_SIZE_RX];

	iLen = 0;

	// read received data
	DI;

	iTail = Serial_iRecvTail + 1;
	if (iTail == SERIAL_BUF_SIZE_RX)
	{
		iTail = 0;
	}

	while (iTail != Serial_iRecvHead)
	{
		pBuf[iLen] = Serial_pRecv[iTail];
		iLen ++;
		Serial_iRecvTail = iTail;
		iTail ++;
		if (iTail == SERIAL_BUF_SIZE_RX)
		{
			iTail = 0;
		}
	}

	EI;

	// loop back
	for (i = 0; i < iLen; i ++)
	{
		if (!UART_SendByte(pBuf[i]))			// Check for transmit error
		{
			UdpDebugString("UART Send Error");		// Report error on Udp
		}
	}
}

#endif
#endif	// SERIAL_UI
