/**********************************************************************
 Project:    Automatic cat feeder
 Date:       march 19 2006
 Author:     Jean-Fran�ois Martel
 Target:     PIC 18F252
 Compiler:   Microchip mcc18
 Filename:	Protocol.c
 
 File description:	Communication protocol implementation.
 
 
 jean-francois.martel@polymtl.ca
 **********************************************************************/
#include "define.h"
#include <string.h>
#include "ProtocolDefs.h"
#include "BootloaderProtocol.h"
#include "BootloaderInterface.h"
#include "WiFiCtrl.h"
#include "checksum.h"


//test
unsigned int BootloaderHeader = 0;
unsigned int BootloaderDataSize = 0;
unsigned int BootloaderDataCtr = 0;
unsigned int BootloaderBufPtr = 0;
unsigned int BootloaderCRC = 0;
unsigned int BtldrComputedCRC = CRC_START_32;
unsigned char *BootloaderRxPtr;
unsigned char BootloaderCommand = 0;
unsigned char BootloaderState = RxHeader1;
const unsigned char *BootloaderDataStartPtr = &BootloaderBuffer[9];


static char MyDeviceID = ID_SPRINKLER_DEVICE;

void BootloaderProtocolInit(void)
{
	BootloaderProtocolResetStateMachine();
	
}

void BootloaderProtocolStateMachine(unsigned char Data)
{
	switch(BootloaderState)
	{
		case Initialization:	//Reset all pointers and data...
		{
			BootloaderDataSize = 0;
			BootloaderBufPtr = 0;
			BootloaderCommand = 0;
			BootloaderCRC = 0;
			BootloaderState = RxHeader1;
			BtldrComputedCRC = CRC_START_32;
			break;
		}
		case RxHeader1:	//Wait for data header...
		{
			BootloaderHeader <<= 8;
			BootloaderHeader += Data;	//0xDE
			BtldrComputedCRC = update_crc_32(BtldrComputedCRC,Data);
			
			if(Data == BOOTLOADER_FRAME_HEADER_1)
			{
				BootloaderState = RxHeader2;
			}
			else
			{
				BootloaderProtocolResetStateMachine();
			}
			break;	
		}
		case RxHeader2:	//Wait for data header...
		{
			BootloaderHeader <<= 8;
			BootloaderHeader += Data;	//0xAD
			BtldrComputedCRC = update_crc_32(BtldrComputedCRC,Data);
			
			if(Data == BOOTLOADER_FRAME_HEADER_2)
			{
				BootloaderState = RxHeader3;
			}
			else
			{
				BootloaderProtocolResetStateMachine();
			}
			break;	
		}
		case RxHeader3:	//Wait for data header...
		{
			BootloaderHeader <<= 8;
			BootloaderHeader += Data;	//0xBE
			BtldrComputedCRC = update_crc_32(BtldrComputedCRC,Data);
			
			if(Data == BOOTLOADER_FRAME_HEADER_3)
			{
				BootloaderState = RxHeader4;
			}
			else
			{
				BootloaderProtocolResetStateMachine();
			}
			break;	
		}
		case RxHeader4:	//Wait for data header...
		{
			BootloaderHeader <<= 8;
			BootloaderHeader += Data;	//0xEF
			BtldrComputedCRC = update_crc_32(BtldrComputedCRC,Data);
			
			if(BootloaderHeader != BOOTLOADER_FRAME_HEADER)
			{
				//TODO, send NACK?
				BootloaderProtocolResetStateMachine();
				break;
			}
			else
			{
				BootloaderState = RxCmd;
			}
			
			break;	
		}
		case RxCmd:
		{
			BootloaderCommand = Data;
			BootloaderState = RxPayloadSize1;
			BtldrComputedCRC = update_crc_32(BtldrComputedCRC,Data);
			break;
		}
		case RxPayloadSize1:
		{
			BootloaderDataSize = Data;
			BootloaderState = RxPayloadSize2;
			BtldrComputedCRC = update_crc_32(BtldrComputedCRC,Data);
			break;	
		}
		case RxPayloadSize2:
		{
			BootloaderDataSize <<= 8;
			BootloaderDataSize += Data;
			BtldrComputedCRC = update_crc_32(BtldrComputedCRC,Data);
			
			BootloaderState = RxPayloadSize3;
			break;	
		}
		case RxPayloadSize3:
		{
			BootloaderDataSize <<= 8;
			BootloaderDataSize += Data;
			BtldrComputedCRC = update_crc_32(BtldrComputedCRC,Data);
			
			BootloaderState = RxPayloadSize4;
			break;	
		}
		case RxPayloadSize4:
		{
			BootloaderDataSize <<= 8;
			BootloaderDataSize += Data;
			BtldrComputedCRC = update_crc_32(BtldrComputedCRC,Data);
			
			if(BootloaderDataSize > MAX_BOOTLOADER_PAYLOAD_SIZE+8)	//+8 bytes for the size and index data
			{
				//TODO, send NACK?
				BootloaderProtocolResetStateMachine();
				break;
			}
			
			if(BootloaderDataSize == 0)
			{
				BootloaderState = RxCRC1;
			}
			else
			{
				BootloaderState = RxPayload;
			}
			break;	
		}
		case RxPayload:	//Data size
		{
			*BootloaderRxPtr = Data;
			BootloaderRxPtr++;
			
			BootloaderDataCtr++;
			BtldrComputedCRC = update_crc_32(BtldrComputedCRC,Data);
			
			if(BootloaderDataCtr == BootloaderDataSize)
			{
				BootloaderState = RxCRC1;
				break;
			}
			
			break;	
		}
		case RxCRC1:	//Data size
		{
			BootloaderCRC = Data;
			BootloaderState = RxCRC2;
			break;	
		}
		case RxCRC2:	//Data size
		{
			BootloaderCRC <<= 8;
			BootloaderCRC += Data;
			BootloaderState = RxCRC3;
			break;	
		}
		case RxCRC3:	//Data size
		{
			BootloaderCRC <<= 8;
			BootloaderCRC += Data;
			BootloaderState = RxCRC4;
			break;	
		}
		case RxCRC4:	//Data size
		{
			BootloaderCRC <<= 8;
			BootloaderCRC += Data;
			
			//TODO: Compute and Compare CRC.
			
			BtldrComputedCRC ^= 0xffffffffL;
			//if(BootloaderCRC != 0xBAADCAFE)
			if(BootloaderCRC != BtldrComputedCRC)
			{
				BootloaderExecuteCmd(BootloaderCommand,0);
				BootloaderProtocolResetStateMachine();
				return;
			}
			
			BootloaderExecuteCmd(BootloaderCommand,1);
			BootloaderProtocolResetStateMachine();
			break;	
		}
		default:
		{
			BootloaderProtocolResetStateMachine();
			break;
		}		
	}	
}

void BootloaderProtocolProtocolAnalyzeNewData(unsigned char *DataBuf, int size)
{
	int i;
	for(i = 0; i < size; i++)
	{
		BootloaderProtocolStateMachine(*DataBuf++);
	}
}

void BootloaderProtocolResetStateMachine()
{
	BootloaderDataSize = 0;
	BootloaderHeader = 0;
	BootloaderBufPtr = 0;
	BootloaderCommand = 0;
	BootloaderCRC = 0;
	BootloaderState = RxHeader1;
	BootloaderDataCtr = 0;
	BtldrComputedCRC = CRC_START_32;
	BootloaderRxPtr = &BootloaderBuffer[0];
}

void BootloaderProtocolSendFrame(unsigned char Cmd, int Size)
{
	//Header
	BootloaderBuffer[0] = BOOTLOADER_FRAME_HEADER_1;	//Header
	BootloaderBuffer[1] = BOOTLOADER_FRAME_HEADER_2;
	BootloaderBuffer[2] = BOOTLOADER_FRAME_HEADER_3;
	BootloaderBuffer[3] = BOOTLOADER_FRAME_HEADER_4;
	
	BootloaderBuffer[4] = Cmd;
	
    char nibble = (char)((Size >> 24) &0x000000FF);
    BootloaderBuffer[5] = nibble;
	
    nibble = (char)((Size >> 16) &0x000000FF);
    BootloaderBuffer[6] = nibble;
	
    nibble = (char)((Size >> 8) &0x000000FF);
    BootloaderBuffer[7] = nibble;
	
	nibble = (char)(Size &0x000000FF);
    BootloaderBuffer[8] = nibble;
	
	unsigned int CRC = CRC_START_32;
	CRC = crc_32((const unsigned char*)BootloaderBuffer,Size+9);
	
	unsigned char* CRCPtr = (unsigned char*)BootloaderDataStartPtr + Size;
	
	nibble = (char)((CRC >> 24) &0x000000FF);
    *CRCPtr++ = nibble;
	
    nibble = (char)((CRC >> 16) &0x000000FF);
    *CRCPtr++ = nibble;
	
    nibble = (char)((CRC >> 8) &0x000000FF);
    *CRCPtr++ = nibble;
	
	nibble = (char)(CRC &0x000000FF);
    *CRCPtr++ = nibble;
	
	//	*CRCPtr++ = 0xBA;
	//	*CRCPtr++ = 0xAD;
	//	*CRCPtr++ = 0xCA;
	//	*CRCPtr++ = 0xFE;
	
	SendBootloaderData(&BootloaderBuffer[0],Size + PROTOCOL_INFO_DATA_SIZE);
}

unsigned char *BootloaderProtocolGetDataBufferPtr()
{
	return (unsigned char*)BootloaderDataStartPtr;
}

void BootloaderProtocolSendHeartbeat()
{
	*BootloaderProtocolGetDataBufferPtr() = BOOTLOADER_ACK;
	BootloaderProtocolSendFrame(BOOTLOADER_HEARTBEAT_RESPONSE,1);
}

void BootloaderProtocolSendACK(unsigned char Cmd)
{
	*BootloaderProtocolGetDataBufferPtr() = BOOTLOADER_ACK;
	BootloaderProtocolSendFrame(Cmd,1);
}

void BootloaderProtocolSendNACK(unsigned char Cmd)
{
	*BootloaderProtocolGetDataBufferPtr() = 0;
	BootloaderProtocolSendFrame(Cmd,1);
}

void BootloaderProtocolSendInitUploadResponse(char result)
{
	int MaxSize = MAX_BOOTLOADER_PAYLOAD_SIZE;
	char* DataPtr = BootloaderProtocolGetDataBufferPtr();
	
	*DataPtr++ = result;
	if(result == 1)
	{
		char nibble = (char)((MaxSize >> 24) &0x000000FF);
		*DataPtr++ = nibble;
		
		nibble = (char)((MaxSize >> 16) &0x000000FF);
		*DataPtr++ = nibble;
		
		nibble = (char)((MaxSize >> 8) &0x000000FF);
		*DataPtr++ = nibble;
		
		nibble = (char)(MaxSize &0x000000FF);
		*DataPtr++ = nibble;
	}
	else
	{
		*DataPtr++ = 0;
		*DataPtr++ = 0;
		*DataPtr++ = 0;
		*DataPtr++ = 0;
	}
	
	BootloaderProtocolSendFrame(BOOTLOADER_INIT_UPLOAD_RESPONSE,5);
}

void BootloaderProtocolSendDataChunkResult(char ErrorCode, int ChunkValue)
{
	char* DataPtr = BootloaderProtocolGetDataBufferPtr();
	
	*DataPtr++ = ErrorCode;
	
	char nibble = (char)((ChunkValue >> 24) &0x000000FF);
    *DataPtr++ = nibble;
	
    nibble = (char)((ChunkValue >> 16) &0x000000FF);
    *DataPtr++ = nibble;
	
    nibble = (char)((ChunkValue >> 8) &0x000000FF);
    *DataPtr++ = nibble;
	
	nibble = (char)(ChunkValue &0x000000FF);
    *DataPtr++ = nibble;
	
	BootloaderProtocolSendFrame(BOOTLOADER_SEND_DATA_CHUNK_RESPONSE,5);
}

void BootloaderProtocolSendBootloaderState(char State)
{
	char* DataPtr = BootloaderProtocolGetDataBufferPtr();
	
	*DataPtr++ = State;
	
	BootloaderProtocolSendFrame(BOOTLOADER_GET_STATE_RESPONSE,1);
}

void BootloaderProtocolSendFirmwareUploadResult(char Result)
{
	char* DataPtr = BootloaderProtocolGetDataBufferPtr();
	
	*DataPtr++ = Result;
	
	BootloaderProtocolSendFrame(BOOTLOADER_UPLOAD_FINISHED_RESPONSE,1);
}

void BootloaderProtocolSendFlashCheckResult(char Result)
{
	char* DataPtr = BootloaderProtocolGetDataBufferPtr();
	
	*DataPtr++ = Result;
	
	BootloaderProtocolSendFrame(BOOTLOADER_CHECK_FLASH_FIRMW_INTEGRITY_RESPONSE,1);
}

void BootloaderProtocolSendStoredFirmwareInfoResponse(unsigned char* FirmwareData, int size)
{
	char* DataPtr = BootloaderProtocolGetDataBufferPtr();
	
	int i = 0;
	for(i = 0; i < size; i++)
	{
		*DataPtr++ = *FirmwareData++;
	}
	
	
	BootloaderProtocolSendFrame(BOOTLOADER_GET_STORED_FIRMWARE_INFO_RESPONSE,size);
}