#include "cantp.h"
#include "cantp_cfg.h"
#include "cantp_core.h"
#include "cantp_hal.h"
#include "cantp_fifo.h"

cantp_rx_indication_type CanTp_rxIndicationCallback;
cantp_tx_confirmation_type CanTp_txConfirmationCallback;
const cantp_ch_config_array_type_t* CanTp_channelArrayPtr;
static cantp_fifo_info_type_t** CanTp_fifoArray;

cantp_return_type CanTp_Init(const cantp_config_t* configPtr)
{
    CanTp_Hal_Init();
    CanTp_Fifo_Init();
    CanTp_Core_Init(configPtr);
    CanTp_fifoArray = CanTp_Fifo_GetFifoArray();
    CanTp_channelArrayPtr = configPtr->chConfig;
    CanTp_rxIndicationCallback = configPtr->rxCallback;
    CanTp_txConfirmationCallback = configPtr->txCallback;
    return CANTP_E_OK;
}

void CanTp_TimeService()
{
    CanTp_Core_DecreaseCounter();
}

void CanTp_MainFunction(void)
{
    CanTp_Hal_Mainfunction();
    CanTp_Core_MainFunction();

    /* Check whether has some msg to transmit */
    for (cantp_u8 idx = 0; idx < CANTP_CHANNEL_NUM; idx++)
    {
        CanTp_Core_TxNode(idx);
    }

}

cantp_u32 CanTp_GetPendingLength(sdu_id_type sduId)
{
    cantp_u32 pendingLength = 0;
    cantp_u8 fifoId = sduId * 2;
    CanTp_Fifo_IsWaitTx(fifoId, &pendingLength);
    return pendingLength;
}


buf_req_return_type CanTp_CopyTxData (
    sdu_id_type sduId,
    const pdu_info_type_t * pduInfoPtr,
    const retry_info_type_t * retryInfoPtr,
    pdu_length_type * availableDataPtr
)
{
    cantp_fifo_id_type fifoId = sduId * 2U;
    buf_req_return_type res = CANTP_BUF_REQ_OK;
    cantp_fifo_res_type fifoRes = CANTP_FIFO_RES_SUCCESS;
    (void)availableDataPtr;

    if (retryInfoPtr != CANTP_NULL && CANTP_DATA_RETRY == retryInfoPtr->tpDataState)
    {
        cantp_fifo_msg_info_type_t* msgInfoPtr = CanTp_Fifo_GetMsgPtr(fifoId);
        msgInfoPtr->copiedLoc -= retryInfoPtr->txTpDataCnt;
    }
    fifoRes = CanTp_Fifo_PopPduData(fifoId, pduInfoPtr->sduLength, pduInfoPtr->sduDataPtr);
    if (CANTP_FIFO_RES_SUCCESS != fifoRes)
    {
        res = CANTP_BUF_REQ_NOT_OK;
    }
    return res;
}

buf_req_return_type CanTp_CopyRxData (
    pdu_id_type id,
    const pdu_info_type_t * pduInfoPtr,
    pdu_length_type * bufferSizePtr
)
{
    buf_req_return_type res = CANTP_BUF_REQ_OK;
    cantp_fifo_id_type fifoId = id * 2 + 1;
    do
    {
        if (id >= CANTP_FIFO_NUM)
        {
            /* can not make any buffer available with Wrong SduId. */
            res = CANTP_BUF_REQ_NOT_OK;
            break;
        }
        if (pduInfoPtr == CANTP_NULL)
        {
            /* TODO: after update fifo */
            *bufferSizePtr = 0xFFFFFFFF;
            break;
        }
        CanTp_Fifo_PushPduData(fifoId, pduInfoPtr->sduLength, pduInfoPtr->sduDataPtr);
    } while (CANTP_FALSE);

    return res;
}

buf_req_return_type CanTp_StartOfReception (
    pdu_id_type id,
    const pdu_info_type_t * pduInfoPtr,
    pdu_length_type tpSduLength,
    pdu_length_type * bufferSizePtr
)
{
    buf_req_return_type res = CANTP_BUF_REQ_OK;
    cantp_fifo_id_type fifoId = id * 2 + 1U;
    do
    {
        if (fifoId >= CANTP_FIFO_NUM)
        {
            /* can not make any buffer available with Wrong SduId. */
            res = CANTP_BUF_REQ_NOT_OK;
            break;
        }
        /**
         * If received a SF or FF when rxNode is already in receiving, the receiving will be abort
         * and restart a receiving. This branch will not be hit.
         */
        if (CanTp_fifoArray[fifoId]->lastMsgInfoPtr != CANTP_NULL)
        {
            res = CANTP_BUF_REQ_BUSY;
            break;
        }
        /* Actual write len is: InfoType + MetaData + SduData */
        *bufferSizePtr = CanTp_Fifo_GetCanWriteLen(fifoId) - (sizeof(cantp_fifo_msg_info_type_t) + META_DATA_LEN);
        if (*bufferSizePtr < tpSduLength)
        {
            res = CANTP_BUF_REQ_OVFL;
            break;
        }

        CanTp_Fifo_StartSdu(fifoId, pduInfoPtr);
    } while (CANTP_FALSE);
    return res;
}

cantp_return_type CanTp_Transmit(sdu_id_type sduId, pdu_info_type_t* sduInfoPtr)
{
    cantp_return_type res = CANTP_E_NOT_OK;
    do {
        if ((CanTp_channelArrayPtr[sduId].addrType == CANTP_ADDR_TYPE_FUNCTIONAL) && 
            (CanTp_channelArrayPtr[sduId].SF_DL < sduInfoPtr->sduLength))
        {
            break;
        }

        cantp_fifo_res_type fifoRes = CanTp_Fifo_PushSdu(sduId * 2, sduInfoPtr);
        if (fifoRes == CANTP_FIFO_RES_SUCCESS)
        {
            res = CANTP_E_OK;
        }
    } while (CANTP_FALSE);
    return res;
}

void CanTp_TxConfirmation (sdu_id_type sduId, cantp_core_res_type res)
{
    cantp_fifo_id_type fifoId = sduId * 2;
    CanTp_Fifo_PopSdu(fifoId);
    if (CanTp_txConfirmationCallback != CANTP_NULL)
    {
        CanTp_txConfirmationCallback(sduId, res);
    }
}

void CanTp_RxIndication(sdu_id_type sduId, cantp_return_type res)
{
    cantp_fifo_id_type fifoId = sduId * 2 + 1;
    CanTp_Fifo_FinishSdu(fifoId, res);
    if (CanTp_rxIndicationCallback != CANTP_NULL)
    {
        CanTp_rxIndicationCallback(sduId, res);
    }

}

cantp_copy_res_type CanTp_CopyRxSdu(pdu_id_type sduId, pdu_info_type_t* sduInfoPtr)
{
    cantp_copy_res_type res = CANTP_COPY_EMPTY;
    cantp_fifo_id_type fifoId = sduId * 2 + 1;
    if (CanTp_fifoArray[fifoId] != CANTP_NULL && CanTp_fifoArray[fifoId]->fifoStatus != FIFO_EMPTY)
    {
        cantp_fifo_res_type fifoRes = CanTp_Fifo_GetSdu(fifoId, sduInfoPtr);
        switch (fifoRes) {
            case CANTP_FIFO_RES_SUCCESS:
                res = CANTP_COPY_OK;
                break;
            case CANTP_FIFO_RES_MEM_NOT_ENOUGH:
                res = CANTP_COPY_MEM_NOT_ENOUGH;
                break;
            default:
                res = CANTP_COPY_ERROR;
                break;
        }
        CanTp_Fifo_PopSdu(fifoId);        
    }
    return res;
}

void CanTp_InstallIndication(cantp_rx_indication_type callback)
{
    CanTp_rxIndicationCallback = callback;
}

void CanTp_InstallConfirmation(cantp_tx_confirmation_type callback)
{
    CanTp_txConfirmationCallback = callback;
}
