#include <stdint.h>
#include <string.h>
#include <stdbool.h>
#include <p33EV256GM004.h>


#include "../mcc_generated_files/pin_manager.h"
#include "userSPI.h"
#include "../mcc_generated_files/ext_int.h"
#include "misc.h"
#include "userComm.h"


volatile static uint8_t *pTxBuff;
volatile static uint8_t *pRxBuff;

static uint8_t *pTxBuff2;
static uint8_t *pRxBuff2;

volatile static uint8_t dataReadIndex = 0;
volatile static uint8_t dataWriteIndex = 0;

static uint8_t dataReadIndex2 = 0;
static uint8_t dataWriteIndex2 = 0;
static uint8_t dynLen;
static uint16_t dataStartIndex;
static uint16_t spi2BuffLen;

static uint8_t RxTempBuff;

volatile static uint8_t accDataToMX[40];


volatile static uint8_t accDataToMXIndex;
volatile static uint8_t accDataToMXCount;

volatile static bool accDataLoad = false;
volatile static bool accDataLoaded = true;                                               //at start data is loaded (this pass condition to not load when previously loaded data has not been sent)
volatile static bool accDataReqSent = false;

static uint16_t delaySPI;
static uint16_t lastReset;
static uint16_t cntReset;
static uint16_t cntByteSpi;

static bool interruptEnable = false;

//init SPI1 - MainMCU
void DRV_SPIInit(uint8_t* rxBuff, uint8_t* txBuff)
{
    pTxBuff = txBuff;
    pRxBuff = rxBuff;
}

//init SPI2 - MeasureMCU
void DRV_SPI2Init(uint8_t* rxBuff, uint8_t* txBuff, uint16_t len)
{
    pTxBuff2 = txBuff;
    pRxBuff2 = rxBuff;
    spi2BuffLen = len;
}

//schedule acc data to send to main mcu
bool ScheduleAccData(uint8_t *data, uint8_t len)
{
    if (accDataLoaded == true)
    {
        accDataLoaded = false;
        accDataToMXCount = 12;
        memcpy(accDataToMX,data,len);
        return true;
    }
    return false;
    
}

//fill SPI FIFO, 
void DRV_SPITask(void)
{
    //write data to FIFO
    while(SPI1STATbits.SPITBF == 0)
    {
        LED7_Toggle();
        
        switch(dataWriteIndex)
        {
            case 0:
            {
                SPI1BUF = 0xAA;
                break;
            }
            case 1:
            {
                SPI1BUF = accDataToMXCount;
                if (accDataToMXCount != 0)                      
                {
                    accDataToMXCount = 0;
                    accDataLoad = true;
                }
                break;
            }
            case 14:
            {
                SPI1BUF = GEN_GetTemp();
                break;
            }
            case 15:
            {
                SPI1BUF = GEN_GetStatusByte();
                GEN_OpNack();
                break;
            }
            default:
            {
                if (accDataLoad == true)                                        //if there is some acc data, fill message with acc data, else fill zeros
                {
                    SPI1BUF = accDataToMX[accDataToMXIndex++];
                    LED7_Toggle();
                    LED7_Toggle();
                }else{
                    SPI1BUF = 0;
                }
                break;
            }
                
        }
               
        dataWriteIndex++;
        if (dataWriteIndex > 15)
        {
            if (accDataLoad == true)
            {
                if (accDataToMXIndex == 12)
                {
                    accDataToMXCount = 12;
                    accDataReqSent = true;
                    
                }
                else if (accDataToMXIndex == 24)
                {
                    accDataToMXIndex = 0;  
                    accDataReqSent = true;
                    accDataLoaded = true;                    
                }
                accDataLoad = false;
                
                //GEN_ReadyToAsk();
            }
            if (interruptEnable == false)
            {
                interruptEnable = true;                
                IFS0bits.SPI1IF = 0;
                IEC0bits.SPI1IE = 1;
            }

            dataWriteIndex = 0;
        }
        
        
        if (SPI1STATbits.SRXMPT == 0)
        {
            pRxBuff[dataReadIndex++] = SPI1BUF;
        }
    }
    
    if ((accDataLoaded == false) && (accDataToMXIndex < 12) )
    {
        accDataReqSent = false;
        GEN_ReadyToAsk();
    }

        

    
    //read data from FIFO
    while (SPI1STATbits.SRXMPT == 0)
    {
        pRxBuff[dataReadIndex++] = SPI1BUF;
        //LED7_Toggle();

    }
    
    //LED9_Toggle();
    if (dataReadIndex > 15)
    {
        dataReadIndex = 0;
        DMA0_CallBack();
    }
    if (SPI1STATbits.SPIROV == 1)
    {
        SPI1STATbits.SPIROV = 0;
    }
}


//read data which measure mcu sends
void DRV_SPI2Task(void)
{
    static uint16_t dataStartIndexTmp;
    uint8_t spiBuffRead;
    //write data to FIFO
    if(SPI2STATbits.SPITBF == 0)
    {
        SPI2BUF = 0;
    }

    
    //read data from FIFO
    while (SPI2STATbits.SRXMPT == 0)
    {
        //pRxBuff2[dataStartIndex] = SPI2BUF;
        spiBuffRead = SPI2BUF;
        
        if (dataReadIndex2 == 0)                                                //first data byte received
        {            
            if (spiBuffRead == 'a')
            {
                dynLen = 25;
                dataReadIndex2++;
            }
            else if (spiBuffRead == 't')
            {
                dynLen = 2;
                dataReadIndex2++;
                //dataStartIndex -= 1;
            }
            else
            {
                //LED8_Toggle();
                dataReadIndex2 = 0;
            }
        }else{
            if (dynLen == 25)
            {
                pRxBuff2[dataStartIndex] = spiBuffRead;
                dataStartIndex++;
                if (dataStartIndex >= spi2BuffLen)
                {
                    dataStartIndex = 0;
                }
                
            }else{
                RxTempBuff = spiBuffRead;
            }

            dataReadIndex2++;

        }

        if (dataReadIndex2 >= dynLen)
        {
            dataReadIndex2 = 0;
            if (dynLen == 25)
            {
                //Nop();
                SPI2_CallbackAcc(dataStartIndex);
            }else{
                //Nop();
                SPI2_CallbackTemp(RxTempBuff);
            }

            

        }
        //cntByteSpi++;
        DelayReset(&delaySPI);
        //lastReset = dataReadIndex2;
        //cntReset = 0;
        //LED7_Toggle();

    }
    
    //LED9_Toggle();
    //cntReset++;
    if (HasDelayElapsed(3,&delaySPI))                                           // master-slave synchronization lost, reset interface
    {
        
        if (dataReadIndex2 != 0)
        {
            //TMR2 = 5;
            dataReadIndex2 = 0;
            dataStartIndex = 0;
            SPI2_CallbackAcc(dataStartIndex);
            GEN_ResetAccDataStart();
            accDataLoaded = true;
            //LED7_Toggle();
            
            SPI2STATbits.SPIEN = 0;
            SPI2STATbits.SPIEN = 1;
        }
    }

    
    if (SPI2STATbits.SPIROV == 1)
    {
        SPI2STATbits.SPIROV = 0;
    }
}



void __attribute__ ((weak)) SPI2_CallbackAcc(uint16_t endIndex)
{
    // Add your custom callback code here
}

void __attribute__ ((weak)) SPI2_CallbackTemp(uint8_t temp)
{
    // Add your custom callback code here
}


void INT0_CallBack(void)
{
    //LED10_Toggle();
    dataReadIndex = 0;                  //should be zero, but due to some interference could be not zero
    //CS went to zero, new data will arrive in few moments
}


//in case some operation block core when SPI is about to run out of data, interrupt and write next data to FIFO
void __attribute__ ( ( interrupt, no_auto_psv ) ) _SPI1Interrupt(void)
{
    //***User Area Begin->code: External Interrupt 0***
    LED10_Toggle();

    while(SPI1STATbits.SPITBF == 0)
    {
        LED7_Toggle();
        
        switch(dataWriteIndex)
        {
            case 0:
            {
                SPI1BUF = 0xAA;
                break;
            }
            case 1:
            {
                SPI1BUF = accDataToMXCount;
                if (accDataToMXCount != 0)
                {
                    accDataToMXCount = 0;
                    accDataLoad = true;
                }
                break;
            }
            case 14:
            {
                SPI1BUF = GEN_GetTemp();
                break;
            }
            case 15:
            {
                SPI1BUF = GEN_GetStatusByte();
                break;
            }
            default:
            {
                if (accDataLoad == true)
                {
                    SPI1BUF = accDataToMX[accDataToMXIndex++];
                    LED7_Toggle();
                    LED7_Toggle();
                }else{
                    SPI1BUF = 0;
                }
                break;
            }
                
        }
               
        dataWriteIndex++;
        if (dataWriteIndex > 15)
        {
            if (accDataLoad == true)
            {
                if (accDataToMXIndex == 12)
                {
                    accDataToMXCount = 12;
                    accDataReqSent = true;
                    
                }
                else if (accDataToMXIndex == 24)
                {
                    accDataToMXIndex = 0;  
                    accDataReqSent = true;
                    accDataLoaded = true;                    
                }
                accDataLoad = false;
                //GEN_ReadyToAsk();
            }
            dataWriteIndex = 0;
        }
        
        
        if (SPI1STATbits.SRXMPT == 0)
        {            
            if (dataReadIndex <=15)
            {
                pRxBuff[dataReadIndex++] = SPI1BUF;
            }

    }
    }
    
    if ((accDataLoaded == false) && (accDataToMXIndex < 12) )
    {
        accDataReqSent = false;
        GEN_ReadyToAsk();
    }
    
    //DRV_SPITask();
    //***User Area End->code: External Interrupt 0***
    IFS0bits.SPI1IF = 0;
}