/*
 *  TX-only version for 담당자 1 (no interrupt, no semaphore)
 *  - 일정 주기마다 메시지 송신
 *  - UART로 메시지 ID 및 Payload 일부 출력
 *
 *  원본: TI mcan_external_read_write 예제 코드 기반
 */

#include <stdio.h>
#include <kernel/dpl/DebugP.h>
#include <kernel/dpl/AddrTranslateP.h>
#include <drivers/mcan.h>
#include "ti_drivers_config.h"
#include "ti_drivers_open_close.h"
#include "ti_board_open_close.h"

/* FreeRTOS vTaskDelay 사용 */
#include "FreeRTOS.h"
#include "task.h"

/* ========================================================================== */
/*                          매크로 및 전역 정의                               */
/* ========================================================================== */

#define APP_MCAN_BASE_ADDR                       (CONFIG_MCAN4_BASE_ADDR)
#define APP_MCAN_MSG_LOOP_COUNT                  (10U)    /* 사용 안 함 */
#define APP_MCAN_LOOPBACK_MODE_DISABLE           (FALSE)

/* Allocate Message RAM memory section to filter elements, buffers, FIFO */
/* Maximum STD Filter Element can be configured is 128 */
#define APP_MCAN_STD_ID_FILTER_CNT               (1U)
/* Maximum EXT Filter Element can be configured is 64 */
#define APP_MCAN_EXT_ID_FILTER_CNT               (0U)
/* Maximum TX Buffer + TX FIFO, combined can be configured is 32 */
#define APP_MCAN_TX_BUFF_CNT                     (1U)
#define APP_MCAN_TX_FIFO_CNT                     (0U)
/* Maximum TX Event FIFO can be configured is 32 */
#define APP_MCAN_TX_EVENT_FIFO_CNT               (0U)
/* Maximum RX FIFO 0 can be configured is 64 */
#define APP_MCAN_FIFO_0_CNT                      (0U)
/* Maximum RX FIFO 1 can be configured is 64 and
 * rest of the memory is allocated to RX buffer which is again of max size 64 */
#define APP_MCAN_FIFO_1_CNT                      (0U)

/* Standard Id configured in this app */
#define APP_MCAN_STD_ID                          (0xC0U)
#define APP_MCAN_STD_ID_MASK                     (0x7FFU)
#define APP_MCAN_STD_ID_SHIFT                    (18U)

#define APP_MCAN_EXT_ID_MASK                     (0x1FFFFFFFU)

/* In the CAN FD format, the Data length coding differs from the standard CAN.
 * In case of standard CAN it is 8 bytes */
static const uint8_t gMcanDataSize[16U] = {0U,  1U,  2U,  3U,
                                           4U,  5U,  6U,  7U,
                                           8U,  12U, 16U, 20U,
                                           24U, 32U, 48U, 64U};

static uint32_t          gMcanBaseAddr;

/* 송신 메시지 시퀀스 번호 */
static uint32_t          gTxMsgSeq = 0U;

/* Static Function Declarations */
static void    App_mcanConfig(Bool enableInternalLpbk);
static void    App_mcanInitMsgRamConfigParams(
               MCAN_MsgRAMConfigParams *msgRAMConfigParams);
static void    App_mcanConfigTxMsg(MCAN_TxBufElement *txMsg);
static void    App_mcanInitStdFilterElemParams(
                                  MCAN_StdMsgIDFilterElement *stdFiltElem,
                                  uint32_t bufNum);

void mcanEnableTransceiver(void);

/* ========================================================================== */
/*                          메인 함수 (TX 전용)                                */
/* ========================================================================== */

void mcan_external_read_write_main(void *args)
{
    int32_t           status = SystemP_SUCCESS;
    MCAN_TxBufElement txMsg;
    uint32_t          bufNum;
    uint32_t          stdId;

    /* 드라이버 (UART 포함) 오픈 */
    Drivers_open();
    Board_driversOpen();

    /* 트랜시버 Enable (IO Expander 통해 경로/스탠바이 설정) */
    mcanEnableTransceiver();

    DebugP_log("[MCAN TX] TX-only node, periodic transmit WITHOUT interrupt ...\r\n");

    /* MCAN base 주소 설정 */
    gMcanBaseAddr = (uint32_t) AddrTranslateP_getLocalAddr(APP_MCAN_BASE_ADDR);
    DebugP_log("[MCAN TX] gMcanBaseAddr=0x%08x\r\n", gMcanBaseAddr);

    /* MCAN 설정 (loopback 비활성화, 필터/bit timing 등) */
    App_mcanConfig(APP_MCAN_LOOPBACK_MODE_DISABLE);
    DebugP_log("[MCAN TX] App_mcanConfig done.\r\n");

    DebugP_log("[MCAN TX] Ready. Sending frames periodically (no ISR).\r\n");

    /* ===================== 연속 송신 루프 ===================== */
    bufNum = 0U;  /* Tx Buffer 0만 사용 */

    while(1)
    {
        /* 송신 메시지 구성 */
        App_mcanConfigTxMsg(&txMsg);

        /* Msg RAM에 데이터 쓰기 */
        MCAN_writeMsgRam(gMcanBaseAddr,
                         MCAN_MEM_TYPE_BUF,
                         bufNum,
                         &txMsg);

        /* 전송 요청 – 하드웨어가 버스 상태 보고 알아서 송신 */
        status = MCAN_txBufAddReq(gMcanBaseAddr, bufNum);
        if (status != CSL_PASS)
        {
            DebugP_logError("[MCAN TX] MCAN_txBufAddReq failed, status=%d\r\n", status);
        }

        /* 표준 ID 추출 */
        stdId = (txMsg.id >> APP_MCAN_STD_ID_SHIFT) & APP_MCAN_STD_ID_MASK;

        /* UART로 송신 정보 출력 */
        DebugP_log("[MCAN TX] Seq=%lu, ID=0x%03X, data[0..3]=%02X %02X %02X %02X\r\n",
                   (unsigned long)gTxMsgSeq,
                   (unsigned int)stdId,
                   (unsigned int)txMsg.data[0],
                   (unsigned int)txMsg.data[1],
                   (unsigned int)txMsg.data[2],
                   (unsigned int)txMsg.data[3]);

        gTxMsgSeq++;

        /* ★ 주기 조절: 파형 보기 편하게 10ms 주기 예시
         *   - 느리게:   pdMS_TO_TICKS(1000)  (1초)
         *   - 보통:     pdMS_TO_TICKS(10)
         *   - 거의 연속: pdMS_TO_TICKS(1) 또는 주석 처리
         */
        vTaskDelay(pdMS_TO_TICKS(1000));
    }

    /* 도달하지 않음 */
}

/* ========================================================================== */
/*                        MCAN 설정 및 헬퍼 함수                               */
/* ========================================================================== */

static void App_mcanConfig(Bool enableInternalLpbk)
{
    MCAN_StdMsgIDFilterElement stdFiltElem[APP_MCAN_STD_ID_FILTER_CNT] = {0U};
    MCAN_InitParams            initParams = {0U};
    MCAN_ConfigParams          configParams = {0U};
    MCAN_MsgRAMConfigParams    msgRAMConfigParams = {0U};
    MCAN_BitTimingParams       bitTimes = {0U};
    uint32_t                   i;

    /* Initialize MCAN module initParams */
    MCAN_initOperModeParams(&initParams);
    /* CAN FD Mode and Bit Rate Switch Enabled (원래 예제 설정 유지) */
    initParams.fdMode          = TRUE;
    initParams.brsEnable       = TRUE;

    /* Initialize MCAN module Global Filter Params */
    MCAN_initGlobalFilterConfigParams(&configParams);

    /* Initialize MCAN module Bit Time Params
     *  기본: Nominal 1Mbps, Data 5Mbps (TI 예제 기본값)
     */
    MCAN_initSetBitTimeParams(&bitTimes);

    /* Initialize MCAN module Message Ram Params */
    App_mcanInitMsgRamConfigParams(&msgRAMConfigParams);

    /* Initialize Filter element to receive msg, should be same as tx msg id */
    for (i = 0U; i < APP_MCAN_STD_ID_FILTER_CNT; i++)
    {
        App_mcanInitStdFilterElemParams(&stdFiltElem[i], i);
    }

    /* wait for memory initialization to happen */
    while (FALSE == MCAN_isMemInitDone(gMcanBaseAddr))
    {
        /* 여기서 너무 오래 걸리면 SysConfig나 baseAddr 문제 가능 */
    }

    /* Put MCAN in SW initialization mode */
    MCAN_setOpMode(gMcanBaseAddr, MCAN_OPERATION_MODE_SW_INIT);
    while (MCAN_OPERATION_MODE_SW_INIT != MCAN_getOpMode(gMcanBaseAddr))
    {}

    /* Initialize MCAN module */
    MCAN_init(gMcanBaseAddr, &initParams);
    /* Configure MCAN module Global Filter */
    MCAN_config(gMcanBaseAddr, &configParams);
    /* Configure Bit timings */
    MCAN_setBitTime(gMcanBaseAddr, &bitTimes);
    /* Configure Message RAM Sections */
    MCAN_msgRAMConfig(gMcanBaseAddr, &msgRAMConfigParams);
    /* Set Extended ID Mask */
    MCAN_setExtIDAndMask(gMcanBaseAddr, APP_MCAN_EXT_ID_MASK);

    /* Configure Standard ID filter element */
    for (i = 0U; i < APP_MCAN_STD_ID_FILTER_CNT; i++)
    {
        MCAN_addStdMsgIDFilter(gMcanBaseAddr, i, &stdFiltElem[i]);
    }

    if (TRUE == enableInternalLpbk)
    {
        MCAN_lpbkModeEnable(gMcanBaseAddr, MCAN_LPBK_MODE_INTERNAL, TRUE);
    }

    /* Take MCAN out of the SW initialization mode */
    MCAN_setOpMode(gMcanBaseAddr, MCAN_OPERATION_MODE_NORMAL);
    while (MCAN_OPERATION_MODE_NORMAL != MCAN_getOpMode(gMcanBaseAddr))
    {}

    return;
}

/* 8바이트 Classic CAN 스타일의 송신 메시지 구성 */
static void App_mcanConfigTxMsg(MCAN_TxBufElement *txMsg)
{
    uint32_t i;

    /* Initialize message to transmit */
    MCAN_initTxBufElement(txMsg);

    /* Standard message identifier 11 bit, stored into ID[28-18] */
    txMsg->id  = ((APP_MCAN_STD_ID & APP_MCAN_STD_ID_MASK) << APP_MCAN_STD_ID_SHIFT);

    /* 8바이트 DLC 사용 (Classic CAN 느낌) */
    txMsg->dlc = MCAN_DATA_SIZE_8BYTES;
    txMsg->fdf = FALSE; /* Classic CAN frame */
    txMsg->brs = FALSE;
    txMsg->xtd = FALSE; /* Extended id not configured */

    /* payload 예시
     *  data[0..3] : 시퀀스 번호
     *  data[4..7] : 0으로 채움
     */
    txMsg->data[0] = (uint8_t)((gTxMsgSeq >> 0)  & 0xFFU);
    txMsg->data[1] = (uint8_t)((gTxMsgSeq >> 8)  & 0xFFU);
    txMsg->data[2] = (uint8_t)((gTxMsgSeq >> 16) & 0xFFU);
    txMsg->data[3] = (uint8_t)((gTxMsgSeq >> 24) & 0xFFU);

    for (i = 4U; i < gMcanDataSize[MCAN_DATA_SIZE_8BYTES]; i++)
    {
        txMsg->data[i] = 0U;
    }

    return;
}

static void App_mcanInitStdFilterElemParams(MCAN_StdMsgIDFilterElement *stdFiltElem,
                                            uint32_t bufNum)
{
    /* sfid1 defines the ID of the standard message to be stored. */
    stdFiltElem->sfid1 = APP_MCAN_STD_ID;
    /* As buffer mode is selected, sfid2 should be bufNum[0 - 63] */
    stdFiltElem->sfid2 = bufNum;
    /* Store message in buffer */
    stdFiltElem->sfec  = MCAN_STD_FILT_ELEM_BUFFER;
    /* Below configuration is ignored if message is stored in buffer */
    stdFiltElem->sft   = MCAN_STD_FILT_TYPE_RANGE;

    return;
}

static void App_mcanInitMsgRamConfigParams(MCAN_MsgRAMConfigParams
                                           *msgRAMConfigParams)
{
    int32_t status;

    MCAN_initMsgRamConfigParams(msgRAMConfigParams);

    /* Configure the user required msg ram params */
    msgRAMConfigParams->lss = APP_MCAN_STD_ID_FILTER_CNT;
    msgRAMConfigParams->lse = APP_MCAN_EXT_ID_FILTER_CNT;
    msgRAMConfigParams->txBufCnt = APP_MCAN_TX_BUFF_CNT;
    msgRAMConfigParams->txFIFOCnt = APP_MCAN_TX_FIFO_CNT;
    /* Buffer/FIFO mode is selected */
    msgRAMConfigParams->txBufMode = MCAN_TX_MEM_TYPE_BUF;
    msgRAMConfigParams->txEventFIFOCnt = APP_MCAN_TX_EVENT_FIFO_CNT;
    msgRAMConfigParams->rxFIFO0Cnt = APP_MCAN_FIFO_0_CNT;
    msgRAMConfigParams->rxFIFO1Cnt = APP_MCAN_FIFO_1_CNT;
    /* FIFO blocking mode is selected */
    msgRAMConfigParams->rxFIFO0OpMode = MCAN_RX_FIFO_OPERATION_MODE_BLOCKING;
    msgRAMConfigParams->rxFIFO1OpMode = MCAN_RX_FIFO_OPERATION_MODE_BLOCKING;

    status = MCAN_calcMsgRamParamsStartAddr(msgRAMConfigParams);
    DebugP_assert(status == CSL_PASS);

    return;
}
