/*******************************************************************************
*                                                                              *
* Copyright 2010 Rheinmetall Canada Inc.                                  	   *
*                                                                              *
* No part of this document may be reproduced, stored in                        *
* a retrieval system, or transmitted, in any form or by any means,             *
* electronic, mechanical, photocopying, recording, or otherwise,               *
* without the prior written permission of Rheinmetall Canada Inc.              *
*                                                                              *
*******************************************************************************/
/*
	Description:
	This is a template file for standard C code file.

*/

/* ************************************************************************** */
/* ¤Revision:
000 20100616 JFM,
	Original version.

### YYYYMMDD Initial, Bug Identification
	Change description.
 */

/* ************************************************************************** */
/* Includes */
#include "define.h"
#include "Uart.h"
#include "Internaluart.h"
#include <stdio.h>
#include <string.h>
//#include "Watchdog.h"

#ifndef NO_EXTERNAL_UART
#include "sc16IS740Driver.h"
#endif

#include "digitalio.h"
//#include "DriveProtocol.h"
#include "terminal.h"
#include "NetworkProtocol.h"
#include "LoraInterface.h"


/* ************************************************************************** */
/* Local variables */

stUartData astUartData[MAX_UART_HANDLE];

const char *gUartStrings[MAX_UART_HANDLE] = { 	"PRINTF"	//UART_1
												//,"CU",		//UART_2
//												,"CONSOLE"  //UART_3
};

/* ************************************************************************** */
/* Private function prototypes */
//void _mon_putc(char c);	//override from stdio to redirect stdout on uart 3B
//void _mon_write (const char * s, unsigned int count);

//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void InitUart(void)
{
	int i;

	InternalUartInit();
	//InitSC16S740();
		
	memset(&astUartData,0,sizeof(astUartData));

	for(i = 0; i < MAX_UART_HANDLE; i++)
	{
		astUartData[i].iDataPending = 0;		//This flag is 0 when : TxPtr == RxPtr OR Uart is transmitting.
		astUartData[i].pcTxInPtr = &astUartData[i].acTxCircularBuffer[0];
		astUartData[i].pcTxOutPtr = &astUartData[i].acTxCircularBuffer[0];
		//memset(&astUartData[i].acTxCircularBuffer[0],0xDE,UART_MAX_TX_BUFF_SIZE);
		
		astUartData[i].pcRxInDataPtr = astUartData[i].pcRxOutDataPtr = &astUartData[i].acRxCircularBuffer[0];
		astUartData[i].iNbRxFIFOPendingBytes = 0;
	}
	

	//This is a physical mapping of the UARTS.
	//DO NOT CHANGE assignments unless hardware changed !
	//
	astUartData[UART_1].iIsInternal = 1;
	astUartData[UART_1].iPhysicalUartPort = INTERNAL_UART_PORT_1;	//	(232)
	
	astUartData[UART_2].iIsInternal = 1;
	astUartData[UART_2].iPhysicalUartPort = INTERNAL_UART_PORT_2;	//	(232)

#ifndef NO_EXTERNAL_UART
	astUartData[UART_3].iIsInternal = 0;
	astUartData[UART_3].iPhysicalUartPort = EXT_UART_1;	//	(SPI - 232 daughter board)	
#endif
	
	setbuf(stdout,NULL);	//to use printf without \r
	fflush(stdout);

	UartOpenComPort(NETWORK_UART_PORT,9600,UART_ONE_STOP_BIT,UART_NO_PARITY);	//Open Network port
    UartOpenComPort(LORA_MODULE_UART_PORT,9600,UART_ONE_STOP_BIT,UART_NO_PARITY);	//Open Lora module port
#ifdef USE_PRINTF	
	//UartOpenComPort(CONSOLE_UART_PORT,115200,UART_ONE_STOP_BIT,UART_NO_PARITY);	//Open console port
    
#endif

}

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

int UartResetPort(int p_iUartHandle)
{
	if(p_iUartHandle > MAX_UART_HANDLE)
		return 0;
	
	//reset the port data structures...
	astUartData[p_iUartHandle].iDataPending = 0;		//This flag is 0 when : TxPtr == RxPtr OR Uart is transmitting.
	astUartData[p_iUartHandle].pcTxInPtr = &astUartData[p_iUartHandle].acTxCircularBuffer[0];
	astUartData[p_iUartHandle].pcTxOutPtr = &astUartData[p_iUartHandle].acTxCircularBuffer[0];
	astUartData[p_iUartHandle].pcRxInDataPtr = astUartData[p_iUartHandle].pcRxOutDataPtr = &astUartData[p_iUartHandle].acRxCircularBuffer[0];
	astUartData[p_iUartHandle].iNbRxFIFOPendingBytes = 0;
	
	if(astUartData[UART_1].iIsInternal == 1)
	{
		switch(astUartData[p_iUartHandle].iPhysicalUartPort)
		{
			case INTERNAL_UART_PORT_1:
			{
				ResetUart1();
				break;
			}
			case INTERNAL_UART_PORT_2:
			{
				ResetUart2();
				break;
			}	
			default:
			break;	
		}
	}
	else
	{
#ifndef NO_EXTERNAL_UART
		switch(astUartData[p_iUartHandle].iPhysicalUartPort)
		{
			case EXT_UART_1:
			{
				break;
			}
			default:
			break;	
		}
#endif
	}	
	
	return 1;
}	

//-----------------------------------------------------------------------------
int UartOpenComPort(int p_iUartHandle, int p_iBaudRate, int p_iNbStopBits, int p_iParityEnable)
{
	int iStopbits,iParity,iRet;
	if(p_iUartHandle >= MAX_UART_HANDLE || p_iUartHandle < 0)
		return UART_INVALID_HANDLE;
	
	if(astUartData[p_iUartHandle].iIsInternal == 1)
	{
		switch(p_iNbStopBits)
		{
			case UART_ONE_STOP_BIT:
			case UART_ONE_HALF_STOP_BIT:	//1˝ stop bits doesn't exist for internal uart
			{
				iStopbits = INT_UART_ONE_STOP_BIT;
				break;
			}	
			case UART_TWO_STOP_BITS:
			{
				iStopbits = INT_UART_TWO_STOP_BITS;
				break;
			}	
		}
		
		switch(p_iParityEnable)
		{
			case UART_NO_PARITY:
			{
				iParity = INT_UART_NO_PARITY;
				break;
			}
			case UART_EVEN_PARITY:
			{
				iParity = INT_UART_EVEN_PARITY;
				break;
			}
			case UART_ODD_PARTIY:
			{
				iParity = INT_UART_ODD_PARITY;
				break;
			}	
		}
		
		iRet = OpenInternalPort(astUartData[p_iUartHandle].iPhysicalUartPort,
								p_iUartHandle,
								&astUartData[p_iUartHandle].acTxCircularBuffer[0], 
								&astUartData[p_iUartHandle].acTxCircularBuffer[UART_MAX_TX_BUFF_SIZE-1],
								p_iBaudRate,
								iStopbits,
								iParity);
	}
#ifndef NO_EXTERNAL_UART
	else
	{
			switch(p_iNbStopBits)
		{
			case UART_ONE_STOP_BIT:
			{
				iStopbits = ONE_STOP_BIT;
				break;
			}	
			case UART_ONE_HALF_STOP_BIT:	//1˝ stop bits doesn't exist for internal uart
			{
				iStopbits = ONE_HALF_STOP_BIT;
				break;
			}	
			case UART_TWO_STOP_BITS:
			{
				iStopbits = TWO_STOP_BITS;
				break;
			}	
		}

		switch(p_iParityEnable)
		{
			case UART_NO_PARITY:
			{
				iParity = NO_PARITY;
				break;
			}
			case UART_EVEN_PARITY:
			{
				iParity = EVEN_PARITY;
				break;
			}
			case UART_ODD_PARTIY:
			{
				iParity = ODD_PARITY;
				break;
			}	
		}

		iRet = OpenExternalPort(astUartData[p_iUartHandle].iPhysicalUartPort,
								p_iUartHandle,
								&astUartData[p_iUartHandle].acTxCircularBuffer[0],
								&astUartData[p_iUartHandle].acTxCircularBuffer[UART_MAX_TX_BUFF_SIZE-1],
								p_iBaudRate,
								iStopbits,
								iParity);
	}		
#endif
	
	return iRet;
}	

//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//All the uart callback function assignment must be done here !!!
//This must be changed if hardware changes or if port assignment is changed !!!
//
int UartReceiveData(int p_iUartHandle, char *p_pcBuffer, int p_iSize)
{
	int i;
	stUartData *p_stUartDatPtr = &astUartData[p_iUartHandle];
	
	if(p_iUartHandle < 0  && p_iUartHandle >= MAX_UART_HANDLE)
	{
		//TODO: Flag a logic error !
		return 0;
	}
	
	for(i = 0; i < p_iSize; i++)
	{
		*p_stUartDatPtr->pcRxInDataPtr++ = *p_pcBuffer++;
		if(p_stUartDatPtr->pcRxInDataPtr > &p_stUartDatPtr->acRxCircularBuffer[UART_MAX_RX_BUFF_SIZE-1])
		{	
			p_stUartDatPtr->pcRxInDataPtr = &p_stUartDatPtr->acRxCircularBuffer[0];	//wrap pointer
//			printf("Wrap\n");
		}	
	}	
	p_stUartDatPtr->iNbRxFIFOPendingBytes += p_iSize;
	
	if(p_stUartDatPtr->iNbRxFIFOPendingBytes >= UART_MAX_RX_BUFF_SIZE)
		PRINTF("RX FIFO Overflow\n");
	
		
/*	switch(p_iUartHandle)
	{
		case UART_0:
		{
			break;
		}
		case UART_1:
		{
			break;
		}
		case UART_2:
		{
			break;
		}
		case UART_3:
		{
			break;
		}
		case UART_4:
		{
			break;
		}
		case UART_5:
		{
			break;
		}
	}	*/
	return 1;
}	

int UartGetPendingDataSize(int iUartHandle)
{
	stUartData *p_stUartDatPtr = &astUartData[iUartHandle];
	
	if(p_stUartDatPtr->pcRxInDataPtr == p_stUartDatPtr->pcRxOutDataPtr)
	{
		return 0;
	}
	else if(p_stUartDatPtr->pcRxOutDataPtr < p_stUartDatPtr->pcRxInDataPtr)
	{
		return(p_stUartDatPtr->pcRxInDataPtr - p_stUartDatPtr->pcRxOutDataPtr);
	}
	else
	{
		int size = &p_stUartDatPtr->acRxCircularBuffer[UART_MAX_RX_BUFF_SIZE-1] - p_stUartDatPtr->pcRxOutDataPtr;
		size += p_stUartDatPtr->pcRxInDataPtr - &p_stUartDatPtr->acRxCircularBuffer[0];
		return size;
	}		
}	

//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
int UartTransmitData(int p_iUartHandle, char *p_pcBuffer, int p_iSize)
{
	int iRet = 0;
	
	// Case where OutPtr < InPtr
	// * = Available space
	//
	//Top ---->|---------------|
	// 		   |       *       |
	//OutPtr ->|               |
	//		   |               |
	//		   |   (p_pcBuffer)    |	//Available size = (OutPtr - Top) + (Bottom - InPtr)
	//		   |               |
	//InPtr -->|               |
	//		   |       *       |
	//		   |       *       |
	//Bottom ->|---------------|
	
	
	// Case where OutPtr > InPtr
	// * = Available space
	//
	//Top ---->|---------------|
	// 		   |               |
	//InPtr -->|               |
	//		   |       *       |
	//		   |       *       |	//available size = OutPtr - InPtr
	//		   |       *       |
	//OutPtr ->|               |
	//		   |    (p_pcBuffer)   |
	//		   |               |
	//Bottom ->|---------------|
	
	
	//Check if data buffer fits in remaining circular buffer space
	if(astUartData[p_iUartHandle].pcTxOutPtr < astUartData[p_iUartHandle].pcTxInPtr)
	{
		//Calculate remaining buffer space and check if it's enough to fit requested data buffer
		if(p_iSize > ((astUartData[p_iUartHandle].pcTxOutPtr - &astUartData[p_iUartHandle].acTxCircularBuffer[0]) + 	//Outptr - Top
				   (&astUartData[p_iUartHandle].acTxCircularBuffer[UART_MAX_TX_BUFF_SIZE-1] - astUartData[p_iUartHandle].pcTxInPtr)))	//Bottom - Inptr
		{
			//Drop remaining packets, flush buffer
			astUartData[p_iUartHandle].iDataPending = 0;	
			astUartData[p_iUartHandle].pcTxInPtr = &astUartData[p_iUartHandle].acTxCircularBuffer[0];
			astUartData[p_iUartHandle].pcTxOutPtr = &astUartData[p_iUartHandle].acTxCircularBuffer[0];
			return UART_BUFFER_FULL;
		}
	}
	else if(astUartData[p_iUartHandle].pcTxInPtr < astUartData[p_iUartHandle].pcTxOutPtr)
	{
		//Calculate remaining buffer space and check if it's enough to fit requested data buffer
		if(p_iSize > (astUartData[p_iUartHandle].pcTxOutPtr - astUartData[p_iUartHandle].pcTxInPtr))
		{
			//Drop remaining packets, flush buffer
			astUartData[p_iUartHandle].iDataPending = 0;	//Force data pending flag to flush buffer
			astUartData[p_iUartHandle].pcTxInPtr = &astUartData[p_iUartHandle].acTxCircularBuffer[0];
			astUartData[p_iUartHandle].pcTxOutPtr = &astUartData[p_iUartHandle].acTxCircularBuffer[0];
			return UART_BUFFER_FULL;
		}
	}
	else	//OutPtr = InPtr
	{
		if(p_iSize > UART_MAX_TX_BUFF_SIZE - 1)	//message is too big to fit in buffer !!!
		{
			//Drop remaining packets, flush buffer
			astUartData[p_iUartHandle].iDataPending = 0;	//Force data pending flag to flush buffer
			astUartData[p_iUartHandle].pcTxInPtr = &astUartData[p_iUartHandle].acTxCircularBuffer[0];
			astUartData[p_iUartHandle].pcTxOutPtr = &astUartData[p_iUartHandle].acTxCircularBuffer[0];

			return UART_BUFFER_FULL;
		}	
	}
	
	int i;
	
	
	for(i = 0; i < p_iSize; i++)
	{
		*astUartData[p_iUartHandle].pcTxInPtr++ = *p_pcBuffer++;	//fill circular buffer

		if(astUartData[p_iUartHandle].pcTxInPtr > &astUartData[p_iUartHandle].acTxCircularBuffer[UART_MAX_TX_BUFF_SIZE-1])		//check for wrapping
		{	
			astUartData[p_iUartHandle].pcTxInPtr = &astUartData[p_iUartHandle].acTxCircularBuffer[0];
		}	
	}
	
	//Now flag pending data for transmission.
	astUartData[p_iUartHandle].iDataPending = 1;	//The data send will start on next Main loop processing


	return iRet;
}	

//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//Some data has been entirely sent
//Move output pointer
//
int DataSentNotification(int p_iUartHandle,int DataSize)
{
	//Check wrapping.
	if(astUartData[p_iUartHandle].pcTxOutPtr + DataSize < &astUartData[p_iUartHandle].acTxCircularBuffer[UART_MAX_TX_BUFF_SIZE])
	{
		astUartData[p_iUartHandle].pcTxOutPtr += DataSize;
	}
	else	//Pointer must wrap
	{

		//Top ------>|---------------|
		// 		     |      *        |
		//Final Pos->|               |	// Final Pos = Top + *
		//		     |       	     |
		//		     |   (buffer)    |	// DataSize = & + *
		//		     |               |	// & = Bottom - OutPtr
		//OutPtr --->|               |	// * = DataSize - &
		//	   	     |       &       |  // FinalPos = Top + (DataSize - (Bottom - OutPtr))
		//		     |       &       |
		//Bottom --->|---------------|
		
//		if(p_iUartHandle != PRINTF_UART_PORT)
//			printf("Out wrap\r");

		DataSize -= (&astUartData[p_iUartHandle].acTxCircularBuffer[UART_MAX_TX_BUFF_SIZE] - astUartData[p_iUartHandle].pcTxOutPtr); //p_iSize - (Bottom - OutPtr) 
		
		astUartData[p_iUartHandle].pcTxOutPtr = &astUartData[p_iUartHandle].acTxCircularBuffer[0] + DataSize;	//Top + new p_iSize
		

	}

	//Check if data is pending in buffer
	if(astUartData[p_iUartHandle].pcTxOutPtr != astUartData[p_iUartHandle].pcTxInPtr)
	{
		astUartData[p_iUartHandle].iDataPending = 1;
	}		
	else
	{
		astUartData[p_iUartHandle].iDataPending = 0;
	}

	return UART_OK;
}

//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//Called by main loop.  Check if any uart has data pending to be sent
//
int UartTick(void)
{
	int i = 0;
	int iRet;
	int p_iSize;
	char aTempBuffer[UART_MAX_RX_BUFF_SIZE];
	
	TickInternalUart();
	
	//Send...
	
	for(i = 0; i < MAX_UART_HANDLE; i++)
	{
		if(astUartData[i].iDataPending)
		{
			char *pcTxInPtr = astUartData[i].pcTxInPtr;
			//Check remaining data size.
			if(astUartData[i].pcTxOutPtr < pcTxInPtr)
			{
				p_iSize = pcTxInPtr - astUartData[i].pcTxOutPtr;
			}
			else
			{
				p_iSize = ((&astUartData[i].acTxCircularBuffer[UART_MAX_TX_BUFF_SIZE] - astUartData[i].pcTxOutPtr) + 
						 (pcTxInPtr - &astUartData[i].acTxCircularBuffer[0]));
			}

			if(astUartData[i].iIsInternal)	//Uart handle mapped to PIC internal uart
			{
				astUartData[i].iDataPending = 0;
				iRet = SendInternalUartData(astUartData[i].pcTxOutPtr,
											p_iSize,
											astUartData[i].iPhysicalUartPort,
											&astUartData[i].acTxCircularBuffer[0],
											&astUartData[i].acTxCircularBuffer[UART_MAX_TX_BUFF_SIZE-1]);
			}
			
#ifndef NO_EXTERNAL_UART			
			else	//mapped to external uart
			{
				astUartData[i].iDataPending = 0;
				iRet = SendExternalUartData(astUartData[i].iPhysicalUartPort, astUartData[i].pcTxOutPtr, p_iSize,&astUartData[i].acTxCircularBuffer[0], &astUartData[i].acTxCircularBuffer[UART_MAX_TX_BUFF_SIZE-1]);
			}
#endif
			
			//TODO: manage return values
			switch(iRet)
			{
				case UART_OK:
				{
					break;
				}
				case UART_PORT_BUSY:
				case UART_BUFFER_FULL:
				{
					astUartData[i].iDataPending = 1;
					break;
				}
				case UART_PORT_NOT_OPENED:
				{
					break;
				}		
			}
		}
		
		
		//Receive...
		
	//	int iNbPendingData = astUartData[i].iNbRxFIFOPendingBytes;		//JFM 2012-09-05
		int iNbPendingData = UartGetPendingDataSize(i);
		if(iNbPendingData > 0)
		{
			int byte;
			for(byte = 0; byte < iNbPendingData; byte++)
			{
				aTempBuffer[byte] = *astUartData[i].pcRxOutDataPtr++;
				if(astUartData[i].pcRxOutDataPtr > &astUartData[i].acRxCircularBuffer[UART_MAX_RX_BUFF_SIZE-1])	
					astUartData[i].pcRxOutDataPtr = &astUartData[i].acRxCircularBuffer[0];	//wrap pointer
			}	
			astUartData[i].iNbRxFIFOPendingBytes -= iNbPendingData;
			
			switch(i)
			{
//				case DRIVE_UART_PORT:
//				{
//					//printf("drive rx\n");
//					DriveProtocolRxData(aTempBuffer,iNbPendingData);
//					break;
//				}
//				case CU_UART_PORT:
//				{
//					CUProtocolRxData(aTempBuffer,iNbPendingData); // HCAM 20120918
//					break;
//				}
				case NETWORK_UART_PORT:
				{
					//HEARTBEAT_LED_1_TOGGLE_REG = HEARTBEAT_LED_1_TOGGLE_MASK;
					//UartTransmitData(CONSOLE_UART_PORT, aTempBuffer, iNbPendingData);//echo received character
					int i = 0;
					for(i = 0; i< iNbPendingData; i++)
					{
						ProtocolAnalyzeNewData(aTempBuffer[i]);
						//RxTerminalData(aTempBuffer[i]);
					}
					break;
				}	
                case LORA_MODULE_UART_PORT:
				{
					//HEARTBEAT_LED_1_TOGGLE_REG = HEARTBEAT_LED_1_TOGGLE_MASK;
					//UartTransmitData(CONSOLE_UART_PORT, aTempBuffer, iNbPendingData);//echo received character
					int i = 0;
					for(i = 0; i< iNbPendingData; i++)
					{
						LoraInterfaceRxData(aTempBuffer[i]);
					}
					break;
				}	
                
			}	
		}	
	}
	
	return UART_OK;
}
//-----------------------------------------------------------------------------
//
// This function is used in the case of a large amount of data to be sent or received
// in a process without going back to main loop.

void UartBlockAndTick(unsigned int TickCount)
{
	int i;
	
//	KickWatchdog();
	for(i = 0; i < TickCount; i++)
	{
		UartTick();
	}
//	KickWatchdog();
}	
//-----------------------------------------------------------------------------

//-----------------------------------------------------------------------------
//
// This function is used in the case of a large amount of data to be sent or received
// in a process without going back to main loop.
int UartBlockUntillBufEmpty(int iUartHandle)
{
	if(iUartHandle >= MAX_UART_HANDLE)
		return 0;
		
//	KickWatchdog();
	while(astUartData[iUartHandle].iDataPending)
	{
		UartTick();
	}
//	KickWatchdog();
	return 1;
	
}	
//-----------------------------------------------------------------------------

//-----------------------------------------------------------------------------	
//Overload the stdio character output routine to redirect printfs
//
//void _mon_putc(char c)
//{
//	KickWatchdog();

//    U2TXREG = c;
//    while (U2STAbits.TRMT==0);
    
	
//#ifdef USE_BLOCKING_PRINTF
//	switch(CONSOLE_UART_PORT)
//	{	
//		case UART_1:		//(422)	Uart 1B
//		{
//			if(U1MODEbits.ON == 1)
//			{
//				U1TXREG = c;
//				while (U1STAbits.TRMT==0);
//			}
//			break;
//		}
//		case UART_2:		//(422)	Uart 2A
//		{
//			if(U2MODEbits.ON == 1)
//			{
//				U2TXREG = c;
//				while (U2STAbits.TRMT==0);
//			}
//			break;
//		}
		
//	}		
//#else		
//	UartTransmitData(PRINTF_UART_PORT, &c, 1);
//#endif
//	UartTransmitData(CONSOLE_UART_PORT, &c, 1);
//    Nop();

//	KickWatchdog();
//}	

/*
void _mon_write (const char * s, unsigned int count)
{
#ifdef USE_BLOCKING_PRINTF
	int i;
	for(i = 0; i < count; i++)
		_mon_putc(*s++);
#else		
	UartTransmitData(UART_10, s, count);
#endif
}*/	

//EOF

