/*
 * 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_type.h"
#include "lin_if.h"
#include "lin_schedule.h"
#include "lin_cfg.h"
#include "lin_com.h"
#include "lin_nm.h"

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

/*******************************************************************************
 * the typedefs
 ******************************************************************************/
static void LinNm_InitStateProc(void);
static void LinNm_RunningStateProc(void);
static void LinNm_PrepareSleepStateProc(void);
static void LinNm_ReadySleepStateProc(void);
static void LinNm_SleepStateProc(void);


/*******************************************************************************
 * the globals
 ******************************************************************************/
LinNm_StateType LinNm_State;

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

/*! \brief Initialize the communication manager
 *
 * This function execute to initialize the communication manager 
 *
 * \param[in] cpuClockFrequency: cpu lock frequency
 * \return: void
 */
void LinNm_Init(uint32_t cpuClockFrequency)
{
    memset(&LinNm_State, 0, sizeof(LinNm_State));
}

/*! \brief Get the state of the bus 
 *
 * This function execute to get the bus state
 *
 * \param[in] void: 
 * \return: bool
 */
static bool LinNm_GetBusIsInIdle(void)
{
    bool             ret          = false;
    Lin_OpStatusType tempOpStatus = LIN_STATUS_IDLE;

    /* All node is idle or not in busy state */
#if LINCFG_CH0_USED == LINCFG_CH_ACTIVE
    tempOpStatus = LinIf_GetTransferOperateStatus(0);
#endif

#if LINCFG_CH1_USED == LINCFG_CH_ACTIVE
    tempOpStatus = LinIf_GetTransferOperateStatus(1);
#endif

#if LINCFG_CH2_USED == LINCFG_CH_ACTIVE
    tempOpStatus = LinIf_GetTransferOperateStatus(2);
#endif

#if LINCFG_CH3_USED == LINCFG_CH_ACTIVE
    tempOpStatus = LinIf_GetTransferOperateStatus(2);
#endif

    if((tempOpStatus == LIN_STATUS_IDLE) || (tempOpStatus > LIN_STATUS_TX_DATA_BUSY))
    {
        ret = true;
    }

    return ret;
}

/*! \brief Set the communication mode
 *
 * This function execute to set the communication mode
 *
 * \param[in] targeMode: target mode 
 * \return: void
 */
void LinNm_SetComMode(LinNm_ModeType targeMode)
{
    bool busIsInIdle = false;

    LinNm_State.targetComMode = targeMode;

    busIsInIdle = LinNm_GetBusIsInIdle();

    /* Only all node are in idle or not in busy, then can change the current communication mode */
    if(busIsInIdle == true)
    {
        LinNm_State.currentComMode = LinNm_State.targetComMode;
    }
}

/*! \brief Get the communication mode
 *
 * This function execute to get the communication mode
 *
 * \param[in] void: 
 * \return: the communication mode
 */
LinNm_ModeType LinNm_GetComMode(void)
{
    return LinNm_State.currentComMode;
}

/*! \brief Set sleep request
 *
 * This function execute to request to enter into sleep mode
 *
 * \param[in] void: 
 * \return: void
 */
static void LinNm_SetSleepRequest(void)
{
    LinNm_State.requestSleep = true;
}

/*! \brief Get the request of sleep 
 *
 * This function execute to get the request of sleep
 *
 * \param[in] void: 
 * \return: true: requested the sleep
 *          false: no request the sleep
 */
static bool LinNm_GetSleepRequest(void)
{
    return LinNm_State.requestSleep;
}

/*! \brief Clear the request of sleep
 *
 * This function execute to clear the request of sleep
 *
 * \param[in] void: 
 * \return: void
 */
static void LinNm_ClearSleepRequest(void)
{
    LinNm_State.requestSleep = false;
}

/*! \brief Set the sleep indication
 *
 * This function execute to set the sleep indication after enter into sleep 
 *
 * \param[in] void: 
 * \return: void
 */
static void LinNm_SetSleepIndication(void)
{
    LinNm_State.sleepIndication = true;
}

/*! \brief Get the sleep indication
 *
 * This function execute to get the sleep indication
 *
 * \param[in] void: 
 * \return: true: in sleep mode
 *          false: not in sleep mode
 */
static bool LinNm_GetSleepIndication(void)
{
    return LinNm_State.sleepIndication;
}

/*! \brief Clear the sleep indication
 *
 * This function execute to clear the sleep indication after exist from sleep mode
 *
 * \param[in] void: 
 * \return: void
 */
static void LinNm_ClearSleepIndication(void)
{
    LinNm_State.sleepIndication = false;
}

/*! \brief Request sleep 
 *
 * This function execute to request sleep  
 *
 * \param[in] sleepRequest: request sleep 
 * \return: void
 */
void LinNm_RequestSleep(bool sleepRequest)
{
    if(sleepRequest == true)
    {
        LinNm_SetSleepRequest();
    }
    else
    {
        LinNm_ClearSleepRequest();
    }
}

/*! \brief Set the active wake up request
 *
 * This function execute to set active wake-up request if the local condition is match to exist from sleep
 *
 * \param[in] void: 
 * \return: void
 */
void LinNm_SetActiveRequestWakeUp(void)
{
    LinNm_State.activeWakeUpReq = true;
}

/*! \brief Get the active wakeup request
 *
 * This function execute to get the active wakeup request
 *
 * \param[in] void: 
 * \return: true: active wakeup request is enabled
 *          false: active wakeup request is disabled
 */
static bool LinNm_GetActiveRequestWakeUp(void)
{
    return LinNm_State.activeWakeUpReq;
}

/*! \brief Clear the active request of wakeup
 *
 * This function execute to clear the active request of wakeup
 *
 * \param[in] void: 
 * \return: void
 */
static void LinNm_ClearActiveRequestWakeUp(void)
{
    LinNm_State.activeWakeUpReq = false;
}

/*! \brief Check the passive wakeup signal
 *
 * This function execute to get the passive wake-up signal from bus
 *
 * \param[in] uint8_t channelIdx: which physical channel is used 
 * \return: true: passive wakeup signal is valid
 *          false: passive wakup signal is invalid
 */
bool LinNm_CheckPassiveWakeUpSignal(uint8_t channelIdx)
{
    bool ret = false;

    if((LinNm_GetSleepIndication() == true) && (LinNm_GetActiveRequestWakeUp() == false))
    {
        if(LinIf_GetWakeupSignal(channelIdx) == true)
        {
            if(LinNm_State.wakeUpSignalValidTickCnt > LINCFG_WAKEUP_SIGNAL_CHECK_VALID_MIN_CNT)
            {
                LinNm_State.passiveWakeUpConfirm = true;
            }
            else
            {
                LinNm_State.wakeUpSignalValidTickCnt++;
                LinNm_State.passiveWakeUpConfirm = false;
            }
        }
        else
        {
            if(LinNm_State.passiveWakeUpConfirm == true)
            {
                LinNm_State.passiveWakeUpConfirm = false;
                ret                                = true;
            }

            LinNm_State.wakeUpSignalValidTickCnt = 0;
        }
    }

    return ret;
}

/*! \brief Set the error response bit
 *
 * This function execute to update the response error bit
 *
 * \param[in] errBit: err
 * \return: void
 */
void LinNm_SetErrResponseBit(bool errBit)
{
    LinNm_State.responseError = errBit;
}

/*! \brief Get the error response bit
 *
 * This function execute to get the error response bit
 *
 * \param[in] void: 
 * \return: true: error is happen
 *          false: error is not happen
 */
bool LinNm_GetErrResponseBit(void)
{
    return LinNm_State.responseError;
}

/*! \brief Set the transfer successful bit
 *
 * This function execute 
 *
 * \param[in] successfullBit: true or false
 * \return: void
 */
void LinNm_SetSuccessfulTransferBit(bool successfulBit)
{
    LinNm_State.transferSuccessful = successfulBit;
}

/*! \brief Get the transfer successful bit
 *
 * This function execute to get the transfer successful bit
 *
 * \param[in] void: 
 * \return: true: transfer successfully
 *          false: transfer failed
 */
bool LinNm_GetSuccessfulTransferBit(void)
{
    return LinNm_State.transferSuccessful;
}

/*! \brief Process the idle state of communication manager
 *
 * This function execute to process the idle state of communication manager
 *
 * \param[in] void: 
 * \return: void
 */
static void LinNm_IdleStateProc(void)
{
#if LINCFG_NODE_USED == LINCFG_NODE_MASTER
    if(LinNm_GetActiveRequestWakeUp() == false)
    {
        uint8_t defaultScheduleIdx = LINSCH_SCHEDULE_TABLE_NULL;

        defaultScheduleIdx = LinSchedule_GetDefaultScheduleTblIdx(LINCFG_MASTER_CH_IDX);
        LinIf_Init(LINCFG_MASTER_CH_IDX, LIN_NODE_IS_MASTER);
        LinSchedule_Init(LINCFG_MASTER_CH_IDX);
        LinSchedule_ReqSwitchScheduleTbl(LINCFG_MASTER_CH_IDX, defaultScheduleIdx);
        LinIf_ClearSleepRequestIndication(LINCFG_MASTER_CH_IDX);

#if LINCFG_CH0_USED == LINCFG_CH_ACTIVE && LINCFG_MASTER_CH_IDX != LINCFG_CH_IDX_0
        LinIf_Init(0, LIN_NODE_IS_SLAVE);
#endif

#if LINCFG_CH1_USED == LINCFG_CH_ACTIVE && LINCFG_MASTER_CH_IDX != LINCFG_CH_IDX_1
        LinIf_Init(1, LIN_NODE_IS_SLAVE);
#endif

#if LINCFG_CH2_USED == LINCFG_CH_ACTIVE && LINCFG_MASTER_CH_IDX != LINCFG_CH_IDX_2
        LinIf_Init(2, LIN_NODE_IS_SLAVE);
#endif

#if LINCFG_CH3_USED == LINCFG_CH_ACTIVE && LINCFG_MASTER_CH_IDX != LINCFG_CH_IDX_3
        LinIf_Init(3, LIN_NODE_IS_SLAVE);
#endif
        LinNm_ClearSleepRequest();
        LinNm_SetComMode(LINNM_MODE_INIT);
    }
    else
    {
        LinNm_State.wakeUpSignalValidTickCnt++;
        if(LinNm_State.wakeUpSignalValidTickCnt > LINCFG_WAKEUP_SIGNAL_DURATION_MIN_CNT)
        {
            uint8_t defaultScheduleIdx = LINSCH_SCHEDULE_TABLE_NULL;

            defaultScheduleIdx = LinSchedule_GetDefaultScheduleTblIdx(LINCFG_MASTER_CH_IDX);

            LinNm_State.wakeUpSignalValidTickCnt = 0;
            LinIf_Init(LINCFG_MASTER_CH_IDX, LIN_NODE_IS_MASTER);
            LinSchedule_Init(LINCFG_MASTER_CH_IDX);
            LinSchedule_ReqSwitchScheduleTbl(LINCFG_MASTER_CH_IDX, defaultScheduleIdx);

            LinNm_ClearSleepRequest();
            LinNm_SetComMode(LINNM_MODE_INIT);
        }
    }
#else
    if(LinNm_GetActiveRequestWakeUp() == false)
    {
#if LINCFG_CH0_USED == LINCFG_CH_ACTIVE
        LinIf_Init(0, LIN_NODE_IS_SLAVE);
#endif

#if LINCFG_CH1_USED == LINCFG_CH_ACTIVE
        LinIf_Init(1, LIN_NODE_IS_SLAVE);
#endif

#if LINCFG_CH2_USED == LINCFG_CH_ACTIVE
        LinIf_Init(2, LIN_NODE_IS_SLAVE);
#endif

#if LINCFG_CH3_USED == LINCFG_CH_ACTIVE
        LinIf_Init(3, LIN_NODE_IS_SLAVE);
#endif
        LinNm_ClearSleepRequest();
        LinNm_SetComMode(LINNM_MODE_INIT);
    }
    else
    {
        LinNm_State.wakeUpSignalValidTickCnt++;
        if(LinNm_State.wakeUpSignalValidTickCnt > LINCFG_WAKEUP_SIGNAL_DURATION_MIN_CNT)
        {
            LinNm_State.wakeUpSignalValidTickCnt = 0;
#if LINCFG_CH0_USED == LINCFG_CH_ACTIVE
            LinIf_Init(0, LIN_NODE_IS_SLAVE);
#endif

#if LINCFG_CH1_USED == LINCFG_CH_ACTIVE
            LinIf_Init(1, LIN_NODE_IS_SLAVE);
#endif

#if LINCFG_CH2_USED == LINCFG_CH_ACTIVE
            LinIf_Init(2, LIN_NODE_IS_SLAVE);
#endif

#if LINCFG_CH3_USED == LINCFG_CH_ACTIVE
            LinIf_Init(3, LIN_NODE_IS_SLAVE);
#endif
            LinNm_ClearSleepRequest();
            LinNm_SetComMode(LINNM_MODE_INIT);
        }
    }
#endif
}

/*! \brief Process the initialize state of communication manager
 *
 * This function execute to process the initialize state of communication manager
 *
 * \param[in] void: 
 * \return: void
 */
static void LinNm_InitStateProc(void)
{
#if LINCFG_NODE_USED == LINCFG_NODE_MASTER
    if(LinNm_GetActiveRequestWakeUp() == false)
    {
        LinNm_State.busIdleTickCnt = 0;
        LinNm_SetComMode(LINNM_MODE_RUNNING);
    }
    else
    {
        LinNm_State.delayCommunicateTickCnt++;
        if(LinNm_State.delayCommunicateTickCnt > LINCFG_START_COM_AFTER_WKP_CNT)
        {
            LinNm_State.delayCommunicateTickCnt = 0;
            LinNm_ClearActiveRequestWakeUp();
            LinNm_State.wakeUpSignalValidTickCnt = 0;

            LinNm_State.busIdleTickCnt = 0;
            LinNm_SetComMode(LINNM_MODE_RUNNING);
        }
    }
#else
    if(LinNm_GetActiveRequestWakeUp() == false)
    {
        LinNm_State.busIdleTickCnt       = 0;
        LinNm_State.wakeUpTimeOutTickCnt = 0;
        LinNm_State.retryWakeUpCnt       = 0;
        LinNm_SetComMode(LINNM_MODE_RUNNING);
    }
    else
    {
        LinNm_State.wakeUpTimeOutTickCnt++;
        if(LinNm_State.retryWakeUpCnt >= LINCFG_WAKEUP_RETRY_MAX_CNT)
        {
            if(LinNm_State.wakeUpTimeOutTickCnt > LINCFG_SLOW_WAKEUP_PEIROD)
            {
                LinNm_State.wakeUpTimeOutTickCnt = 0;

                LinIf_SendWakeUpSignal(LINCFG_SLAVE_CH_AS_WAKEUP_IDX);
                LinNm_State.wakeUpSignalValidTickCnt = 0;

                LinNm_SetComMode(LINNM_MODE_IDLE);
            }
            else
            {
                bool busIsInIdle = false;

                busIsInIdle = LinNm_GetBusIsInIdle();
                if(busIsInIdle == false)
                {
                    LinNm_ClearActiveRequestWakeUp();
                    LinNm_State.busIdleTickCnt       = 0;
                    LinNm_State.wakeUpTimeOutTickCnt = 0;
                    LinNm_State.retryWakeUpCnt       = 0;
                    LinNm_SetComMode(LINNM_MODE_RUNNING);
                }
            }
        }
        else
        {
            if(LinNm_State.wakeUpTimeOutTickCnt > LINCFG_FAST_WAKEUP_PERIOD)
            {
                LinNm_State.wakeUpTimeOutTickCnt = 0;
                LinNm_State.retryWakeUpCnt++;

                LinIf_SendWakeUpSignal(LINCFG_SLAVE_CH_AS_WAKEUP_IDX);
                LinNm_State.wakeUpSignalValidTickCnt = 0;

                LinNm_SetComMode(LINNM_MODE_IDLE);
            }
            else
            {
                bool busIsInIdle = false;

                busIsInIdle = LinNm_GetBusIsInIdle();
                if(busIsInIdle == false)
                {
                    LinNm_ClearActiveRequestWakeUp();
                    LinNm_State.busIdleTickCnt       = 0;
                    LinNm_State.wakeUpTimeOutTickCnt = 0;
                    LinNm_State.retryWakeUpCnt       = 0;
                    LinNm_SetComMode(LINNM_MODE_RUNNING);
                }
            }
        }
    }
#endif
}

/*! \brief Process the running state of communication manager
 *
 * This function execute to process the running state of communication manager
 *
 * \param[in] void: 
 * \return: void
 */
static void LinNm_RunningStateProc(void)
{
#if LINCFG_NODE_USED == LINCFG_NODE_MASTER
    if(LinNm_GetSleepRequest() == false)
    {
        bool busIsInIdle = false;

        busIsInIdle = LinNm_GetBusIsInIdle();
        if(busIsInIdle == true)
        {
            /* If all node are in idle or not in busy state, and the time is out, then can enter into sleep */
            if(LinNm_State.busIdleTickCnt >= LINCFG_BUS_IDLE_TICK_CNT)
            {
                uint8_t            pidIdx        = LinSchedule_GetPidIdx(LIN_STACK_PID_MRF);
                Lin_FrameDataType *pFrameInfoPtr = LinCom_GetFramePointer(pidIdx);
                uint8_t            tableIdx              = 0;

                pFrameInfoPtr->poyload.diagBytes.nad     = LINCFG_NODE_ADDRESS;
                pFrameInfoPtr->poyload.diagBytes.data[0] = 0xFF;
                pFrameInfoPtr->poyload.diagBytes.data[1] = 0xFF;
                pFrameInfoPtr->poyload.diagBytes.data[2] = 0xFF;
                pFrameInfoPtr->poyload.diagBytes.data[3] = 0xFF;
                pFrameInfoPtr->poyload.diagBytes.data[4] = 0xFF;
                pFrameInfoPtr->poyload.diagBytes.data[5] = 0xFF;
                pFrameInfoPtr->poyload.diagBytes.data[6] = 0xFF;

                linCom_BusTxSignalUpdate(pidIdx);

                tableIdx = LinSchedule_GetScheduleTblIdxByTableType(LINSCH_TBL_TYPE_GO_TO_SLEEP);
                if(LinSchedule_ReqSwitchScheduleTbl(LINCFG_MASTER_CH_IDX, tableIdx) == true)
                {
                    LinNm_State.busIdleTickCnt = 0;
                    LinNm_SetSleepRequest();
                    LinNm_SetComMode(LINNM_MODE_SLEEP_PREPARE);
                }
            }
            else
            {
                LinNm_State.busIdleTickCnt++;
            }
        }
        else
        {
            LinNm_State.busIdleTickCnt = 0;
        }
    }
    else
    {
        uint8_t            pidIdx        = LinSchedule_GetPidIdx(LIN_STACK_PID_MRF);
        Lin_FrameDataType *pFrameInfoPtr = LinCom_GetFramePointer(pidIdx);
        uint8_t            tableIdx      = 0;

        pFrameInfoPtr->poyload.diagBytes.nad     = LINCFG_NODE_ADDRESS;
        pFrameInfoPtr->poyload.diagBytes.data[0] = 0xFF;
        pFrameInfoPtr->poyload.diagBytes.data[1] = 0xFF;
        pFrameInfoPtr->poyload.diagBytes.data[2] = 0xFF;
        pFrameInfoPtr->poyload.diagBytes.data[3] = 0xFF;
        pFrameInfoPtr->poyload.diagBytes.data[4] = 0xFF;
        pFrameInfoPtr->poyload.diagBytes.data[5] = 0xFF;
        pFrameInfoPtr->poyload.diagBytes.data[6] = 0xFF;
        linCom_BusTxSignalUpdate(pidIdx);

        tableIdx = LinSchedule_GetScheduleTblIdxByTableType(LINSCH_TBL_TYPE_GO_TO_SLEEP);
        if(LinSchedule_ReqSwitchScheduleTbl(LINCFG_MASTER_CH_IDX, tableIdx) == true)
        {
            LinNm_SetSleepRequest();
            LinNm_SetComMode(LINNM_MODE_SLEEP_PREPARE);
        }
    }
#else
    if(LinNm_GetSleepRequest() == false)
    {
        bool busIsInIdle = false;

        busIsInIdle = LinNm_GetBusIsInIdle();
        if(busIsInIdle == true)
        {
            LinNm_State.busIdleTickCnt++;
            /* If all node are in idle or not in busy state, and the time is out, then can enter into sleep */
            if(LinNm_State.busIdleTickCnt >= LINCFG_BUS_IDLE_TICK_CNT)
            {
                LinNm_State.busIdleTickCnt = 0;
                LinNm_SetSleepRequest();

                LinNm_SetComMode(LINNM_MODE_SLEEP_READY);
            }
        }
        else
        {
            LinNm_State.busIdleTickCnt = 0;
        }
    }
    else
    {
        LinNm_State.busIdleTickCnt = 0;
        LinNm_SetComMode(LINNM_MODE_SLEEP_READY);
    }
#endif
}

/*! \brief Process the prepare sleep state of communication manager
 *
 * This function execute to process the prepare sleep state of communication manager
 *
 * \param[in] void: 
 * \return: void
 */
static void LinNm_PrepareSleepStateProc(void)
{
#if LINCFG_NODE_USED == LINCFG_NODE_MASTER
    if(LinNm_GetSleepRequest() == true)
    {
        if(LinIf_GetSleepRequestIndication(LINCFG_MASTER_CH_IDX) == true)
        {
            bool busIsInIdle = false;

            busIsInIdle = LinNm_GetBusIsInIdle();
            if(busIsInIdle == true)
            {
                LinIf_ClearSleepRequestIndication(LINCFG_MASTER_CH_IDX);
                LinNm_SetComMode(LINNM_MODE_SLEEP_READY);
            }
        }
    }
#else
    /* Slave node does not have this state */
#endif
}

/*! \brief Process the ready sleep state of communication manager
 *
 * This function execute to process the ready sleep state of communication manager
 *
 * \param[in] void: 
 * \return: void
 */
static void LinNm_ReadySleepStateProc(void)
{
#if LINCFG_NODE_USED == LINCFG_NODE_MASTER
    if(LinNm_GetSleepRequest() == true)
    {
        bool busIsInIdle = false;

        busIsInIdle = LinNm_GetBusIsInIdle();
        if(busIsInIdle == true)
        {
            /* If sleep request is by node self or from bus , then can enter into sleep */
            LinNm_SetComMode(LINNM_MODE_SLEEP);

            /* Before enter sleep mode, re-configurate the lin driver into low-power or idle mode */
#if LINCFG_CH0_USED == LINCFG_CH_ACTIVE
            LinIf_DeInit(0);
#endif

#if LINCFG_CH1_USED == LINCFG_CH_ACTIVE
            LinIf_DeInit(1);
#endif

#if LINCFG_CH2_USED == LINCFG_CH_ACTIVE
            LinIf_DeInit(2);
#endif

#if LINCFG_CH3_USED == LINCFG_CH_ACTIVE
            LinIf_DeInit(3);
#endif
        }
    }
    else
    {
        bool busIsInIdle = false;

        busIsInIdle = LinNm_GetBusIsInIdle();
        if(busIsInIdle == true)
        {
            LinNm_SetComMode(LINNM_MODE_IDLE);
        }
    }
#else
    if(LinNm_GetSleepRequest() == true)
    {
        /* If sleep request is by node self or from bus , then can enter into sleep */
        LinNm_SetComMode(LINNM_MODE_SLEEP);

        /* Before enter sleep mode, re-configurate the lin driver into low-power or idle mode */
#if LINCFG_CH0_USED == LINCFG_CH_ACTIVE
        LinIf_DeInit(0);
#endif

#if LINCFG_CH1_USED == LINCFG_CH_ACTIVE
        LinIf_DeInit(1);
#endif

#if LINCFG_CH2_USED == LINCFG_CH_ACTIVE
        LinIf_DeInit(2);
#endif

#if LINCFG_CH3_USED == LINCFG_CH_ACTIVE
        LinIf_DeInit(3);
#endif
    }
#endif
}

/*! \brief Process the sleep state of communication manager
 *
 * This function execute to process the sleep state of communication manager
 *
 * \param[in] void: 
 * \return: void
 */
static void LinNm_SleepStateProc(void)
{
#if LINCFG_NODE_USED == LINCFG_NODE_MASTER
    if(LinNm_GetSleepRequest() == true)
    {
        LinNm_ClearSleepRequest();
        LinNm_SetSleepIndication();
    }
    else
    {
        if(LinNm_GetSleepIndication() == true)
        {
            if(LinNm_GetActiveRequestWakeUp() == true)
            {
                LinNm_State.wakeUpSignalValidTickCnt = 0;
                LinNm_ClearSleepIndication();
                /* The internal logic to wakeup the bus */
                LinIf_SendWakeUpSignal(LINCFG_MASTER_CH_IDX);

                LinNm_SetComMode(LINNM_MODE_IDLE);
            }
            else
            {
                if(LinNm_CheckPassiveWakeUpSignal(LINCFG_MASTER_CH_IDX) == true)
                {
                    LinNm_ClearSleepIndication();
                    LinNm_SetComMode(LINNM_MODE_IDLE);
                }
            }
        }
    }
#else
    if(LinNm_GetSleepRequest() == true)
    {
        LinNm_ClearSleepRequest();
        LinNm_SetSleepIndication();
    }
    else
    {
        if(LinNm_GetSleepIndication() == true)
        {
            if(LinNm_GetActiveRequestWakeUp() == true)
            {
                LinNm_State.wakeUpSignalValidTickCnt = 0;
                LinNm_ClearSleepIndication();
                LinIf_SendWakeUpSignal(LINCFG_SLAVE_CH_AS_WAKEUP_IDX);

                LinNm_SetComMode(LINNM_MODE_IDLE);
            }
            else
            {
                if(LinNm_CheckPassiveWakeUpSignal(LINCFG_SLAVE_CH_AS_WAKEUP_IDX) == true)
                {
                    LinNm_ClearSleepIndication();
                    LinNm_SetComMode(LINNM_MODE_IDLE);
                }
            }
        }
    }
#endif
}

/*! \brief Process the communication manager
 *
 * This function execute to process the communication manager 
 *
 * \param[in] void: 
 * \return: void
 */
void LinNm_MainFunction(void)
{
    if(LinNm_State.currentComMode != LinNm_State.targetComMode)
    {
        bool busIsInIdle = false;

        busIsInIdle = LinNm_GetBusIsInIdle();
        /* Only all node are in idle or not in busy, then can change the current communication mode */
        if(busIsInIdle == true)
        {
            LinNm_State.currentComMode = LinNm_State.targetComMode;
        }
    }
    else
    {
        switch(LinNm_State.currentComMode)
        {
        case LINNM_MODE_IDLE:
            LinNm_IdleStateProc();
            break;

        case LINNM_MODE_INIT:
            LinNm_InitStateProc();
            break;

        case LINNM_MODE_RUNNING:
            LinNm_RunningStateProc();
            break;

        case LINNM_MODE_SLEEP_PREPARE:
            LinNm_PrepareSleepStateProc();
            break;

        case LINNM_MODE_SLEEP_READY:
            LinNm_ReadySleepStateProc();
            break;

        case LINNM_MODE_SLEEP:
            LinNm_SleepStateProc();
            break;

        default:
            break;
        }
    }
}
