/*
*         Copyright (c), NXP Semiconductors Caen / France
*
*                     (C)NXP Semiconductors
*       All rights are reserved. Reproduction in whole or in part is
*      prohibited without the written consent of the copyright owner.
*  NXP reserves the right to make changes without notice at any time.
* NXP makes no warranty, expressed, implied or statutory, including but
* not limited to any implied warranty of merchantability or fitness for any
*particular purpose, or that the use will not infringe any third party patent,
* copyright or trademark. NXP must not be liable for any loss or damage
*                          arising from its use.
*/

#include <stdint.h>
#include "board.h"
#include "fsl_gpio.h"
#include <tool.h>

typedef enum {ERROR = 0, SUCCESS = !ERROR} Status;

static bool isDwlMode = false;
#define HEADER_SZ (isDwlMode==true?1:2)
#define FOOTER_SZ (isDwlMode==true?2:0)

#ifdef BOARD_NXPNCI_INTERFACE_I2C
#include "fsl_i2c.h"
i2c_master_transfer_t masterXfer = {0};

static void INTF_INIT(void)
{
	i2c_master_config_t masterConfig;
    I2C_MasterGetDefaultConfig(&masterConfig);
    masterConfig.baudRate_Bps = BOARD_NXPNCI_I2C_BAUDRATE;
    I2C_MasterInit(BOARD_NXPNCI_I2C_INSTANCE, &masterConfig, BOARD_NXPNCI_I2C_CLOCK_FREQ);
    masterXfer.slaveAddress = BOARD_NXPNCI_I2C_ADDR;
    masterXfer.subaddress = 0;
    masterXfer.subaddressSize = 0;
    masterXfer.flags = kI2C_TransferDefaultFlag;

}

static status_t INTF_WRITE(uint8_t *pBuff, uint16_t buffLen)
{
    masterXfer.direction = kI2C_Write;
    masterXfer.data = pBuff;
    masterXfer.dataSize = buffLen;
    return I2C_MasterTransferBlocking(BOARD_NXPNCI_I2C_INSTANCE, &masterXfer);
}

static status_t INTF_READ(uint8_t *pBuff, uint16_t buffLen)
{
    masterXfer.direction = kI2C_Read;
    masterXfer.data = pBuff;
    masterXfer.dataSize = buffLen;
    return I2C_MasterTransferBlocking(BOARD_NXPNCI_I2C_INSTANCE, &masterXfer);
}
#else // BOARD_NXPNCI_INTERFACE_SPI
#include "fsl_spi.h"
spi_transfer_t masterXfer;

static void INTF_INIT(void)
{
	spi_master_config_t masterConfig;
    SPI_MasterGetDefaultConfig(&masterConfig);
    masterConfig.baudRate_Bps = BOARD_NXPNCI_SPI_BAUDRATE;
    masterConfig.sselNum = (spi_ssel_t) BOARD_NXPNCI_SPI_SSEL;
    masterConfig.sselPol = (spi_spol_t) BOARD_NXPNCI_SPI_SPOL;
    masterXfer.configFlags = kSPI_FrameAssert;
    SPI_MasterInit(BOARD_NXPNCI_SPI_INSTANCE, &masterConfig, BOARD_NXPNCI_SPI_CLOCK);
}

static status_t INTF_WRITE(uint8_t *pBuff, uint16_t buffLen)
{
	uint8_t temp[1000];
	temp[0] = 0x7F;
	memcpy(temp+1, pBuff, buffLen);
    masterXfer.txData   = temp;
    masterXfer.rxData   = NULL;
    masterXfer.dataSize = buffLen+1;
    return SPI_MasterTransferBlocking(BOARD_NXPNCI_SPI_INSTANCE, &masterXfer);
}

static status_t INTF_READ(uint8_t *pBuff, uint16_t buffLen)
{
	status_t status;
	uint8_t temp[257];
	temp[0] = 0xFF;
	masterXfer.txData   = temp;
    masterXfer.rxData   = temp;
    masterXfer.dataSize = buffLen+1;
    status = SPI_MasterTransferBlocking(BOARD_NXPNCI_SPI_INSTANCE, &masterXfer);
    if(status == kStatus_Success) memcpy(pBuff, temp+1, buffLen);
    SDK_DelayAtLeastUs(10, SDK_DEVICE_MAXIMUM_CPU_CLOCK_FREQUENCY);
    return status;
}
#endif

static Status tml_Init(void) {
    gpio_pin_config_t in_config = {kGPIO_DigitalInput, 0};
    gpio_pin_config_t out_config = {kGPIO_DigitalOutput, 0};

    isDwlMode = false;

    GPIO_PortInit(GPIO, BOARD_NXPNCI_GPIO_PORT);
    GPIO_PinInit(GPIO, BOARD_NXPNCI_IRQ_PORT, BOARD_NXPNCI_IRQ_PIN, &in_config);
    GPIO_PinInit(GPIO, BOARD_NXPNCI_VEN_PORT, BOARD_NXPNCI_VEN_PIN, &out_config);
    GPIO_PinInit(GPIO, BOARD_NXPNCI_DWL_PORT, BOARD_NXPNCI_DWL_PIN, &out_config);

    INTF_INIT();

    return SUCCESS;
}

static Status tml_DeInit(void) {
    GPIO_PinWrite(GPIO, BOARD_NXPNCI_VEN_PORT, BOARD_NXPNCI_VEN_PIN, 0);
    return SUCCESS;
}

static Status tml_Reset(void) {
    GPIO_PinWrite(GPIO, BOARD_NXPNCI_VEN_PORT, BOARD_NXPNCI_VEN_PIN, 0);
	Sleep(10);
    GPIO_PinWrite(GPIO, BOARD_NXPNCI_VEN_PORT, BOARD_NXPNCI_VEN_PIN, 1);
	Sleep(10);
	return SUCCESS;
}

static Status tml_Tx(uint8_t *pBuff, uint16_t buffLen) {
    if (INTF_WRITE(pBuff, buffLen) != kStatus_Success)
    {
    	Sleep(10);
    	if(INTF_WRITE(pBuff, buffLen) != kStatus_Success)
    	{
    		return ERROR;
    	}
    }

	return SUCCESS;
}

static Status tml_Rx(uint8_t *pBuff, uint16_t buffLen, uint16_t *pBytesRead) {
    if(INTF_READ(pBuff, HEADER_SZ+1) == kStatus_Success)
    {
    	if ((pBuff[HEADER_SZ] + HEADER_SZ+1) <= buffLen)
    	{
			if (pBuff[HEADER_SZ] > 0)
			{
				if(INTF_READ(&pBuff[HEADER_SZ+1], pBuff[HEADER_SZ]+FOOTER_SZ) == kStatus_Success)
				{
					*pBytesRead = pBuff[HEADER_SZ] + HEADER_SZ+1;
				}
				else return ERROR;
			} else
			{
				*pBytesRead = HEADER_SZ+1;
			}
    	}
		else return ERROR;
   }
    else return ERROR;

	return SUCCESS;
}

static Status tml_WaitForRx(uint32_t timeout) {
	if (timeout == 0) {
		while ((GPIO_PinRead(GPIO, BOARD_NXPNCI_IRQ_PORT, BOARD_NXPNCI_IRQ_PIN) == 0));
	} else {
		int16_t to = timeout;
		while ((GPIO_PinRead(GPIO, BOARD_NXPNCI_IRQ_PORT, BOARD_NXPNCI_IRQ_PIN) == 0)) {
			Sleep(10);
			to -= 10;
			if (to <= 0) return ERROR;
		}
	}
	return SUCCESS;
}

void tml_Connect(void) {
	tml_Init();
	tml_Reset();
}

void tml_Disconnect(void) {
	tml_DeInit();
}

void tml_EnterDwlMode(void) {
	isDwlMode = true;
    GPIO_PinWrite(GPIO, BOARD_NXPNCI_DWL_PORT, BOARD_NXPNCI_DWL_PIN, 1);
    tml_Reset();
}

void tml_LeaveDwlMode(void) {
	isDwlMode = false;
    GPIO_PinWrite(GPIO, BOARD_NXPNCI_DWL_PORT, BOARD_NXPNCI_DWL_PIN, 0);
    tml_Reset();
}

void tml_Send(uint8_t *pBuffer, uint16_t BufferLen, uint16_t *pBytesSent) {
	if(tml_Tx(pBuffer, BufferLen) == ERROR) *pBytesSent = 0;
	else *pBytesSent = BufferLen;
}

void tml_Receive(uint8_t *pBuffer, uint16_t BufferLen, uint16_t *pBytes, uint16_t timeout) {
	if (tml_WaitForRx(timeout) == ERROR) *pBytes = 0;
	else tml_Rx(pBuffer, BufferLen, pBytes);
}
