/*******************************************************************************
 *                                                                              *
 * 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.              *
 *                                                                              *
 *******************************************************************************/


/*---- example include file ---------------------------------------------------*/
#include "define.h"
#include "BootloaderStateMachine.h"
#include "FlashInterface.h"
#include "BootloaderProtocol.h"
#include "InternalUart.h"
#include "BootloaderInterface.h"
#include "FlashMapping.h"
#include <plib.h>
//#include <string.h>
//#include <stdio.h>


unsigned int mSMState;
unsigned int mPseudoTimer;
extern unsigned long _app_start;
int mIsSPIFlashDetected;





void BootloaderSMInit(void)
{
	mSMState = SM_CHECK_BOOTLOADER_FLAGS;
	mPseudoTimer = 0;
	mIsSPIFlashDetected = 0;
}

//#pragma CODE_SECTION(BootloaderSM,"ramfuncs");
void BootloaderSM(unsigned int event, unsigned int Data)
{
	switch(mSMState)
	{
		case SM_STANDBY_STATE:
		{
			break;
		}
		case SM_CHECK_BOOTLOADER_FLAGS:
		{
			switch(event)
			{
				case SM_TICK_EVENT:
				{
					//First, check if SPI flash is functionnal
					if(mIsSPIFlashDetected == 0)
					{
						//Flash is not functionnal...
						//TODO: Check if we need to update via serial port
						
						//	JumpToApp();
					}
					
					//Flash is detected.  Check the bootloader flags...
					BoootloaderGetBootloaderFlags();
					
//					if(BootloaderAction == 0)
//					{
//						JumpToApp();
//					}
//					else if(BootloaderAction == 1)
//					{
//						if( BootloaderAction == 0xA8)
//						{
//							//It's requested to write the firmware to PIC's Flash.  Let's check SPI flash data integrity.
//							mSMState = SM_CHECK_FIRMWARE_VALIDITY;
//						}
//						else
//						{
//							//TODO: What should we do?
//							JumpToApp();
//						}
//					}
//					else
//					{
//						//TODO
//						JumpToApp();
//					}
					
					
				
					switch(mBootloaderFlags.BootloaderAction)
					{
						case BOOTLOADER_ACTION_JUMP_TO_APP_VALUE:
						{
							char mbuf[30];
							mbuf[0] = 'G';
							mbuf[1] = 'o';
							mbuf[2] = 'i';
							mbuf[3] = 'n';
							mbuf[4] = 'g';
							mbuf[5] = ' ';
							mbuf[6] = 'S';
							mbuf[7] = 't';
							mbuf[8] = 'd';
							mbuf[9] = 'b';
							mbuf[10] = 'y';
							mbuf[11] = '\n';
							mbuf[12] = '\r';
							SendInternalUartData(&mbuf[0],13);
							mSMState = SM_STANDBY_STATE;
							JumpToApp();
							break;
						}
						case BOOTLOADER_ACTION_FLASH_FIRMWARE_VALUE:
						{
							if( mBootloaderFlags.BootloaderActionValidator == BOOTLOADER_FLASH_FIRMWARE_VALIDATOR)
							{
								//It's requested to write the firmware to PIC's Flash.  Let's check SPI flash data integrity.
								mSMState = SM_CHECK_FIRMWARE_VALIDITY;
							}
							else
							{
								//TODO: What should we do?
								JumpToApp();
							}
							break;
						}
						case BOOTLOADER_ACTION_ENTER_UPDATE_MODE_VALUE:
						{
							//TODO
							JumpToApp();
							break;
						}
						default:
						{
							JumpToApp();
							break;
						}
					}
//					
					break;
				}
				case SM_RX_CMD_EVENT:
				case SM_ABORT_UPDATE:
				default:
				{
					break;
				}
			}
			break;
		}
		case SM_CHECK_FIRMWARE_VALIDITY:
		{
			switch(event)
			{
				case SM_TICK_EVENT:
				{
					if(BootloaderCheckFlashBootloaderData() == RET_OK)
					{
						//All is good, let's write the firmware to PIC's program Flash
						mSMState = SM_ERASE_INTERNAL_FLASH;						
					}
					else
					{
						//TODO: What should we do?
						JumpToApp();
					}
					break;
				}
				default:
				{
					break;
				}
			}
			break;
		}
		case SM_ERASE_INTERNAL_FLASH:
		{
			switch(event)
			{
				case SM_TICK_EVENT:
				{
					if(FlashErase() == FLASH_STATUS_ERROR)
					{
						char mbuf[30];
						mbuf[0] = 'F';
						mbuf[1] = 'l';
						mbuf[2] = 'a';
						mbuf[3] = 's';
						mbuf[4] = 'h';
						mbuf[5] = ' ';
						mbuf[6] = 'E';
						mbuf[7] = 'r';
						mbuf[8] = 'r';
						mbuf[9] = 'o';
						mbuf[10] = 'r';
						mbuf[11] = '\n';
						mbuf[12] = '\r';
						SendInternalUartData(&mbuf[0],13);
						mSMState = SM_STANDBY_STATE;
						break;
					}
					else
					{
//						mSMState = SM_STANDBY_STATE;
						char mbuf[30];
						mbuf[0] = 'F';
						mbuf[1] = 'l';
						mbuf[2] = 'a';
						mbuf[3] = 's';
						mbuf[4] = 'h';
						mbuf[5] = ' ';
						mbuf[6] = 'E';
						mbuf[7] = 'r';
						mbuf[8] = 'a';
						mbuf[9] = 's';
						mbuf[10] = 'e';
						mbuf[11] = 'd';
						mbuf[12] = '\n';
						mbuf[13] = '\r';
						SendInternalUartData(&mbuf[0],14);
						mSMState = SM_WRITE_DATA_TO_PROGRAM_FLASH;
					}
					break;
				}
				default:
				{
					break;
				}
			}
			break;
		}
		case SM_WRITE_DATA_TO_PROGRAM_FLASH:
		{
			switch(event)
			{
				case SM_TICK_EVENT:
				{
					//First, load the firmware info.
					unsigned char FlashData[100];
					unsigned int FlashAddress = FLASH_BTLDR_FIRMWARE_START_ADDRESS;
					unsigned int FileHeaderCode, FirmwareFlags, NbRecords, FirmwareSize, VersionCode, CRC32;
					//		unsigned int ComputedCRC32 = CRC_START_32;
					
					//SPIFlashReadBuffer(FlashData,700,FlashAddress);
					
					
					//printf("Checking Flash bootloader data integrity... \n");
					SPIFlashReadBuffer(FlashData,FLASH_BTLDR_HEADER_SIZE,FlashAddress);
					
					FileHeaderCode = BootloaderBytesToInt(FlashData);
					FirmwareFlags = BootloaderBytesToInt(&FlashData[4]);
					NbRecords = BootloaderBytesToInt(&FlashData[8]);
					FirmwareSize = BootloaderBytesToInt(&FlashData[12]);
					VersionCode = BootloaderBytesToInt(&FlashData[16]);
					CRC32 = BootloaderBytesToInt(&FlashData[20]);
					
					//TODO: Do we need to recheck validity?  It's just been done before...
					
					FlashAddress += FLASH_BTLDR_HEADER_SIZE;	//point to the start of bootloader data
					
					if(NbRecords > 0)
					{
						int CurRecord = 0;
						unsigned int Done = false;
						unsigned int RecHeader, RecSize, RecStartAddress;
						while(Done == false)
						{
							SPIFlashReadBuffer(&FlashData[0],12,FlashAddress);
							RecHeader = BootloaderBytesToInt(&FlashData[0]);
							RecSize = BootloaderBytesToInt(&FlashData[4]);		//This is in bytes
							RecSize /= 4;
							RecStartAddress = BootloaderBytesToInt(&FlashData[8]);
							FlashAddress += 12;
							
							
							if(RecHeader != BOOTLOADER_RECORD_HEADER_CODE)
							{
								//TODO: What
							}
							//					if(RecSize == 0)
							//					{
							//TODO: What
							//						return RET_ERROR;
							//					}
							
							//Load the firmware data from SPI Flash
							CurrentDataSector.SectorStartAddress = RecStartAddress;
							CurrentDataSector.SectorSize = RecSize;
							int RecWord;
							for(RecWord = 0; RecWord < RecSize; RecWord++)
							{
								char WordBytes[4];
								int Word;
								SPIFlashReadBuffer(WordBytes,4,FlashAddress);
//								Word = BootloaderBytesToInt(WordBytes);
								Word = BootloaderBytesToIntLittleEndian(WordBytes);
								CurrentDataSector.SectorData[RecWord] = Word;
								FlashAddress += 4;
							}
							
							
							//Write it to PIC's internal flash
							if(CurrentDataSector.SectorStartAddress < KVA_TO_PA(APPLICATION_FLASH_START_ADDRESS))
							{
								//TODO: Manage this!
							}	
							else if(FlashProgramData((void*)(CurrentDataSector.SectorStartAddress), CurrentDataSector.SectorData , CurrentDataSector.SectorSize) != FLASH_STATUS_OK)
							{
								//TODO: Manage this!
								char mbuf[30];
								mbuf[0] = 'W';
								mbuf[1] = 'r';
								mbuf[2] = 'i';
								mbuf[3] = 't';
								mbuf[4] = 'e';
								mbuf[5] = ' ';
								mbuf[6] = 'E';
								mbuf[7] = 'r';
								mbuf[8] = 'r';
								mbuf[9] = 'o';
								mbuf[10] = 'r';
								mbuf[11] = '\n';
								mbuf[12] = '\r';
								SendInternalUartData(&mbuf[0],13);
							}
							
							
							CurRecord++;
							if(CurRecord == NbRecords)
							{
								Done = true;
								break;
							}
						}
					}
					
					//Fimware is all written successfully
					mSMState = SM_CHECK_PROGRAM_FLASH_VALIDITY;
					
					break;
				}
				default:
				{
					break;
				}
				
				//			CurrentDataSector.SectorStartAddress
				break;
			}
			case SM_CHECK_PROGRAM_FLASH_VALIDITY:
			{
				switch(event)
				{
					case SM_TICK_EVENT:
					{
						//TODO: Compute CRC32...
						
						mSMState = SM_STANDBY_STATE;
						char mbuf[30];
						mbuf[0] = 'F';
						mbuf[1] = 'l';
						mbuf[2] = 'a';
						mbuf[3] = 's';
						mbuf[4] = 'h';
						mbuf[5] = ' ';
						mbuf[6] = 'P';
						mbuf[7] = 'r';
						mbuf[8] = 'o';
						mbuf[9] = 'g';
						mbuf[10] = 'r';
						mbuf[11] = 'a';
						mbuf[12] = 'm';
						mbuf[13] = 'm';
						mbuf[14] = 'e';
						mbuf[15] = 'd';
						mbuf[16] = '\n';
						mbuf[17] = '\r';
						SendInternalUartData(&mbuf[0],19);

						char toto[2];
						toto[0] = 0;
						toto[1] = 0;
						SPIFlashWriteBuffer(toto,2,0x000000);
						JumpToApp();
					}
						break;
					}
					default:
					{
						break;
					}
				}
				break;
			}
			case SM_ERROR_STATE:
			{
				//				TODO: Manage this state
				break;
			}
		}
	}
	//void BootloaderSM(unsigned int event, unsigned int Data)
	//{
	//	switch(mSMState)
	//	{
	//		case SM_UPDATE_WAIT_STATE:
	//		{
	//			switch(event)
	//			{
	//				case SM_TICK_EVENT:
	//				{
	//					if(mPseudoTimer++ >= PSEUDO_TIMER_VALUE)
	//					{
	//						char mbuf[30];
	//						mbuf[0] = 'R';
	//						mbuf[1] = 'e';
	//						mbuf[2] = 'b';
	//						mbuf[3] = 'o';
	//						mbuf[4] = 'o';
	//						mbuf[5] = 't';
	//						mbuf[6] = 'i';
	//						mbuf[7] = 'n';
	//						mbuf[8] = 'g';
	//						mbuf[9] = ' ';
	//						mbuf[10] = 'i';
	//						mbuf[11] = 'n';
	//						mbuf[12] = ' ';
	//						mbuf[13] = 'a';
	//						mbuf[14] = 'p';
	//						mbuf[15] = 'p';
	//						mbuf[16] = '\r';
	//						SendInternalUartData(&mbuf[0],17);
	//						mPseudoTimer = 0;
	////					#ifndef _DEBUG
	////						JumpToApp();
	////					#endif
	//					}
	//					break;
	//				}
	//				case SM_RX_CMD_EVENT:
	//				{
	//					if(Data == CONNECT_REQUEST_CMD)
	//					{
	//						mSMState = SM_IDLE_STATE;
	//						ProtocolSendAck(PROTOCOL_ACK,RET_PROTOCOL_OK);	//Bootloader ack
	//					}
	//
	//					//Ignore any other command received...
	//
	//					break;
	//				}
	//				case SM_ABORT_UPDATE:
	//				{
	//					break;
	//				}
	//			}
	//			break;	//case SM_UPDATE_WAIT_STATE:
	//		}
	//		case SM_IDLE_STATE:
	//		{
	//			switch(event)
	//			{
	//				case SM_TICK_EVENT:
	//				{					
	//					break;
	//				}
	//				case SM_RX_CMD_EVENT:
	//				{
	//					switch(Data)
	//					{
	//						case START_UPDATE_REQUEST_CMD:
	//						{
	//							mSMState = SM_WAIT_FOR_UNLOCK_STATE;
	//							ProtocolSendAck(PROTOCOL_ACK,RET_PROTOCOL_OK);	//Bootloader ack
	//							break;
	//						}
	//						case HEARTBEAT_REQUEST_CMD:
	//						{
	//							ProtocolSendCmd(HEARTBEAT_CMD,0,0);
	//							break;
	//						}
	//						case END_UPDATE_REQUEST_CMD:
	//						{
	//							ProtocolSendAck(PROTOCOL_ACK,RET_PROTOCOL_OK);	//Bootloader ack
	//							SoftReset();	//simply reset cpu.
	//							break;
	//						}
	//						case FLASH_API_VER_REQUEST_CMD:
	//						{
	//							char Version;
	//							Version = 0x01;
	//							ProtocolSendCmd(FLASH_API_VER_CMD,&Version,1);
	//							break;
	//						}
	//						case SCRUB_FLASH_REQUEST_CMD:
	//						{
	//							//TODO
	//							break;
	//						}
	//					}
	//					break;
	//				}
	//				case SM_ABORT_UPDATE:
	//				{
	//					ProtocolSendAck(PROTOCOL_ACK,RET_PROTOCOL_OK);	//Bootloader ack
	//					SoftReset();
	//					break;
	//				}
	//			}
	//			break;	//case SM_IDLE_STATE:
	//		}
	//		case SM_WAIT_FOR_UNLOCK_STATE:
	//		{
	//			switch(event)
	//			{
	//				case SM_TICK_EVENT:
	//				{
	//					break;
	//				}
	//				case SM_RX_CMD_EVENT:
	//				{
	//					if(Data == BOOTLOADER_UNLOCK_REQUEST_CMD)
	//					{
	//						if(RxDataBuffer[0] == BOOTLOADER_UNLOCK_CODE)
	//						{
	//							ProtocolSendAck(PROTOCOL_ACK,RET_PROTOCOL_FLASH_ERASE);	//Inform PTE that flash is being erased
	//
	//					//		#ifndef _DEBUG		//don't touch FLASH in debug mode...
	//								//Erase FLASH
	//								if(FlashErase() != FLASH_STATUS_OK)
	//								{
	//									ProtocolSendAck(PROTOCOL_NACK,RET_PROTOCOL_ERR_ERASE_FLASH_FAILED);	//Acknowledge...
	//									mSMState = SM_IDLE_STATE;
	//									break;
	//								}	
	//					//		#endif
	//							
	//							mSMState = SM_WAIT_FOR_NEXT_RECORD;							
	//							ProtocolSendAck(PROTOCOL_ACK,RET_PROTOCOL_OK);
	//						}
	//						else//Invalid unlock code... return to IDLE...
	//						{
	//						  	mSMState = SM_IDLE_STATE;
	//					 		ProtocolSendAck(PROTOCOL_NACK,RET_PROTOCOL_ERR_INVALID_UNLOCK_CODE);
	//						}
	//					}
	//					else//If another command than UNLOCK is received... return to IDLE
	//					{
	//						mSMState = SM_IDLE_STATE;
	//					 	ProtocolSendAck(PROTOCOL_NACK,RET_PROTOCOL_ERR_UNLOCK_EXPECTED);
	//					
	//					}
	//
	//					break;	//case SM_RX_CMD_EVENT
	//				}
	//				case SM_ABORT_UPDATE:
	//				{
	//					break;
	//				}
	//			}
	//			break;	//case SM_WAIT_FOR_UNLOCK_STATE:
	//		}
	//		case SM_WAIT_FOR_NEXT_RECORD:
	//		{
	//			switch(event)
	//			{
	//				case SM_TICK_EVENT:
	//				{
	//					break;
	//				}
	//				case SM_RX_CMD_EVENT:
	//				{
	//					switch(Data)
	//					{
	//						case SEND_RECORD_REQUEST_CMD:
	//						{
	//							//TODO: Address check...
	//							ProtocolSendAck(PROTOCOL_ACK,RET_PROTOCOL_FLASH_WRITE);
	//							
	////							unsigned int SectorStartAddress;
	////							unsigned short SectorSize;
	////							unsigned int *RecordDataPtr;
	////							unsigned int data;
	////							 
	////							SectorStartAddress = *(unsigned int*)(&RxDataBuffer[0]);
	////							SectorSize = *(unsigned short*)(&RxDataBuffer[4]);
	////							RecordDataPtr = (unsigned int*)&RxDataBuffer[8];
	////							data = *RecordDataPtr;
	//
	//	//						unsigned int test = KVA_TO_PA(APPLICATION_START_ADDRESS);
	//
	//							if(CurrentDataSector.SectorStartAddress < KVA_TO_PA(APPLICATION_FLASH_START_ADDRESS))
	//							{
	//								ProtocolSendAck(PROTOCOL_NACK,RET_PROTOCOL_ERR_INVALID_ADDRESS);
	//								break;
	//							}	
	//							else if(FlashProgramData((void*)(CurrentDataSector.SectorStartAddress), CurrentDataSector.SectorData , CurrentDataSector.SectorSize) != FLASH_STATUS_OK)
	//							{
	//								ProtocolSendAck(PROTOCOL_NACK,RET_PROTOCOL_ERR_WRITE_FLASH_FAILED);
	//							   	break;	
	//							}
	//
	// 
	//							ProtocolSendAck(PROTOCOL_ACK,RET_PROTOCOL_OK);
	//
	//
	//							break;
	//						}
	//						case END_UPDATE_REQUEST_CMD:
	//						{
	//							ProtocolSendAck(PROTOCOL_ACK,RET_PROTOCOL_OK);
	//
	//							#ifndef _DEBUG
	//								JumpToApp();
	//							#else
	//								mSMState = SM_IDLE_STATE;
	//							#endif
	//							break;
	//						}
	//
	//					}
	//
	//					break;	//case SM_RX_CMD_EVENT:
	//				}
	//				case SM_ABORT_UPDATE:
	//				{
	//					break;
	//				}
	//			}
	//			break;	//case SM_WAIT_FOR_NEXT_RECORD:
	//		}
	//	}
	//}
	
	void JumpToApp()
	{	
		//	CloseInternalUart();
		char mbuf[30];
		mbuf[0] = 'R';
		mbuf[1] = 'e';
		mbuf[2] = 'b';
		mbuf[3] = 'o';
		mbuf[4] = 'o';
		mbuf[5] = 't';
		mbuf[6] = 'i';
		mbuf[7] = 'n';
		mbuf[8] = 'g';
		mbuf[9] = ' ';
		mbuf[10] = 'i';
		mbuf[11] = 'n';
		mbuf[12] = ' ';
		mbuf[13] = 'a';
		mbuf[14] = 'p';
		mbuf[15] = 'p';
		mbuf[16] = '\r';
		SendInternalUartData(&mbuf[0],17);
		void (*fptr)(void);
		fptr = (void (*)(void))APPLICATION_START_ADDRESS;
		fptr();
	}	
	
	void BootloaderSMSetSPIFlashDetected(int Detected)
	{
		mIsSPIFlashDetected = Detected;
	}
	
	void BootloaderSMReadBootloaderFlags()
	{
		
	}
	
	void DownloadFlash(unsigned int StartAddress, unsigned int EndAddress)
	{
		/*
		 unsigned int CurAddress = StartAddress;
		 unsigned int Data;
		 unsigned int delay = 0;
		 char Buf[100];
		 int i = 0;
		 
		 while(CurAddress < EndAddress)
		 {
		 Data = (CurAddress >> 16);
		 sprintf(Buf, "0x%.4X",Data);
		 SendInternalUartData(Buf,strlen(Buf));
		 //		ProtocolSendCmd(FLASH_DATA_CMD,Buf,strlen(Buf));
		 Data = CurAddress;
		 sprintf(Buf, "%.4X   ",Data);
		 SendInternalUartData(Buf,strlen(Buf));
		 //		ProtocolSendCmd(FLASH_DATA_CMD,Buf,strlen(Buf));
		 
		 for(i = 0; i < 16; i++)
		 {
		 Data = *(unsigned int*)CurAddress;
		 if(i != 15)
		 sprintf(Buf, "%.4X ",Data);
		 else
		 sprintf(Buf, "%.4X \r",Data);
		 
		 SendInternalUartData(Buf,strlen(Buf));
		 //	ProtocolSendCmd(FLASH_DATA_CMD,Buf,strlen(Buf));
		 CurAddress++;
		 //	while(delay++ < 5000);
		 
		 delay = 0;
		 }
		 
		 
		 }
		 e_FlashUpdateMode = false;
		 
		 */
		
	}
	
	
	//EOf
	
	
