/*
 * 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.
 */

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

#include <stdint.h>
#include <string.h>
#include "lin_schedule.h"
#include "lin_com.h"
#include "lin_nm.h"

/*******************************************************************************
 * the defines
 ******************************************************************************/

/*******************************************************************************
 * the typedefs
 ******************************************************************************/

/*******************************************************************************
 * the globals
 ******************************************************************************/

Lin_FrameDataType *pLinCom_CurrentFrameData = 0;

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

/*! \brief Get the link data buffer pointer according to pid index
 *
 * This function execute to initialize the communication module
 *
 * \param[in] pidIdx: frame index
 * \param[in] dataBufferPtr: the data buffer which be stored in upper layer
 * \return: void
 */
Lin_FrameDataType *LinCom_GetFramePointer(uint8_t pidIdx)
{
    return &pLinCom_CurrentFrameData[pidIdx];
}

/*! \brief Initialize the communication module
 *
 * This function execute to initialize the communication module
 *
 * \param[in] dataBufferPtr: the data buffer which be stored in upper layer  
 * \param[in] frameNum: the total frame num in the upper layer be used
 * \return: void
 */
void LinCom_Init(Lin_FrameDataType *dataBufferPtr, uint8_t frameNum)
{
    pLinCom_CurrentFrameData = dataBufferPtr;
}

/*! \brief Update the tx signal
 *
 * This function execute to update the tx signal from local to lower lay frame buffer
 *
 * \param[in] pidIdx: pid index
 * \return: void
 */
void linCom_BusTxSignalUpdate(uint8_t pidIdx)
{
    Lin_FrameAccessStateType frameAccessState;

    frameAccessState = LinSchedule_GetFrameAccessState(pidIdx);
    if((frameAccessState == LIN_FRAME_ACCESS_STATE_IDLE) || (frameAccessState == LIN_FRAME_ACCESS_STATE_UPDATED))
    {
        Lin_FrameDataType *pFrameDataPtr = 0;

        pFrameDataPtr = LinSchedule_GetFrameBufferPtr(pidIdx);
        if(pFrameDataPtr != 0u)
        {
            
            if(memcmp(&pLinCom_CurrentFrameData[pidIdx], pFrameDataPtr, sizeof(Lin_FrameDataType)) != 0u)
            {
                LinSchedule_SetFrameAccessState(pidIdx, LIN_FRAME_ACCESS_STATE_WRITING_IN);
                memcpy(pFrameDataPtr, &pLinCom_CurrentFrameData[pidIdx], sizeof(Lin_FrameDataType));

                /* Indicate the data has changed values */
                LinSchedule_SetFrameAccessState(pidIdx, LIN_FRAME_ACCESS_STATE_UPDATED);
            }
        }
    }
}

/*! \brief Update the rx signal
 *
 * This function execute to update the received data from lower layer frame buffer to local 
 *
 * \param[in] channelIdx: which physical channel is used 
 * \param[in] pidIdx: pid index
 * \return: void
 */
void linCom_BusRxSignalUpdate(uint8_t channelIdx, uint8_t pidIdx)
{
    Lin_FrameAccessStateType frameAccessState;

    /* These frames are received by slave node */
    frameAccessState = LinSchedule_GetFrameAccessState(pidIdx);
    if(frameAccessState == LIN_FRAME_ACCESS_STATE_UPDATED || frameAccessState == LIN_FRAME_ACCESS_STATE_OVER_RECEIVED)
    {
        Lin_FrameDataType *pFrameDataPtr = NULL;
        Lin_FrametypeType  frameType     = LIN_FRAME_TYPE_RESERVED;
        /* Process the received data from bus */
        frameType = LinSchedule_GetFrameType(channelIdx, pidIdx);

        pFrameDataPtr = LinSchedule_GetFrameBufferPtr(pidIdx);
        if(pFrameDataPtr != NULL)
        {
            /* Update the event trigger data into the associated unconditional frame */
            if(frameType == LIN_FRAME_TYPE_EVENT_TRIGGER)
            {
                uint8_t linkPidIdx = 0;

                linkPidIdx = LinSchedule_GetPidIdx(pFrameDataPtr->poyload.eventBytes.pid);

                if((pFrameDataPtr != NULL) && (linkPidIdx != LIN_DATA_INVALID))
                {
                    memcpy(&pLinCom_CurrentFrameData[pidIdx], pFrameDataPtr, sizeof(Lin_FrameDataType));
                    LinSchedule_SetFrameAccessState(pidIdx, LIN_FRAME_ACCESS_STATE_IDLE);
                }
            }
            else
            {
                memcpy(&pLinCom_CurrentFrameData[pidIdx], pFrameDataPtr, sizeof(Lin_FrameDataType));
                LinSchedule_SetFrameAccessState(pidIdx, LIN_FRAME_ACCESS_STATE_IDLE);

                if(LinSchedule_GetPid(pidIdx) == LIN_STACK_PID_MRF)
                {
#if LINCFG_NODE_USED == LINCFG_NODE_SLAVE
                    if(pLinCom_CurrentFrameData[pidIdx].poyload.diagBytes.nad == LINCFG_NAD_FOR_SLEEP)
                    {
                        /* Get the sleep command from the bus */
                        LinNm_RequestSleep(true);
                    }
                    else
                    {
                        /* TODO: Call out TP */
                    }
#else
                    /* TODO: Call out TP */
#endif
                }
                else if(LinSchedule_GetPid(pidIdx) == LIN_STACK_PID_SRF)
                {
                    /* TODO: Call out TP */
                }
            }
        }
    }
}


/*! \brief Process the local signal of tx or rx
 *
 * This function execute to update local tx to lower frame buffer or update rx from lower to rx local
 *
 * \param[in] channelIdx: which physical channel is used
 * \return: void
 */
void LinCom_MainFunction(uint8_t channelIdx)
{
    Lin_FrameCtrlType frameCtrl;
    uint8_t           i = 0, tempFrameNum = 0;

    tempFrameNum = LinSchedule_GetFrameNum(channelIdx);
    for(i = 0; i < tempFrameNum; i++)
    {
        frameCtrl = LinSchedule_GetFrameCtrl(channelIdx, i);
        if(frameCtrl == LIN_FRAME_CTRL_PUBLISH)
        {
            /* These frames are transmit by slave node */
            linCom_BusTxSignalUpdate(i);
        }
        else if(frameCtrl == LIN_FRAME_CTRL_SUBSCRIBER)
        {
            linCom_BusRxSignalUpdate(channelIdx, i);
        }
    }
}
