/*
 * Copyright (c) 2022, Shenzhen CVA Innovation CO.,LTD
 * All rights reserved.
 *
 * Shenzhen CVA Innovation CO.,LTD (CVA chip) is supplying this file for use
 * exclusively with CVA's microcontroller products. This file can be freely
 * distributed within development tools that are supporting such microcontroller
 * products.
 *
 * THIS SOFTWARE IS PROVIDED "AS IS".  NO WARRANTIES, WHETHER EXPRESS, IMPLIED
 * OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE.
 * CVA SHALL NOT, IN ANY CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL,
 * OR CONSEQUENTIAL DAMAGES, FOR ANY REASON WHATSOEVER.
 */

/*! \brief a loop memory to memory transfer
 */

/*******************************************************************************
 * the includes
 ******************************************************************************/

#include <stdint.h>
#include <string.h>
#include "mcu.h"
#include "uds_user.h"

/*******************************************************************************
 * the defines
 ******************************************************************************/
#define UDS_PHYS_RECV_MSG_ID (0x732)
#define UDS_FUNC_RECV_MSG_ID (0x7DF)
#define UDS_PHYS_RESP_MSG_ID (0x7B2)

#define APP_TX_TEST1_MSG_ID  (0x111)
#define APP_TX_TEST2_MSG_ID  (0x222)
#define APP_RX_TEST1_MSG_ID  (0x444)
#define APP_RX_TEST2_MSG_ID  (0x555)

#define UDS_RECV_BUF         (1026)
#define UDS_SEND_BUF         (128)

#define CAN_DATA_BUFFER_SIZE (8u)
#define CAN_BUFFER_FIFO_SIZE (16u)

int64_t     Get_Cur_Time_Stamp(void);
static int8_t FlexCanBoot_TxMessage(uint32_t msgId, const uint8_t* pData, uint8_t size);

/* Indication value with boot loader request from asw */
#define ASW_BOOT_REQ_ACTIVE   (0x55AAAA55ul)
/* Asw code head id to show asw is not empty */
#define ASW_HEAD_MASK         (0xAABBCCDDul)
#define ASW_TAIL_MASK         (0xFFFFFFFF - ASW_HEAD_MASK)

/*******************************************************************************
 * the typedefs
 ******************************************************************************/
typedef enum
{
    UDS_MSG_IDX_STD_RX_PHYS,
    UDS_MSG_IDX_STD_RX_FUNC,
    UDS_MSG_IDX_STD_RX_TEST1,
    UDS_MSG_IDX_STD_RX_TEST2,
    UDS_MSG_IDX_STD_TX_TEST1,
    UDS_MSG_IDX_STD_TX,
    UDS_MSG_IDX_STD_TEST1,
    UDS_MSG_IDX_STD_TEST2,

    UDS_MSG_IDX_NUM
} Uds_MsgIdIdxType;

typedef struct
{
    uint32_t id;
    uint8_t  data[CAN_DATA_BUFFER_SIZE];
    uint8_t  len;
    uint16_t timeStamp;
    uint32_t hrTimeStamp;
} FlexCan_FrameStructureType;

typedef struct
{
    FlexCan_FrameStructureType rxMsg[CAN_BUFFER_FIFO_SIZE];
    FlexCan_FrameStructureType txMsg[CAN_BUFFER_FIFO_SIZE];
    uint8_t                    wrIdx;
    uint8_t                    rdIdx;
} FlexCan_DataInfoType;

/*******************************************************************************
 * the globals
 ******************************************************************************/
McuType                      mcu;
volatile uint32_t            gSystick1msEvent = 0, gSystick1msCnt = 0, gTestRunCnt = 0, gTestIoEn = 0, gSysTick1sCnt = 0;
uint32_t                     gCpuClockFrequency       = 0;
int64_t                      timer_1ms                = 0;
uint8_t                      udsSendBuf[UDS_SEND_BUF] = {0};
uint8_t                      udsRecvBuf[UDS_RECV_BUF] = {0};
UdsType                      udsObj;
FlexCan_DataInfoType         flexCan_DataInfo;
FlexCanDrv_ControllerCfgType flexCanCfg;
FlexCanDrvType*              flexCanDrv_DemoObj;
uint8_t                      flexCanBoot_EnhanceRxFFCnt = 0;
uint8_t                      txMsgBuf[8]                = {0};
uint8_t                      aswVersion                 = 1;

Uds_ParamsType udsParam = {
    .isotpParams.framePadding = true,
    .isotpParams.blockSize    = 0,
    .isotpParams.recvPhysId   = UDS_PHYS_RECV_MSG_ID,
    .isotpParams.recvFuncId   = UDS_FUNC_RECV_MSG_ID,
    .isotpParams.sendid       = UDS_PHYS_RESP_MSG_ID,
    .isotpParams.sendBuf      = udsSendBuf,
    .isotpParams.sendBufSize  = UDS_SEND_BUF,
    .isotpParams.recvBuf      = udsRecvBuf,
    .isotpParams.recvBufSize  = UDS_RECV_BUF,
    .isotpParams.debug        = NULL,
    .isotpParams.sendCanMsg   = FlexCanBoot_TxMessage,
    .isotpParams.getTimeMs    = Get_Cur_Time_Stamp,
    .p2Server_ms              = 50,
    .p2xServer_10ms           = 500,
    .s3Server_ms              = 5000,
};

const FlexCanDrv_MsgCfgType msgCfgObj[UDS_MSG_IDX_NUM] = {
    {UDS_MSG_IDX_STD_RX_PHYS,  1, UDS_PHYS_RECV_MSG_ID, false, FLEXCANDRV_MSGTYPE_RX, DLC_BYTE_8, false, true,  0xFFFFFFFF}, /* CAN_MSGOBJ_STD_RX_PHYS */
    {UDS_MSG_IDX_STD_RX_FUNC,  1, UDS_FUNC_RECV_MSG_ID, false, FLEXCANDRV_MSGTYPE_RX, DLC_BYTE_8, false, true,  0xFFFFFFFF}, /* CAN_MSGOBJ_STD_RX_FUNC */
    {UDS_MSG_IDX_STD_RX_TEST1, 1, APP_RX_TEST1_MSG_ID,  false, FLEXCANDRV_MSGTYPE_RX, DLC_BYTE_8, false, true,  0xFFFFFFFF}, /* CAN_MSGOBJ_STD_RX_FUNC */
    {UDS_MSG_IDX_STD_RX_TEST2, 1, APP_RX_TEST2_MSG_ID,  false, FLEXCANDRV_MSGTYPE_RX, DLC_BYTE_8, false, true,  0xFFFFFFFF}, /* CAN_MSGOBJ_STD_RX_FUNC */
    {UDS_MSG_IDX_STD_TX,       1, UDS_PHYS_RESP_MSG_ID, false, FLEXCANDRV_MSGTYPE_TX, DLC_BYTE_8, false, false, 0xFFFFFFFF}, /* CAN_MSGOBJ_STD_TX */
    {UDS_MSG_IDX_STD_TEST1,    1, APP_TX_TEST1_MSG_ID,  false, FLEXCANDRV_MSGTYPE_TX, DLC_BYTE_8, false, false, 0xFFFFFFFF}, /* CAN_MSGOBJ_STD_TX */
    {UDS_MSG_IDX_STD_TEST2,    1, APP_TX_TEST2_MSG_ID,  false, FLEXCANDRV_MSGTYPE_TX, DLC_BYTE_8, false, false, 0xFFFFFFFF}, /* CAN_MSGOBJ_STD_TX */
};

#pragma location = ".bss.no_init"
static uint32_t sAswBoot_Req = 0;

#pragma location = ".asw_header"
__root const uint32_t sAswHeader = ASW_HEAD_MASK;

#pragma location = ".asw_tail"
__root const uint32_t sAswTail = ASW_TAIL_MASK;

/*******************************************************************************
 * the functions
 ******************************************************************************/

void Asw_SetBootloaderRequest(void)
{
    sAswBoot_Req = ASW_BOOT_REQ_ACTIVE;
}

int64_t Get_Cur_Time_Stamp(void)
{
    return timer_1ms;
}

void SysTick_Handler(void)
{
    gSystick1msEvent++;
    timer_1ms++;
    Uds_Tick(&udsObj);
}

void FlexCanBoot_TxRxIsr(void)
{
    uint8_t               i = 0;
    FlexCanDrv_MsgObjType msgObj;

    for(i = 0; i < flexCanCfg.msgNum; i++)
    {
        msgObj.msgBufId = i;

        if(FlexCanDrv_GetMsgObjFlag(flexCanDrv_DemoObj, &msgObj))
        {
            if(flexCanCfg.msgCfg[i].msgType == FLEXCANDRV_MSGTYPE_RX)
            {
                /* clear message buffer interrupt flag */
                FlexCanDrv_ClearMsgObjFlag(flexCanDrv_DemoObj, &msgObj);

                /* get the new message data */
                FlexCanDrv_GetRxMsg(flexCanDrv_DemoObj, &msgObj);

                memcpy(flexCan_DataInfo.rxMsg[flexCan_DataInfo.wrIdx].data, msgObj.data, msgObj.dlc);
                flexCan_DataInfo.rxMsg[flexCan_DataInfo.wrIdx].id  = msgObj.msgId;
                flexCan_DataInfo.rxMsg[flexCan_DataInfo.wrIdx].len = msgObj.dlc;
                flexCan_DataInfo.wrIdx++;
                if(flexCan_DataInfo.wrIdx >= CAN_BUFFER_FIFO_SIZE)
                {
                    flexCan_DataInfo.wrIdx = 0;
                }
            }
            else
            {
                /* clear message buffer interrupt flag */
                FlexCanDrv_ClearMsgObjFlag(flexCanDrv_DemoObj, &msgObj);
            }
        }
    }
}

void CAN_ORed_0_31_MB_Handler(void)
{
    FlexCanBoot_TxRxIsr();
}

void CAN_ORed_32_63_MB_Handler(void)
{
    //FlexCanBoot_TxRxIsr();
}

bool FlexCanBoot_ReadoutMsg(FlexCan_FrameStructureType *pRxMsgObj)
{
    bool ret = false;

    if(flexCan_DataInfo.wrIdx != flexCan_DataInfo.rdIdx)
    {
        memcpy(pRxMsgObj, &flexCan_DataInfo.rxMsg[flexCan_DataInfo.rdIdx], sizeof(FlexCan_FrameStructureType));
        flexCan_DataInfo.rdIdx++;
        if(flexCan_DataInfo.rdIdx >= CAN_BUFFER_FIFO_SIZE)
        {
            flexCan_DataInfo.rdIdx = 0;
        }

        ret = true;
    }

    return ret;
}

static int8_t FlexCanBoot_TxMessage(uint32_t msgId, const uint8_t *pData, uint8_t size)
{
    FlexCanDrv_MsgObjType txMsgObj;
    uint8_t               msgIdx = 0, i = 0;

    for(i = 0; i < flexCanCfg.msgNum; i++)
    {
        if(msgId == flexCanCfg.msgCfg[i].msgId)
        {
            msgIdx = i;
            break;
        }
    }

    txMsgObj.msgBufId = flexCanCfg.msgCfg[msgIdx].msgBufId;
    txMsgObj.dlc      = size;
    txMsgObj.msgId    = flexCanCfg.msgCfg[msgIdx].msgId;
    memcpy(&txMsgObj.data[0], pData, size);

    FlexCanDrv_SetTxMsg(flexCanDrv_DemoObj, &txMsgObj);
    /* transmit standard CAN Tx message */
    FlexCanDrv_TransmitMsg(flexCanDrv_DemoObj, &txMsgObj);

    return 0;
}

void FlexCanBoot_Init(void)
{
    uint32_t busClockFreq = 0;

    flexCanDrv_DemoObj = &mcu.flexCanDrv;

    /* set PTC1 MUX as GPIO */
    PinsDrv_SetMuxModeSel(&mcu.ptc, 1, PINSDRV_MUX_AS_GPIO);
    /* set PTC1 as GPIO output */
    PinsDrv_SetPinDirection(&mcu.ptc, 1, 1);
    /* set PTC1 as high to control CAN transceiver STB */
    PinsDrv_WritePin(&mcu.ptc, 1, 0);

    /* set CAN0.RX */
    PinsDrv_SetMuxModeSel(&mcu.ptb, 15, PINSDRV_MUX_ALT6);

    /* set CAN0.TX */
    PinsDrv_SetMuxModeSel(&mcu.ptb, 14, PINSDRV_MUX_ALT6);

    flexCanCfg.clkSrc               = FLEXCANDRV_CLKSRC_CHICLK;
    flexCanCfg.fdEnable             = false;
    flexCanCfg.fdISOEnable          = false;
    flexCanCfg.msgBufDataLenSel     = FLEXCANDRV_MB_SIZE_BYTE_8;
    flexCanCfg.individualMaskEnable = true;

    if(flexCanCfg.clkSrc == FLEXCANDRV_CLKSRC_CHICLK)
    {
        ClockDrv_GetFreq(&mcu.clockDrv, CLOCKDRV_APB, &busClockFreq);
    }
    else
    {
        ClockDrv_GetFreq(&mcu.clockDrv, CLOCKDRV_SOSC_DIV, &busClockFreq);
    }

    if(flexCanCfg.fdEnable == true)
    {
        FlexCanDrv_BitTimingCalc(&flexCanCfg.fdBitTiming,
                                 busClockFreq, /* module clock source: 16M */
                                 2000000,      /* baudrate: 2M */
                                 7500,         /* sample point: 75% */
                                 2000,         /* SJW: 20% */
                                 1);           /* FD bit timing */
    }

    FlexCanDrv_BitTimingCalc(&flexCanCfg.bitTiming,
                             busClockFreq, /* module clock source: 16M */
                             500000,       /* baudrate: 500K */
                             7500,         /* sample point: 75% */
                             2500,         /* SJW: 20% */
                             0);           /* classic CAN bit timing */

    /* initialize CAN module */
    FlexCanDrv_Configure(flexCanDrv_DemoObj, &flexCanCfg);

    /* enable all CAN mailbox interrupt */
    IrqDrv_EnableIrq(CAN_ORed_0_31_MB_IRQn);
    IrqDrv_EnableIrq(CAN_ORed_32_63_MB_IRQn);
}

int main(void)
{
    FlexCan_FrameStructureType rxMsg;
    IrqDrv_DisableGlobalInterrupt();

    /* Setup the clock */
    ClockDrv_ModuleClkConfigType clockConfig;

    /* Initialize all MCU drivers: flash drv included */
    Mcu_Init(&mcu);

    /* Setup the Pll div2 clock */
    clockConfig.gating = true;
    clockConfig.source = CLOCKDRV_PLL;
    clockConfig.div    = 1;
    ClockDrv_ConfigureClock(&mcu.clockDrv, CLOCKDRV_PLL_DIV, &clockConfig);

    /* Enable the clock for all ports */
    clockConfig.gating = true;
    ClockDrv_ConfigureClock(&mcu.clockDrv, CLOCKDRV_PORTA, &clockConfig);
    ClockDrv_ConfigureClock(&mcu.clockDrv, CLOCKDRV_PORTB, &clockConfig);
    ClockDrv_ConfigureClock(&mcu.clockDrv, CLOCKDRV_PORTC, &clockConfig);

    PinsDrv_SetMuxModeSel(&mcu.ptc, 3, PINSDRV_MUX_AS_GPIO);
    PinsDrv_SetPinDirection(&mcu.ptc, 3, 1);
    PinsDrv_SetPullSel(&mcu.ptc,3, PINSDRV_INTERNAL_PULL_UP);
    PinsDrv_WritePin(&mcu.ptc,3, 1);

    PinsDrv_SetMuxModeSel(&mcu.ptc, 1, PINSDRV_MUX_AS_GPIO);
    PinsDrv_SetPinDirection(&mcu.ptc, 1, 1);
    PinsDrv_SetPullSel(&mcu.ptc, 1, PINSDRV_INTERNAL_PULL_UP);
    PinsDrv_WritePin(&mcu.ptc, 1, 1);

    PinsDrv_SetMuxModeSel(&mcu.ptc, 2, PINSDRV_MUX_AS_GPIO);
    PinsDrv_SetPinDirection(&mcu.ptc, 2, 1);
    PinsDrv_SetPullSel(&mcu.ptc, 2, PINSDRV_INTERNAL_PULL_UP);
    PinsDrv_WritePin(&mcu.ptc, 2, 0);

    /* CAN init */
    memset(&flexCan_DataInfo, 0, sizeof(flexCan_DataInfo));
    memset(&flexCanCfg, 0, sizeof(flexCanCfg));

    /* get CAN controller default configuration */
    FlexCanDrv_GetDefaultCfg(&flexCanCfg);

    flexCanCfg.msgNum = sizeof(msgCfgObj) / sizeof(FlexCanDrv_MsgCfgType);
    flexCanCfg.msgCfg = msgCfgObj;

    FlexCanBoot_Init();

    /* UDS init */
    Uds_UserInit(&udsObj, &udsParam);
    /* Set system tick clock, 1ms event */
    ClockDrv_GetFreq(&mcu.clockDrv, CLOCKDRV_CORE, &gCpuClockFrequency);
    SysTick_Config(gCpuClockFrequency / 1000u);
    IrqDrv_EnableIrq(SysTick_IRQn);

    IrqDrv_EnableGlobalInterrupt();

    while(1)
    {
        if(gSystick1msEvent > 0u)
        {
            if(udsObj.session == UDS_SESSION_PROGRAMMING)
            {
                Asw_SetBootloaderRequest();
                ResetDrv_SoftwareResetModule(&mcu.resetDrv, RESETDRV_SWRESET_SYS);
            }

            gSystick1msEvent--;
            gSystick1msCnt++;
            gSysTick1sCnt++;

            if(aswVersion == 0)
            {
                if(gSysTick1sCnt >= 1000)
                {
                    gSysTick1sCnt = 0;
                    if(gTestIoEn == 0)
                    {
                        gTestIoEn = 1;
                        PinsDrv_WritePin(&mcu.ptc, 3, 1);
                        PinsDrv_WritePin(&mcu.ptc, 1, 1);
                        PinsDrv_WritePin(&mcu.ptc, 2, 1);
                    }
                    else
                    {
                        gTestIoEn = 0;
                        PinsDrv_WritePin(&mcu.ptc, 30, 0);
                        PinsDrv_WritePin(&mcu.ptc, 1, 0);
                        PinsDrv_WritePin(&mcu.ptc, 2, 0);
                    }
                }

                if(gSystick1msCnt >= 2)
                {
                    uint8_t txMsgBuf[8] = {0};
                    gSystick1msCnt = 0;
                    txMsgBuf[0]    = aswVersion;
                    txMsgBuf[1] = aswVersion;
                    txMsgBuf[2]    = ((sAswBoot_Req >> 24)& 0xFF);
                    txMsgBuf[3]    = ((sAswBoot_Req >> 16) & 0xFF);
                    txMsgBuf[4]    = ((sAswBoot_Req >> 8) & 0xFF);
                    txMsgBuf[5]    = ((sAswBoot_Req >> 0) & 0xFF);
                    txMsgBuf[6]    = gSysTick1sCnt & 0xFF;

                    FlexCanBoot_TxMessage(APP_TX_TEST1_MSG_ID, txMsgBuf, 8);
                }
            }
            else if(aswVersion == 1)
            {
                if(gSystick1msCnt >= 10)
                {
                    uint8_t txMsgBuf[8] = {0};
                    gSystick1msCnt = 0;
                    txMsgBuf[0]    = aswVersion;
                    txMsgBuf[1]    = aswVersion;
                    txMsgBuf[2]    = ((sAswBoot_Req >> 24) & 0xFF);
                    txMsgBuf[3]    = ((sAswBoot_Req >> 16) & 0xFF);
                    txMsgBuf[4]    = ((sAswBoot_Req >> 8) & 0xFF);
                    txMsgBuf[5]    = ((sAswBoot_Req >> 0) & 0xFF);
                    txMsgBuf[6]    = gSysTick1sCnt & 0xFF;

                    FlexCanBoot_TxMessage(APP_TX_TEST2_MSG_ID, txMsgBuf, 8);
                }

                if(gSysTick1sCnt >= 1000)
                {
                    static uint8_t blueLed = 0, redLed = 0, greenLed = 0;

                    gSysTick1sCnt = 0;
                    if(blueLed == 0 && redLed == 0 && greenLed == 0)
                    {
                        PinsDrv_WritePin(&mcu.ptc, 3, 0);
                        PinsDrv_WritePin(&mcu.ptc, 1, 1);
                        PinsDrv_WritePin(&mcu.ptc, 2, 1);
                        blueLed  = 1;
                        redLed   = 0;
                        greenLed = 0;
                    }
                    else if(blueLed == 1 && redLed == 0 && greenLed == 0)
                    {
                        PinsDrv_WritePin(&mcu.ptc, 30, 0);
                        PinsDrv_WritePin(&mcu.ptc, 1, 0);
                        PinsDrv_WritePin(&mcu.ptc, 2, 1);
                        blueLed  = 1;
                        redLed   = 1;
                        greenLed = 0;
                    }
                    else if(blueLed == 1 && redLed == 1 && greenLed == 0)
                    {
                        PinsDrv_WritePin(&mcu.ptc, 3, 0);
                        PinsDrv_WritePin(&mcu.ptc, 1, 0);
                        PinsDrv_WritePin(&mcu.ptc, 2, 0);
                        blueLed  = 1;
                        redLed   = 1;
                        greenLed = 1;
                    }
                    else if(blueLed == 1 && redLed == 1 && greenLed == 1)
                    {
                        PinsDrv_WritePin(&mcu.ptc, 3, 1);
                        PinsDrv_WritePin(&mcu.ptc, 1, 1);
                        PinsDrv_WritePin(&mcu.ptc, 2, 1);
                        blueLed  = 0;
                        redLed   = 0;
                        greenLed = 0;
                    }
                }
            }

            /* Handler user routine */
            if(FlexCanBoot_ReadoutMsg(&rxMsg) == true)
            {
                if((rxMsg.id == UDS_PHYS_RECV_MSG_ID) || (rxMsg.id == UDS_FUNC_RECV_MSG_ID))
                {
                    IsoTp_HandleIncomingCanMsg(&udsObj.isotp, rxMsg.id, rxMsg.data, rxMsg.len);
                }
                else
                {
                    uint8_t txMsgBuf[8] = {0};
                    gSystick1msCnt = 0;
                    txMsgBuf[0]    = aswVersion;
                    txMsgBuf[1]    = ((rxMsg.id >> 8) & 0xFF);
                    txMsgBuf[2]    = ((rxMsg.id >> 0) & 0xFF);
                    txMsgBuf[3]    ++;

                    if(aswVersion == 0)
                    {
                        txMsgBuf[7] = 0x55;
                        FlexCanBoot_TxMessage(APP_TX_TEST2_MSG_ID, txMsgBuf, 8);
                    }
                    else
                    {
                        txMsgBuf[7] = 0xaa;
                        FlexCanBoot_TxMessage(APP_TX_TEST2_MSG_ID, txMsgBuf, 8);
                    }
                }
            }

            Uds_Run(&udsObj);
        }
    }
}
