#include "CDD_Usart.h"
#include "Mcu.h"
#include "Port.h"

#include "Fee.h"
#include "Fls.h"
#include "Det.h"

#include "Mcu_RcmReg.h"
#include "Fee_InternalTypes.h"

extern VAR(Fee_ClrGrpInfoType, FEE_VAR) gs_feeClusterInfo[FEE_CLUSTER_GROUPS_NUMBER];

/***************************************************************************
                        CONSTANT DEFINITION
***************************************************************************/
/* Block 0 Content */
uint8 FeeTest_Block0[BLOCK_SIZE_BYTE] = {0x00, 0x00, 0x00, 0x00,
                                         0x00, 0x00, 0x00, 0x00,
                                         0x00, 0x00, 0x00, 0x00,
                                         0x00, 0x00, 0x00, 0x00};

/* Block 1 Content */
uint8 FeeTest_Block1[BLOCK_SIZE_BYTE] = {0x11, 0x11, 0x11, 0x11,
                                         0x11, 0x11, 0x11, 0x11,
                                         0x11, 0x11, 0x11, 0x11,
                                         0x11, 0x11, 0x11, 0x11};

/* Block 2 Content */
uint8 FeeTest_Block2[BLOCK_SIZE_BYTE] = {0x22, 0x22, 0x22, 0x22,
                                         0x22, 0x22, 0x22, 0x22,
                                         0x22, 0x22, 0x22, 0x22,
                                         0x22, 0x22, 0x22, 0x22};

/* Block 3 Content */
uint8 FeeTest_Block3[BLOCK_SIZE_BYTE] = {0x33, 0x33, 0x33, 0x33,
                                         0x33, 0x33, 0x33, 0x33,
                                         0x33, 0x33, 0x33, 0x33,
                                         0x33, 0x33, 0x33, 0x33};

/* Write Fail Content */
uint8 FeeTest_Fail[BLOCK_SIZE_BYTE]  =  {0x44, 0x44, 0x44, 0x44,
                                         0x44, 0x44, 0x44, 0x44,
                                         0x44, 0x44, 0x44, 0x44,
                                         0x44, 0x44, 0x44, 0x44};

/**************************************************************************
                            FUNCTION
**************************************************************************/     
void Fls_EccCompletionNotificaiton(void)
{
    
}
                                         
FUNC( void, FLS_CODE ) Fee_Block0_Callback(CONST(uint32, AUTOMATIC) Addr)
{
    uint8 i = 0U;

    printf("\r\nBlock 0 Write Failed!\r\n");
    printf("\r\nRead latest Data:\r\n");

    for(i = 0; i < BLOCK_SIZE_BYTE; i++)
    {
        printf("0x%02X ", (*(volatile uint8 *)(Addr + FLS_ABSOLUTE_BASE_ADDRESS + i)));

        if ((i + 1) % 4 == 0)
        {
            printf("\r\n");
        }
    }
}

FUNC( void, FLS_CODE ) Fee_Block1_Callback(CONST(uint32, AUTOMATIC) Addr)
{
    uint8 i = 0U;

    printf("\r\nBlock 1 Write Failed!\r\n");
    printf("\r\nRead latest Data:\r\n");

    for(i = 0; i < BLOCK_SIZE_BYTE; i++)
    {
        printf("0x%02X ", (*(volatile uint8 *)(Addr + FLS_ABSOLUTE_BASE_ADDRESS + i)));

        if ((i + 1) % 4 == 0)
        {
            printf("\r\n");
        }
    }
}

FUNC( void, FLS_CODE ) Fee_Block2_Callback(CONST(uint32, AUTOMATIC) Addr)
{
    uint8 i = 0U;

    printf("\r\nBlock 2 Write Failed!\r\n");
    printf("\r\nRead latest Data:\r\n");

    for(i = 0; i < BLOCK_SIZE_BYTE; i++)
    {
        printf("0x%02X ", (*(volatile uint8 *)(Addr + FLS_ABSOLUTE_BASE_ADDRESS + i)));

        if ((i + 1) % 4 == 0)
        {
            printf("\r\n");
        }
    }
}

FUNC( void, FLS_CODE ) Fee_Block3_Callback(CONST(uint32, AUTOMATIC) Addr)
{
    uint8 i = 0U;

    printf("\r\nBlock 3 Write Failed!\r\n");
    printf("\r\nRead latest Data:\r\n");

    for(i = 0; i < BLOCK_SIZE_BYTE; i++)
    {
        printf("0x%02X ", (*(volatile uint8 *)(Addr + FLS_ABSOLUTE_BASE_ADDRESS + i)));

        if ((i + 1) % 4 == 0)
        {
            printf("\r\n");
        }
    }
}

/**************************************************************************
                      LOCAL FUNCTION
**************************************************************************/
/* Carry out FEE tasks */
static void Fee_WaitTask(MemIf_JobResultType job)
{
    while((Fee_GetJobResult() == job) &&
          (0 == g_detEventErrNum)&&
          (0 == g_detRuntimeEventErrNum))
    {
        Fee_MainFunction();
        Fls_MainFunction();
    }
}

/* Set watchdog parameters */
static void SetWdg(void)
{
    if(0U != REG_BIT_GET32(RCM_CSTS_REG_ADDR, RCM_CSTS_IWDTRSTFLG_MASK))
    {
        REG_BIT_SET32(RCM_CSTS_REG_ADDR, RCM_CSTS_RSTFLGCLR_MASK);
    }
    else
    {
        /* do nothing */
    }

    REG_WRITE32((uint32)0x40003000, 0x5555);
    REG_WRITE32((uint32)0x40003004, 0);
#ifdef __ICCARM__
    REG_WRITE32((uint32)0x40003008, 8);
#elif(defined(__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050))
    REG_WRITE32((uint32)0x40003008, 6);
#elif(defined(__GNUC__))
    REG_WRITE32((uint32)0x40003008, 7);
#endif
    REG_WRITE32((uint32)0x40003000, 0xAAAA);
    REG_WRITE32((uint32)0x40003000, 0xCCCC);
}

/**************************************************************************
                      main.c
**************************************************************************/
int main(void)
{
    Std_ReturnType apiReturn;
    uint32 i = 0U;
    /* Store the read results */
    uint8 testReadBuffer[BLOCK_SIZE_BYTE] = {0};

    Mcu_Init(&g_mcuConfig);
    Mcu_InitClock(McuClockSettingConfig_0);
    Port_Init(&g_portConfig);
    Usart_Init(g_usartConfg);  //TX PIN:PA9

    Fls_Init(&g_flsConfig);
    NVIC_EnableIRQ(FLASH_IRQn);
    Fee_Init(NULL_PTR);
    Fee_WaitTask(MEMIF_JOB_PENDING);

    /* Read flash to trig ECC interrupt */
    if(0U != REG_BIT_GET32(RCM_CSTS_REG_ADDR, RCM_CSTS_IWDTRSTFLG_MASK))
    {
        Fls_Read(gs_feeClusterInfo[0].dataAddrCur, &testReadBuffer[0], BLOCK_SIZE_BYTE);
        Fls_MainFunction();
    }

    /* Write Block 0,1,2 */
    if(0U == REG_BIT_GET32(RCM_CSTS_REG_ADDR, RCM_CSTS_IWDTRSTFLG_MASK))
    {
        apiReturn = Fee_Write(FeeConf_FeeBlockConfiguration_FeeBlockConfiguration_0, FeeTest_Block0);

        if(apiReturn == E_OK)
        {
            Fee_WaitTask(MEMIF_JOB_PENDING);
        }

        printf("\r\nBlock 0 Write Complete!\r\n");

        apiReturn = Fee_Read(FeeConf_FeeBlockConfiguration_FeeBlockConfiguration_0, 0, testReadBuffer, BLOCK_SIZE_BYTE);

        if(apiReturn == E_OK)
        {
            Fee_WaitTask(MEMIF_JOB_PENDING);
        }

        printf("\r\nRead Block 0 Data:\r\n");

        for(i = 0; i < BLOCK_SIZE_BYTE; ++i)
        {
            printf("0x%02X ", testReadBuffer[i]);

            if ((i + 1) % 4 == 0)
            {
                printf("\r\n");
            }
        }

        apiReturn = Fee_Write(FeeConf_FeeBlockConfiguration_FeeBlockConfiguration_1, FeeTest_Block1);

        if(apiReturn == E_OK)
        {
            Fee_WaitTask(MEMIF_JOB_PENDING);
        }

        printf("\r\nBlock 1 Write Complete!\r\n");

        apiReturn = Fee_Read(FeeConf_FeeBlockConfiguration_FeeBlockConfiguration_1, 0, testReadBuffer, BLOCK_SIZE_BYTE);

        if(apiReturn == E_OK)
        {
            Fee_WaitTask(MEMIF_JOB_PENDING);
        }

        printf("\r\nRead Block 1 Data:\r\n");

        for(i = 0; i < BLOCK_SIZE_BYTE; ++i)
        {
            printf("0x%02X ", testReadBuffer[i]);

            if ((i + 1) % 4 == 0)
            {
                printf("\r\n");
            }
        }

        apiReturn = Fee_Write(FeeConf_FeeBlockConfiguration_FeeBlockConfiguration_2, FeeTest_Block2);

        if(apiReturn == E_OK)
        {
            Fee_WaitTask(MEMIF_JOB_PENDING);
        }

        printf("\r\nBlock 2 Write Complete!\r\n");

        apiReturn = Fee_Read(FeeConf_FeeBlockConfiguration_FeeBlockConfiguration_2, 0, testReadBuffer, BLOCK_SIZE_BYTE);

        if(apiReturn == E_OK)
        {
            Fee_WaitTask(MEMIF_JOB_PENDING);
        }

        printf("\r\nRead Block 2 Data:\r\n");

        for(i = 0; i < BLOCK_SIZE_BYTE; ++i)
        {
            printf("0x%02X ", testReadBuffer[i]);

            if ((i + 1) % 4 == 0)
            {
                printf("\r\n");
            }
        }
    }

    /* Write data to the flash and intentionally make the write fail */
    if(0U == REG_BIT_GET32(RCM_CSTS_REG_ADDR, RCM_CSTS_IWDTRSTFLG_MASK))
    {
        apiReturn = Fee_Write(FeeConf_FeeBlockConfiguration_FeeBlockConfiguration_2, FeeTest_Fail);

        if(apiReturn == E_OK)
        {
            /* watchdog begin to run */
            SetWdg();

            Fee_WaitTask(MEMIF_JOB_PENDING);
        }
    }

    /* Write Block 3 */
    apiReturn = Fee_Write(FeeConf_FeeBlockConfiguration_FeeBlockConfiguration_3, FeeTest_Block3);

    if(apiReturn == E_OK)
    {
        Fee_WaitTask(MEMIF_JOB_PENDING);
    }

    printf("\r\nBlock 3 Write Complete!\r\n");

    apiReturn = Fee_Read(FeeConf_FeeBlockConfiguration_FeeBlockConfiguration_3, 0, testReadBuffer, BLOCK_SIZE_BYTE);

    if(apiReturn == E_OK)
    {
        Fee_WaitTask(MEMIF_JOB_PENDING);
    }

    printf("\r\nRead Block 3 Data:\r\n");

    for(i = 0; i < BLOCK_SIZE_BYTE; ++i)
    {
        printf("0x%02X ", testReadBuffer[i]);

        if ((i + 1) % 4 == 0)
        {
            printf("\r\n");
        }
    }

    while(1)
    {

    }
}

#if defined (__CC_ARM) || defined (__ICCARM__) || (defined(__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050))

/*!
* @brief       Redirect C Library function printf to serial port.
*              After Redirection, you can use printf function.
*
* @param       ch:  The characters that need to be send.
*
* @param       *f:  pointer to a FILE that can recording all information
*              needed to control a stream
*
* @retval      The characters that need to be send.
*
* @note
*/
int fputc(int ch, FILE* f)
{
    int reVal = 0;
    reVal = Usart_PutRedefinition(USART1_ID,&ch);

    return reVal;
}

/* If configuring the full library using versions after IAR 9.30,
   it is necessary to implement the _write function for printf redirection.*/
size_t __write(int handle,const unsigned char *buf,size_t bufSize)
{
    int32 tempCh = 0;
    for(int i=0; i<bufSize; i++)
    {
        Usart_PutRedefinition(USART1_ID,&tempCh);
    }
    return bufSize;
}

#elif defined (__GNUC__)

/* For Arm Cross GCC: Eclipse */
int _write(int fd,const unsigned char *buf,int len)
{
    int32 tempCh = 0;
    for(int i=0; i<len; i++)
    {
        int32 tempCh = (int32)buf[i];
        Usart_PutRedefinition(USART1_ID,&tempCh);
    }
    return len;
}

#endif

