#include "spi_to_dual_usart_bridge.h"
#include "board_spi.h"
#include "board_usart.h"
#include "board_gpio.h"
#include "board_ctimer.h"
#include "fsl_usart.h"

//data available from USART flag
static bool isDataAvailableFromSPIHost = false;
//General register set
static Reg_Type genRegSet[GEN_REG_COUNT];
//Special register and Enhanced register set
static Reg_Type specEnRegSet[SPEC_EN_REG_COUNT];
//TX Fifo
static uint8_t tx_fifo[FIFO_MAX_LEN];
//RX Fifo
static uint8_t rx_fifo[FIFO_MAX_LEN];
//TX Fifo level
static uint8_t tx_fifo_level = 64;
//RX Fifo level
static uint8_t rx_fifo_level = 64;
//Bridge information frame header
static Bridge_Data_Frame_Header bridgeInfoFromSPIHost;
//Bridge frame state
static uint8_t bridgeFrameState = (uint8_t)Frame_Header;
//data count from SPI host
static uint8_t dataCountFromSPIHost = 0;
//data count from USART
static uint8_t dataCountFromUSART = 0;
//transfer timeout flag
static bool isTransferTimeOutFlag = false;
//read RHR flag
static bool isReadRHRFlag = false;
//read common reg flag
static bool isCommonRegFlag = false;
//Common register
static uint8_t commonRegVal = 0;
//Register update flag indicates whether SPI set SPI-USART bridge register
static bool isRegUpdateFlag = false;
//Record the updated register information
static Reg_Update_Info_Type regUpdateInfo;
//Number of valid data received from USART peripheral
static uint32_t availableDataCountFromUSART = 0;

/*!
 * brief  initalize bridge register
 * param  void
 * return void
 */
void initalizeBridgeRegister(void)
{
	//initalize tx fifo
	memset(tx_fifo,0,FIFO_MAX_LEN);
	//initialize rx fifo
	memset(rx_fifo,0,FIFO_MAX_LEN);
	//initialize FCR register
	genRegSet[FCR_IIR_ADDR].w_reg_val = 0;
	//initialize TLR register
	genRegSet[TLR_SPR_ADDR].rw_reg_val = (uint8_t)((60 << 4) | (4 << 0));
	//initialize TXLVL register
	genRegSet[TXLVL_ADDR].r_reg_val = FIFO_MAX_LEN;
	//initialize RXLVL register
	genRegSet[RXLVL_ADDR].r_reg_val = 0;
	//initialize rx fifo level
	rx_fifo_level = 64;
	//initialize tx fifo level
	tx_fifo_level = 64;
}

/*!
 * brief  update data transmission parameter
 * param  void
 * return void
 */
void updateDataTransferParameter(void)
{
	//temporary register
	uint8_t tempRegVal = 0;
	//bit field
	uint8_t bitField = 0;
	
	if(isRegUpdateFlag == true)
	{
		//set register update flag to false
		isRegUpdateFlag = false;
		
		switch((uint8_t)(regUpdateInfo.reg_set_type))
		{
			case GEN_REG_SET:
			{//General register set
				//restrict address range
				(regUpdateInfo.reg_addr) &= 0x0F;
				
				switch(regUpdateInfo.reg_addr)
				{
					case LSR_ADDR:
					case TCR_MSR_ADDR:
					case TXLVL_ADDR:
					case RXLVL_ADDR:
					case IOState_ADDR:
					{//LSR,MSR,TXLVL,RXLVL,IOState are not available in write mode
					}
					break;
					
					case reserved_ADDR:
					{//reserved address is not available in both write mode and read mode
					}
					break;
					
					case FCR_IIR_ADDR:
					{//IIR and FCR have the same address, FCR is available in write mode
						//get FCR register value
						tempRegVal = genRegSet[regUpdateInfo.reg_addr].w_reg_val;
						//fifo enable or disable
						bitField = (tempRegVal & 0x01);
						if(bitField == 0)
						{//fifo disable
							//set tx fifo depth
							tx_fifo_level = 1;
							//set rx fifo depth
							rx_fifo_level = 1;
						}
						else
						{//fifo enable
							/**************************set rx fifo trigger level*************************/
							/*
								Bit    Symbol           Description
								7:6    FCR[7] (MSB)     RX trigger. Sets the trigger level for the RX FIFO.
											 FCR[6] (LSB)     	00 = 8  characters
																					01 = 16 characters
																					10 = 56 characters
																					11 = 60 characters
							*/
							bitField = ((tempRegVal >> 6) & 0x03);
							switch(bitField)
							{
								case 0:
								{
									rx_fifo_level = 8;
								}
								break;
								
								case 1:
								{
									rx_fifo_level = 16;
								}
								break;
								
								case 2:
								{
									rx_fifo_level = 56;
								}
								break;
								
								case 3:
								{
									rx_fifo_level = 60;
								}
								break;
							}
							/**************************set rx fifo trigger level*************************/
							
							/**************************set tx fifo trigger level*************************/
							/*
								Bit    Symbol           Description
								5:4    FCR[5] (MSB)     TX trigger. Sets the trigger level for the TX FIFO.
											 FCR[4] (LSB)     	00 = 8 spaces
																					01 = 16 spaces
																					10 = 32 spaces
																					11 = 56 spaces
							*/
							bitField = ((tempRegVal >> 4) & 0x03);
							switch(bitField)
							{
								case 0:
								{
									tx_fifo_level = 56;
								}
								break;
								
								case 1:
								{
									tx_fifo_level = 48;
								}
								break;
								
								case 2:
								{
									tx_fifo_level = 32;
								}
								break;
								
								case 3:
								{
									tx_fifo_level = 8;
								}
								break;
							}
							/**************************set tx fifo trigger level*************************/
						}
						
						//reset rx fifo
						bitField = ((tempRegVal >> 1) & 0x01);
						if(bitField)
						{
							//clear rx fifo
							memset(rx_fifo,0,rx_fifo_level);
							//clear rx fifo reset bit
							genRegSet[regUpdateInfo.reg_addr].w_reg_val &= (~(1 << 1));
						}
						//reset tx fifo
						bitField = ((tempRegVal >> 2) & 0x01);
						if(bitField)
						{
							//clear tx fifo
							memset(tx_fifo,0,tx_fifo_level);
							//clear tx fifo reset bit
							genRegSet[regUpdateInfo.reg_addr].w_reg_val &= (~(1 << 2));
						}
					}
					break;
					
					default:
					{//others are read mode and write mode correspond to the same register entity
					}
					break;
				}
			}
			break;
			
			case SPEC_EN_REG_SET:
			{//Special register and Enhanced register set
				//restrict address range
				(regUpdateInfo.reg_addr) &= 0x07;
			}
			break;
			
			default:
			{
			}
			break;
		}
	}
}

/*!
 * brief  set SPI_2USARTs_bridge register
 * param  Reg_Set_Type reg_set->register set:General register set or Special register and Enhanced register set
 * param  uint8_t reg_addr->register address
 * param  uint8_t reg_value->setting value for register
 * return void
 */
void setBridgeReg(Reg_Set_Type reg_set, uint8_t reg_addr, uint8_t reg_value)
{//correspond to register write mode
	//integer type for reg_set
	uint8_t reg_set_int = (uint8_t)reg_set;
	//integer type for reg_addr
	uint8_t reg_addr_int = reg_addr;
	
	//set register update flag to true
  isRegUpdateFlag = true;
	//Record the updated register information
	regUpdateInfo.reg_set_type = reg_set;
	regUpdateInfo.reg_addr = reg_addr;
	
	switch(reg_set_int)
	{
		case GEN_REG_SET:
		{//General register set
			//restrict address range
			reg_addr_int &= 0x0F;
			
			switch(reg_addr_int)
			{
				case LSR_ADDR:
				case TCR_MSR_ADDR:
				case TXLVL_ADDR:
				case RXLVL_ADDR:
				case IOState_ADDR:
				{//LSR,MSR,TXLVL,RXLVL,IOState are not available in write mode
				}
				break;
				
				case reserved_ADDR:
				{//reserved address is not available in both write mode and read mode
				}
				break;
				
				case FCR_IIR_ADDR:
				{//IIR and FCR have the same address, FCR is available in write mode
					genRegSet[reg_addr_int].w_reg_val = reg_value;
				}
				break;
				
				case TLR_SPR_ADDR:
				{
					tx_fifo_level = (FIFO_MAX_LEN - (reg_value & 0x0F));
					rx_fifo_level = ((reg_value >> 4) & 0x0F);
					genRegSet[reg_addr_int].rw_reg_val = reg_value;
				}
				break;
				
				default:
				{//others are read mode and write mode correspond to the same register entity
					genRegSet[reg_addr_int].rw_reg_val = reg_value;
				}
				break;
			}
		}
		break;
		
		case SPEC_EN_REG_SET:
		{//Special register and Enhanced register set
			//restrict address range
			reg_addr_int &= 0x07;
			specEnRegSet[reg_addr_int].rw_reg_val = reg_value;
		}
		break;
		
		default:
		{
		}
		break;
	}
}

/*!
 * brief  get SPI_2USARTs_bridge register
 * param  Reg_Set_Type reg_set->register set:General register set or Special register and Enhanced register set
 * param  uint8_t reg_addr->register address
 * return uint8_t->register value
 */
uint8_t getBridgeReg(Reg_Set_Type reg_set, uint8_t reg_addr)
{//correspond to register read mode
	//integer type for reg_set
	uint8_t reg_set_int = (uint8_t)reg_set;
	//integer type for reg_addr
	uint8_t reg_addr_int = reg_addr;
	//temporary register value
	uint8_t temp_reg_val = 0;
	
	switch(reg_set_int)
	{
		case GEN_REG_SET:
		{//General register set
			switch(reg_addr_int)
			{	
				case LSR_ADDR:
				case TCR_MSR_ADDR:
				case TXLVL_ADDR:
				case RXLVL_ADDR:
				case IOState_ADDR:
				{//LSR,MSR,TXLVL,RXLVL,IOState are only available in read mode
					temp_reg_val = genRegSet[reg_addr_int].r_reg_val;
				}
				break;
				
				case reserved_ADDR:
				{//reserved address is not available in both write mode and read mode
				}
				break;
				
				case FCR_IIR_ADDR:
				{//IIR and FCR have the same address, IIR is available in read mode
					temp_reg_val = genRegSet[reg_addr_int].r_reg_val;
				}
				break;
				
				default:
				{//others are read mode and write mode correspond to same register entity
					temp_reg_val = genRegSet[reg_addr_int].rw_reg_val;
				}
				break;
			}
		}
		break;
		
		case SPEC_EN_REG_SET:
		{//Special register and Enhanced register set
			temp_reg_val = specEnRegSet[reg_addr_int].rw_reg_val;
		}
		break;
		
		default:
		{
		}
		break;
	}
	
	//return register value
	return temp_reg_val;
}

/*!
 * brief  parse raw data information from SPI host
 * param  uint8_t rawInfo->raw data from SPI host
 * param  Bridge_Data_Frame_Header* parsedInfo->pointer to parsed bridge information
 * return void
 */
static void parseBridgeInfo(uint8_t rawInfo, Bridge_Data_Frame_Header* parsedInfo)
{
	if(parsedInfo != NULL)
	{
		//get the data transmission direction
		parsedInfo->isSPIHostToUSART = ((((rawInfo >> 7) & 0x01) == 0)?true:false);
		//get the bridge register address
		parsedInfo->regAddr = ((rawInfo >> 3) & 0x0F);
		//get the USART channel number
		parsedInfo->USART_channel = ((rawInfo >> 1) & 0x03);
	}
}

/*!
 * brief  pack data frame of SPI to 2USARTs bridge
 * param  uint8_t regAddr->register address
 * param  uint8_t usart_ch_num->USART channel number
 * return uint8_t->packed information
 */
static uint8_t packBridgeInfo(uint8_t regAddr, uint8_t usart_ch_num)
{
	return ((1 << 7) | ((regAddr & 0x0F) << 3) | ((usart_ch_num & 0x03) << 1));
}

/*!
 * brief  generate an IRQ signal to SPI host
 * param  void
 * return void
 */
void emitIRQToSPIHost(void)
{
	//generate a rising edge on IRQ pin by ENABLE_IRQ_PIN following DISABLE_IRQ_PIN
	ENABLE_IRQ_PIN;
	DISABLE_IRQ_PIN;
}

/*!
 * brief  set transfer timeout flag
 * param  void
 * return void
 */
void setTransferTimeOutFlag(void)
{
	isTransferTimeOutFlag = true;
}

/*!
 * brief  preprocess data from SPI host
 * param  uint8_t sourceData->data received from SPI host
 * return void
 */
void preprocessDataFromSPIHost(uint8_t sourceData)
{
	//USART channel number
	int8_t usartChNum = -1;
	//bridge information byte
	uint8_t bridgeInfoByte = 0;
	
	
	switch(bridgeFrameState)
	{
		case Frame_Header:
		{//the current state is the frame header
			//clear direction bit indicates data fol
	    bridgeInfoFromSPIHost.isSPIHostToUSART = false;
			//parse raw data information from SPI host
			parseBridgeInfo(sourceData,&bridgeInfoFromSPIHost);
			//judge whether data transfer direction is from SPI host to USART or not
			if(bridgeInfoFromSPIHost.isSPIHostToUSART == true)
			{//SPI host to USART
				//enable software watchdog
				enableSoftWDT();
			}
			else
			{//USART to SPI host  
				if(bridgeInfoFromSPIHost.regAddr == THR_RHR_ADDR)
				{//The target register is RHR
					//get USART channel number
					usartChNum = getUSARTChannelNum();
					//pack bridge information byte
					bridgeInfoByte = packBridgeInfo(THR_RHR_ADDR,usartChNum);
					//send bridge information to SPI data register
					BOARD_SPISendData(bridgeInfoByte);
					//set read RHR flag as true
					isReadRHRFlag = true;
				}
				else
				{//The target register is not RHR
					//get register value
					commonRegVal = getBridgeReg(GEN_REG_SET,bridgeInfoFromSPIHost.regAddr);
					//clear IIR register
					genRegSet[FCR_IIR_ADDR].r_reg_val = 0;
					//save register value to SPI data register
					BOARD_SPISendData(commonRegVal);
					//set read common reg flag as true
					isCommonRegFlag = true;
				}
			}
			//the next state is data phase
			bridgeFrameState = (uint8_t)Frame_Data;
		}
		break;
		
		case Frame_Data:
		{//the current state is the frame data
			//Only when SPI host sends data to USART peripheral, it is need to save source data to tx fifo,
			//otherwise, if USART peripheral sends data to SPI host, it is no need to save source data 
			if(bridgeInfoFromSPIHost.isSPIHostToUSART == true)
			{//The data transmission direction is from SPI host to USART peripheral
				//judge whether the target register is THR/RHR or not
				switch(bridgeInfoFromSPIHost.regAddr)
				{
					case THR_RHR_ADDR:
					{
						//feed software watchdog
						feedSoftWDT();
						//save data received to tx fifo
						tx_fifo[dataCountFromSPIHost] = sourceData;
						//decrement space number in tx fifo
						genRegSet[TXLVL_ADDR].r_reg_val--;
						//increment data count from SPI host
						dataCountFromSPIHost++;
						//data count whether reaches at tx fifo level or not
						if(dataCountFromSPIHost == tx_fifo_level)
						{//data count reaches at tx fifo level 
							//disable software watchdog
							disableSoftWDT();
							//reset bridge frame state
							bridgeFrameState = (uint8_t)Frame_Header;
							//set IIR register to provides interrupt source
							genRegSet[FCR_IIR_ADDR].r_reg_val &=~ (0x3F);
							genRegSet[FCR_IIR_ADDR].r_reg_val |=  (0x02);
							//set data available from SPI host flag as true
							isDataAvailableFromSPIHost = true;
						}
					}
					break;
					
					default:
					{
						//set register
						setBridgeReg(GEN_REG_SET,bridgeInfoFromSPIHost.regAddr,sourceData);
						//disable software watchdog
						disableSoftWDT();
						//reconfigure data transmission direction flag
						bridgeInfoFromSPIHost.isSPIHostToUSART = false;
						//reset bridge frame state
						bridgeFrameState = (uint8_t)Frame_Header;
					}
					break;
				}
			}
		}
		break;
		
		default:
		{
		}
		break;
	}
}

/*!
 * brief  save data received from USART peripheral to rx fifo in bridge
 * param  uint8_t sourceData->data received from USART peripheral
 * return void
 */
void saveUSARTDataToRxFifo(uint8_t sourceData)
{
	//save data received to rx fifo
	rx_fifo[dataCountFromUSART] = sourceData;
	
	//increment data count from USART
	dataCountFromUSART++;
	//record available data count from USART
	availableDataCountFromUSART = dataCountFromUSART;
	//update RXLVL register
  genRegSet[RXLVL_ADDR].r_reg_val = availableDataCountFromUSART;
	//data count whether reaches at rx fifo level or not
	if(dataCountFromUSART == rx_fifo_level)
	{//data count reaches at rx fifo level 
		//available data count from USART since addtional information byte
		availableDataCountFromUSART++;
		//clear index for rx fifo
		dataCountFromUSART = 0;
		//disable software watchdog
	  disableSoftWDT();
		//reset bridge frame state
	  //bridgeFrameState = (uint8_t)Frame_Header;
		//set IIR register to provides interrupt source
		genRegSet[FCR_IIR_ADDR].r_reg_val &=~ (0x3F);
		genRegSet[FCR_IIR_ADDR].r_reg_val |=  (0x04);
		//generate IRQ interrupt to notify the SPI host to continue sending data
		emitIRQToSPIHost();
	}
	else if(dataCountFromUSART == 1)
	{
		//enable software watchdog
		enableSoftWDT(); 
	}
	else
	{
		//feed software watchdog
		feedSoftWDT();
	}
}

/*!
 * brief  process time out
 * param  void
 * return void
 */
void processTimeOut(void)
{
	//USART channel number
	int8_t usartChNum = -1;
	//bridge information byte
	uint8_t bridgeInfoByte = 0;
	
	if(isTransferTimeOutFlag == true)
	{
		//clear transfer timeout flag
		isTransferTimeOutFlag = false;
		//disable software watchdog
		disableSoftWDT();
		//reset bridge frame state
		bridgeFrameState = (uint8_t)Frame_Header;
		//judge data transmission direction 
		if(bridgeInfoFromSPIHost.isSPIHostToUSART == true)
		{//The data transmission direction is from SPI host to USART peripheral
			if(dataCountFromSPIHost > 0)
			{//tx fifo has available data
				//set data available from SPI host flag as true
				isDataAvailableFromSPIHost = true;
			}
		}
		else
		{//The data transmission direction is from USART peripheral to SPI host
      //clear index for rx fifo
			dataCountFromUSART = 0;
      //update RXLVL register
      genRegSet[RXLVL_ADDR].r_reg_val = availableDataCountFromUSART;			
      //increment available data count from USART when rx timeout		
			availableDataCountFromUSART++;
			//get USART channel number
			usartChNum = getUSARTChannelNum();
			//pack bridge information byte
			bridgeInfoByte = packBridgeInfo(THR_RHR_ADDR,usartChNum);
			//send bridge information to SPI data register
			BOARD_SPISendData(bridgeInfoByte);
			//set IIR register to provides interrupt source
			genRegSet[FCR_IIR_ADDR].r_reg_val &=~ (0x3F);
			genRegSet[FCR_IIR_ADDR].r_reg_val |=  (0x0C);
			//generate IRQ interrupt to notify the SPI host to continue sending data
			emitIRQToSPIHost();
		}
	}
}

/*!
 * brief  process data from SPI host
 * param  void
 * return void
 */
void processDataFromSPIHost(void)
{
	//temporary pointer for USART
	USART_Type* p_tempUsart= NULL;
	
	//check whether data in Tx FIFO is available or not
	if(isDataAvailableFromSPIHost == true)
	{
		//set data available from SPI host flag as false
		isDataAvailableFromSPIHost = false;
		
		if(bridgeInfoFromSPIHost.isSPIHostToUSART == true)
		{//The data transmission direction is from SPI host to USART peripheral
			//reset data transmission direction flag 
			bridgeInfoFromSPIHost.isSPIHostToUSART = false;
			//identify USART device number
			switch(bridgeInfoFromSPIHost.USART_channel)
			{
				case 0:
				{//target usart peripheral channel number is 0
					p_tempUsart = USART0;
				}
				break;
				
				case 1:
				{
					//target usart peripheral channel number is 1
					p_tempUsart = USART1;
				}
				break;
				
				default:
				{
				  //not an available usart device channel number
					p_tempUsart = NULL;
				}
				break;
			}
			//send data saved in TX FIFO to target usart peripheral channel
			//BOARD_UsartSendData(p_tempUsart,tx_fifo,dataCountFromSPIHost);
			for(uint32_t i =0; i < dataCountFromSPIHost;i++)
			{
				//send data saved in TX FIFO to target usart peripheral channel
				BOARD_UsartSendData(p_tempUsart,&tx_fifo[i],1);
			}
			//reset space number in tx fifo
			genRegSet[TXLVL_ADDR].r_reg_val = tx_fifo_level;
			//clear data count from SPI host
			dataCountFromSPIHost = 0;
			//generate IRQ interrupt to notify the SPI host to continue sending data
			emitIRQToSPIHost();
		}
	}
}

/*!
 * brief  send data from USART peripheral to SPI host
 * param  void
 * return void
 */
void sendDataFromUSARTToSPIHost(void)
{	
	//index for rx fifo
	static uint8_t rxFifoIndex = 0;
	
	if(isReadRHRFlag == true)
	{
		//send rx fifo data to SPI data register
		BOARD_SPISendData(rx_fifo[rxFifoIndex]);
		if(rxFifoIndex >= 2)
		{
			//update RXLVL register
      genRegSet[RXLVL_ADDR].r_reg_val--;
		}
		//increment index for rx fifo
		rxFifoIndex++;
		//flush rx fifo
		if(rxFifoIndex == availableDataCountFromUSART)
		{
			//clear rx_fifo
			memset(rx_fifo,0,availableDataCountFromUSART);
			//clear data count from USART
			availableDataCountFromUSART = 0;
			//clear index for rx fifo
			rxFifoIndex = 0;
			//reset read RHR flag
			isReadRHRFlag = false;
			//reset bridge frame state
	    bridgeFrameState = (uint8_t)Frame_Header;
		}
	}
	//return register value
	if(isCommonRegFlag == true)
	{
		//reset read common register flag
		isCommonRegFlag = false;
		//send common register value to SPI data register
		BOARD_SPISendData(commonRegVal);
		//reset bridge frame state
	  bridgeFrameState = (uint8_t)Frame_Header;
	}
}
