/*
 * 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 "dbc_comm.h"

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

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

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

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

void DbcComm_Init(DbcCommType *obj, const DbcComm_ParamsType *pParams)
{
    uint8_t node = 0;
    for(node = 0; node < DBCCOMM_NODE_NUM; ++node)
    {
        obj->enableNodeTx[node] = false;
        obj->enableNodeRx[node] = false;
    }

    obj->sendCanMsg = pParams->sendCanMsg;

    uint16_t i = 0;
    /* Process send message */
    for(i = 0; i < obj->txMsgNum; ++i)
    {
        obj->txMsgArray[i].periodCntMax = (uint32_t)(pParams->runFreq_kHz * obj->txMsgArray[i].pInfo->period_ms);
        obj->txMsgArray[i].periodCnt    = i; /* Stagger the start timing */
    }

    /* Process received message */
    for(i = 0; i < obj->rxMsgNum; ++i)
    {
        obj->rxMsgArray[i].timeoutCntMax = (uint32_t)(pParams->runFreq_kHz * obj->rxMsgArray[i].pInfo->timeout_ms);
        obj->rxMsgArray[i].timeoutCnt    = 0;
        obj->rxMsgArray[i].isTimeout     = false;
    }
}

void DbcComm_Run(DbcCommType *obj)
{
    uint16_t i = 0;
    /* Process send message */
    for(i = 0; i < obj->txMsgNum; ++i)
    {
        DbcComm_CanNodeType node = obj->txMsgArray[i].pInfo->node;
        if(obj->enableNodeTx[node])
        {
            if(obj->txMsgArray[i].periodCntMax > 0)
            {
                obj->txMsgArray[i].periodCnt++;
                if(obj->txMsgArray[i].periodCnt >= obj->txMsgArray[i].periodCntMax)
                {
                    obj->txMsgArray[i].periodCnt = 0;
                    /* Pack message and send it */
                    DbcComm_CanMsgType msg = CAN_MSG_DEFAULT;
                    obj->txMsgArray[i].pInfo->packMsg(obj, &msg);
                    obj->sendCanMsg(node, &msg);
                }
            }
        }
    }

    /* Process received message */
    for(i = 0; i < obj->rxMsgNum; ++i)
    {
        DbcComm_CanNodeType node = obj->rxMsgArray[i].pInfo->node;
        if(obj->enableNodeRx[node])
        {
            if(obj->rxMsgArray[i].timeoutCntMax > 0 && false == obj->rxMsgArray[i].isTimeout)
            {
                obj->rxMsgArray[i].timeoutCnt++;
                if(obj->rxMsgArray[i].timeoutCnt >= obj->rxMsgArray[i].timeoutCntMax)
                {
                    obj->rxMsgArray[i].timeoutCnt = 0;
                    obj->rxMsgArray[i].isTimeout  = true;
                }
            }
        }
    }
}

void DbcComm_ReceiveCanMsg(DbcCommType *obj, DbcComm_CanNodeType node, const DbcComm_CanMsgType *pMsg)
{
    uint16_t i = 0;
    for(i = 0; i < obj->rxMsgNum; ++i)
    {
        if(pMsg->id == obj->rxMsgArray[i].pInfo->id && node == obj->rxMsgArray[i].pInfo->node)
        {
            obj->rxMsgArray[i].timeoutCnt = 0;
            obj->rxMsgArray[i].isTimeout  = false;
            obj->rxMsgArray[i].pInfo->unpackMsg(obj, pMsg);
            break;
        }
    }
}

void DbcComm_EnableRx(DbcCommType *obj, DbcComm_CanNodeType node, bool enable)
{
    if(obj->enableNodeRx[node] != enable)
    {
        /* Change the enable status */
        uint16_t i = 0;
        for(i = 0; i < obj->rxMsgNum; ++i)
        {
            if(obj->rxMsgArray[i].pInfo->node == node)
            {
                if(obj->rxMsgArray[i].timeoutCntMax > 0)
                {
                    obj->rxMsgArray[i].timeoutCnt = 0;
                    obj->rxMsgArray[i].isTimeout  = false;
                }
            }
        }
        obj->enableNodeRx[node] = enable;
    }
}

void DbcComm_EnableTx(DbcCommType *obj, DbcComm_CanNodeType node, bool enable)
{
    if(obj->enableNodeTx[node] != enable)
    {
        /* Change the enable status */
        uint16_t i = 0;
        for(i = 0; i < obj->txMsgNum; ++i)
        {
            if(obj->txMsgArray[i].pInfo->node == node)
            {
                if(obj->txMsgArray[i].periodCntMax > 0)
                {
                    obj->txMsgArray[i].periodCnt = obj->txMsgArray[i].periodCntMax;
                }
            }
        }
        obj->enableNodeTx[node] = enable;
    }
}
