/**************************************************************************//**
 * @file     spi_common.c
 * @version  V1.0
 * $Date:    19/10/22 14:47 $
 * @brief    Common source file for SPI test.
 *
 * @note
 * Copyright (C) 2019 Panchip Technology Corp. All rights reserved.
 *
 ******************************************************************************/
#include <ctype.h>
#include "pan271x.h"
#include "spi_common.h"

uint8_t spiRxBuffer[SPI_RX_BUF_SIZE];
uint8_t spiTxBuffer[SPI_TX_BUF_SIZE];

volatile uint32_t spiRxBufIdx = 0;
uint32_t spiTxBufIdx = 0;

volatile bool spiTxDone = false;


T_SPI_TEST_RESULT (*const SPI_TestCase[])(void) = 
{
    SPI_InterruptTestCase,
    SPI_SimpleTransmissionDemoCase,
};

void SPI_TestFunctionEnter(uint16_t TcIdx)
{
    T_SPI_TEST_RESULT r = SPI_TST_OK;

    if (TcIdx >= sizeof(SPI_TestCase) / sizeof(void*))
    {
        SYS_TEST("Error, cannot find Testcase %d!", TcIdx);
        return;
    }

    r = (SPI_TestCase[TcIdx])();
    if (r != SPI_TST_OK)
    {
        SYS_TEST("SPI Test Fail, Fail case: %d, Error Code: %d\n", TcIdx, r);
    }
    else
    {
        SYS_TEST("SPI Test OK, Success case: %d\n", TcIdx);
    }
}


void SPI_HandleReceivedData(SPI_T* SPIx)
{
    while (!SPI_IsRxFifoEmpty(SPIx))
    {
        spiRxBuffer[spiRxBufIdx++] = SPI_ReceiveData(SPIx);
        if (spiRxBufIdx >= SPI_RX_BUF_SIZE)   //Rx buffer full
        {
            SYS_TEST("Error, too much data received, Rx buffer full!\n");
            spiRxBufIdx = 0;   //Reset Rx buf index
            break;
        }
    }
}

void SPI_HandleTransmittingData(SPI_T* SPIx)
{
    static uint32_t spiTxMark = 0;
    while (!SPI_IsTxFifoFull(SPIx))
    {
        if (spiTxMark < spiTxBufIdx)
        {
            SPI_SendData(SPIx, spiTxBuffer[spiTxMark++]);
        }
        else
        {
			while(SPI_IsBusy(SPIx)){}
            spiTxMark = 0;  //Reset Tx mark while transmitting done
            SPI_DisableIrq(SPIx, SPI_IRQ_TX_HALF_EMPTY);    //Disable tx INT after transmitting done
			spiTxDone = true;
            break;
        }
    }
}

void SPI_HandleProc(SPI_T* SPIx)
{
    if (SPI_IsIrqActive(SPIx, SPI_IRQ_RX_OVERRUN))
    {
        SPI_ClearIrq(SPIx, SPI_IRQ_RX_OVERRUN); //Clear overrun INT flag manually
        SYS_TEST("SPI Rx Overrun!\n");
        return;
    }

    if (SPI_IsIrqActive(SPIx, SPI_IRQ_RX_HALF_FULL))
    {
        SPI_HandleReceivedData(SPIx);
    }
    else if (SPI_IsIrqActive(SPIx, SPI_IRQ_RX_TIMEOUT))
    {
        SPI_HandleReceivedData(SPIx);
    }

    if (SPI_IsIrqActive(SPIx, SPI_IRQ_TX_HALF_EMPTY))
    {
        SPI_HandleTransmittingData(SPIx);
    }
}

void SPI0_IRQHandler(void)
{
    SPI_HandleProc(SPI0);
}


void SPI_TestModuleInit(void)
{
    // Enable Clock
    CLK_APB1PeriphClockCmd(CLK_APB1Periph_SPI0, ENABLE);
    // Config Pinmux
	/*SPI0 Master*/
	SYS_SET_MFP(P0, 7, SPI0_MOSI);
	SYS_SET_MFP(P1, 2, SPI0_MISO);
	SYS_SET_MFP(P1, 3, SPI0_CS);
	SYS_SET_MFP(P1, 4, SPI0_CLK);
	GPIO_EnableDigitalPath(P0, BIT7);
	GPIO_EnableDigitalPath(P1, BIT2 | BIT3 | BIT4);

}


// Receive multiple data from specified SPI, and return the size of actual data received
size_t SPI_RecvMultiDataAsync(SPI_T* SPIx, uint8_t* data, size_t expect_recv_size)
{
    size_t i = 0;
    for (; i < expect_recv_size; i++)
    {
        // Fetch data from Rx FIFO until it's empty
        if (!SPI_IsRxFifoEmpty(SPIx))
        {
            data[i] = SPI_ReceiveData(SPIx);
        }
        else
        {
            break;
        }
    }
    return i;
}

// Send multiple data to specified SPI, and return the size of actual data sent
size_t SPI_SendMultiDataAsync(SPI_T* SPIx, const uint8_t* data, size_t expect_send_size)
{
    size_t i = 0;
    for (; i < expect_send_size; i++)
    {
        // Fill data into Tx FIFO until it's full
        if (!SPI_IsTxFifoFull(SPIx))
        {
            SPI_SendData(SPIx, data[i]);
        }
        else
        {
            break;
        }
    }
    return i;
}

void SPI_MultiDataSendReceiveAsync(SPI_T* SPIx, const uint8_t* data_to_send, size_t expect_send_size, uint8_t* buf_to_recv, size_t expect_recv_size)
{
    size_t actual_sent_size = 0;
    size_t actual_recv_size = 0;

    while (!(actual_sent_size == expect_send_size && actual_recv_size == expect_recv_size))
    {
        actual_sent_size += SPI_SendMultiDataAsync(SPIx, data_to_send + actual_sent_size, expect_send_size - actual_sent_size);
        actual_recv_size += SPI_RecvMultiDataAsync(SPIx, buf_to_recv + actual_recv_size, expect_recv_size - actual_recv_size);
    }
}

void SPI_RecvMultiDataSync(SPI_T* SPIx, uint8_t* data, size_t expect_recv_size)
{
    size_t i = 0;

    while (i < expect_recv_size)
    {
        if (!SPI_IsRxFifoEmpty(SPIx))
        {
            data[i++] = SPI_ReceiveData(SPIx);
        }
    }
}

void SPI_SendMultiDataSync(SPI_T* SPIx, const uint8_t* data, size_t expect_send_size)
{
    size_t i = 0;

    while (i < expect_send_size)
    {
        if (!SPI_IsTxFifoFull(SPIx))
        {
            SPI_SendData(SPIx, data[i++]);
        }
    }
}
