/**
 * @file hv_comm_Timer.c
 * @brief timer source doe.
 * @details This file provides the following functions: \n
 *          (1) Initialization and reset functions \n
 *
 * @author HiView SoC Software Team
 * @version 1.0.0
 * @date 2022-08-23
 * @copyright Copyright(c),2022-8, Hiview Software. All rights reserved.
 * @par History:
 * <table>
 * <tr><th>Author                        <th>Date         <th>Change Description
 * <tr><td>HiView SoC Software Team      <td>2022-08-23   <td>init
 * </table>
*/

#include "hv_comm_Timer.h"
#include "hv_comm_Log.h"
#include "hv_mw_SystemManager.h"
#include "FreeRTOS.h"
#include "timers.h"
#include "hv_comm_Assert.h"
#include "hv_drv_Timer.h"
#include "hv_comm_Event.h"

HwTmr* gpstHWTimer               = NULL;
static HvCommonHwTimer* gpstTimerList   = NULL;
static UCHAR8 gucHWTimerIsStart         = HV_FALSE;
static volatile UINT64 gui64TimerCount  = 0;
static UINT32 gui32HWTimerNumber        = 0;

#ifdef HV_SCALER_DEBUG_VERSION

CHAR8* TimerEventIDConvert(TIMER_EVENT_E enEventType)
{
    CHAR8 * pcId = "default";
    if(( SYS_EVENT_TIMER_POWR_BEGIN <= enEventType) && (SYS_EVENT_TIMER_POWR_END >= enEventType))
    {
        CHAR8* paPowerEventIDType[] =
        {
            "typec parser timeout",
        };
        if((enEventType - SYS_EVENT_TIMER_POWR_BEGIN - 1) < sizeof(paPowerEventIDType)/sizeof(paPowerEventIDType[0]))
        {
            return paPowerEventIDType[enEventType - SYS_EVENT_TIMER_POWR_BEGIN - 1];
        }
        else
        {
            return "NULL";
        }
    }
    if(( SYS_EVENT_TIMER_DP0_BEGIN <= enEventType) && (SYS_EVENT_TIMER_DP0_END >= enEventType))
    {
        CHAR8* paDP0EventIDType[] =
        {
            "dp0 hpd timeout",
            "dp0 timing check",
            "dp0 training NG",
            "dp0 audio check",
        };
        if((enEventType - SYS_EVENT_TIMER_DP0_BEGIN - 1) < sizeof(paDP0EventIDType)/sizeof(paDP0EventIDType[0]))
        {
            return paDP0EventIDType[enEventType - SYS_EVENT_TIMER_DP0_BEGIN - 1];
        }
        else
        {
            return "NULL";
        }
    }
    if(( SYS_EVENT_TIMER_DP1_BEGIN <= enEventType) && (SYS_EVENT_TIMER_DP1_END >= enEventType))
    {
        CHAR8* paDP1EventIDType[] =
        {
            "dp1 hpd timeout",
            "dp1 timing check",
            "dp1 training NG",
            "dp1 audio check",
        };
        if((enEventType - SYS_EVENT_TIMER_DP1_BEGIN) < sizeof(paDP1EventIDType)/sizeof(paDP1EventIDType[0]))
        {
            return paDP1EventIDType[enEventType - SYS_EVENT_TIMER_DP1_BEGIN];
        }
        else
        {
            return "NULL";
        }
    }
    if(( SYS_EVENT_TIMER_HDMI0_BEGIN <= enEventType) && (SYS_EVENT_TIMER_HDMI0_END >= enEventType))
    {
        CHAR8* paHDMI0EventIDType[SYS_EVENT_TIMER_HDMI0_END - SYS_EVENT_TIMER_HDMI0_BEGIN] =
        {
            "hdmi0 wait sync",
            "hdmi0 sync ok",
            "hdmi0 hpd set high",
            "hdmi0 MAC Train Done",
            "hdmi0 Fast Locked",
            "hdmi0 HDCP Sync"
        };
        if((enEventType - SYS_EVENT_TIMER_HDMI0_BEGIN) < sizeof(paHDMI0EventIDType)/sizeof(paHDMI0EventIDType[0]))
        {
            return paHDMI0EventIDType[enEventType - SYS_EVENT_TIMER_HDMI0_BEGIN];
        }
        else
        {
            return "NULL";
        }
    }
    if(( SYS_EVENT_TIMER_HDMI1_BEGIN <= enEventType) && (SYS_EVENT_TIMER_HDMI1_END >= enEventType))
    {
        CHAR8* paHDMI1EventIDType[SYS_EVENT_TIMER_HDMI1_END - SYS_EVENT_TIMER_HDMI1_BEGIN] =
        {
            "hdmi1 wait sync",
            "hdmi1 sync ok",
            "hdmi1 hpd set high",
            "hdmi1 MAC Train Done",
            "hdmi1 Fast Locked",
            "hdmi1 HDCP Sync"
        };
        if((enEventType - SYS_EVENT_TIMER_HDMI1_BEGIN) < sizeof(paHDMI1EventIDType)/sizeof(paHDMI1EventIDType[0]))
        {
            return paHDMI1EventIDType[enEventType - SYS_EVENT_TIMER_HDMI1_BEGIN];
        }
        else
        {
            return "NULL";
        }
    }
    if(( SYS_EVENT_TIMER_CHANNEL_BEGIN <= enEventType) && (SYS_EVENT_TIMER_CHANNEL_TIMER_END >= enEventType))
    {
        CHAR8* paChannelEventIDType[] =
        {
            "main channel search timeout",
            "sub channel search timeout",
            "main channel period search timeout",
            "sub channel period search timeout",
            "main channel av stable",
            "sub channel av stable",
            "main channel no signal timeout",
            "sub channel no signal timeout",
            "main channel mute timeout",
            "sub channel mute timeout",
            "main channel audiomute timeout",
            "sub channel audiomute timeout",
            "burn in period check timeout",
            "audio delay timeout",
            "database sync timeout",
            "av watchdog timer",
            "main audio fs check timer",
            "sub audio fs check timer",
        };
        if((enEventType - SYS_EVENT_TIMER_CHANNEL_BEGIN) < sizeof(paChannelEventIDType)/sizeof(paChannelEventIDType[0]))
        {
            return paChannelEventIDType[enEventType - SYS_EVENT_TIMER_CHANNEL_BEGIN];
        }
        else
        {
            return "NULL";
        }
    }

    if(( SYS_EVENT_TIMER_PANEL_BEGIN <= enEventType) && (SYS_EVENT_TIMER_PANEL_END >= enEventType))
    {
        CHAR8* paPanelEventIDType[] =
        {
            "panel power on",
            "panel data on",
            "panel blk on",
            "panel blk off",
            "panel data off",
            "panel power off",
            "ktx pll lock",
            "edp idle patt end",
            "spi back light",
        };
        if((enEventType - SYS_EVENT_TIMER_PANEL_BEGIN) < sizeof(paPanelEventIDType)/sizeof(paPanelEventIDType[0]))
        {
            return paPanelEventIDType[enEventType - SYS_EVENT_TIMER_PANEL_BEGIN];
        }
        else
        {
            return "NULL";
        }
    }
    if(( OSD_EVENT_TIMER_START <= enEventType) && (OSD_EVENT_TIMER_END >= enEventType))
    {
        CHAR8* paOSDEventIDType[] =
        {
            "osd key repeat",
            "osd key hod",
            "osd auto close",
            "osd auto refresh",
            "osd logo hide",
            "osd remind",
        };
        if((enEventType - OSD_EVENT_TIMER_START) < sizeof(paOSDEventIDType)/sizeof(paOSDEventIDType[0]))
        {
            return paOSDEventIDType[enEventType - OSD_EVENT_TIMER_START];
        }
        else
        {
            return "NULL";
        }
    }
    return pcId;
}

#endif

void Hv_Common_Timer_DispatchEvent(VOID* pvArgument, USHORT16 usTimerId)
{
    UINT64 tick = Hv_Vos_GetTick();
    TIMER_EVENT_E timerEvent = (TIMER_EVENT_E)usTimerId;

    if (timerEvent <= SYS_EVENT_TIMER_START || timerEvent >= EVENT_TIMER_MAX)
    {
        HV_LOG_ERROR(TIMER, "timerEvent = %u. tick = %u. ",timerEvent, tick);
        return;
    }

    //HV_LOG_DEBUG(TIMER, "timerEvent = %u. tick = %u.",timerEvent, tick);
    if (timerEvent < SYS_EVENT_TIMER_END)
    {
        HV_VOS_QUEUE_S *queue = Hv_Mw_System_GetSystemQueue();
        HV_ASSERT_VALID_PTR_VOID(queue);

        SystemEvent stEventMsg;
        stEventMsg.enEventType = SYSTEM_REV_EVENT_TYPE_TIMER;
        stEventMsg.usEventId = timerEvent;
        Hv_Vos_QueueSend(queue, &stEventMsg);
    }
    else if (timerEvent < OSD_EVENT_TIMER_END)
    {
        HV_VOS_QUEUE_S *queue = Hv_Mw_System_GetOsdQueue();
        HV_ASSERT_VALID_PTR_VOID(queue);

        OsdEvent stEventMsg;
        stEventMsg.usEventId = (USHORT16)timerEvent;
        stEventMsg.usValue = (USHORT16)((UINT32)pvArgument);
        Hv_Vos_QueueSend(queue, &stEventMsg);
    }
    else if (timerEvent < EVENT_HUB_TIMER_END)
    {
        HV_VOS_QUEUE_S *queue = Hv_Mw_System_GetEventHubQueue();
        HV_ASSERT_VALID_PTR_VOID(queue);

        Event stEventMsg;
        stEventMsg.usEventId = timerEvent;
        Hv_Vos_QueueSend(queue, &stEventMsg);
    }
    #if (HV_PROJECT_CONFIG_SMART_POWER == HV_CONFIG_ON)
        else if (timerEvent == EVENT_POWER_LOAD_CHECK_TIMER)
        {
            HV_VOS_QUEUE_S *queue = Hv_Mw_System_GetSystemQueue();
            HV_ASSERT_VALID_PTR_VOID(queue);
    
            SystemEvent stEventMsg;
            stEventMsg.enEventType = SYSTEM_REV_EVENT_TYPE_TIMER;
            stEventMsg.usEventId = timerEvent;
            Hv_Vos_QueueSend(queue, &stEventMsg);
        }
    #endif
    else
    {
        HV_LOG_ERROR(TIMER, "Invalid timer event, timerEvent = %u. tick = %u.", timerEvent, tick);
    }

    return;
}

void Hv_DispatchHWTimerEvent(HvCommonHwTimer* pstTimer)
{
    HV_ASSERT_VALID_PTR_VOID(pstTimer);
    //TickType_t tick = xTaskGetTickCount();

    TIMER_EVENT_E timerEvent = (TIMER_EVENT_E)(pstTimer->timerEvent);
    if (timerEvent <= SYS_EVENT_TIMER_START || timerEvent >= EVENT_HUB_TIMER_END)
    {
        return;
    }

    if (timerEvent < SYS_EVENT_TIMER_END)
    {
        HV_VOS_QUEUE_S *queue = Hv_Mw_System_GetSystemQueue();
        HV_ASSERT_VALID_PTR_VOID(queue);

        SystemEvent stEventMsg;
        stEventMsg.enEventType = SYSTEM_REV_EVENT_TYPE_TIMER;
        stEventMsg.usEventId = timerEvent;

        Hv_Vos_QueueSendFromISR(queue, &stEventMsg);
    }
    else if (timerEvent < OSD_EVENT_TIMER_END)
    {
        HV_VOS_QUEUE_S *queue = Hv_Mw_System_GetOsdQueue();
        HV_ASSERT_VALID_PTR_VOID(queue);

        OsdEvent stEventMsg;
        stEventMsg.usEventId = (USHORT16)timerEvent;
        stEventMsg.usValue = pstTimer->param;

        Hv_Vos_QueueSendFromISR(queue, &stEventMsg);
    }
    else if (timerEvent < EVENT_HUB_TIMER_END)
    {
        HV_VOS_QUEUE_S *queue = Hv_Mw_System_GetEventHubQueue();
        HV_ASSERT_VALID_PTR_VOID(queue);

        Event stEventMsg;
        stEventMsg.usEventId = timerEvent;

        Hv_Vos_QueueSendFromISR(queue, &stEventMsg);
    }
    else
    {
    }
}

UINT64 Hv_Common_HwTimer_GetHardWareTimerCount()
{
    return gui64TimerCount;
}

void Hv_Common_HwTimer_StopHardWareTimer(HwTmr* pstHWTimer)
{
    Hv_Drv_Tmr_Stop(pstHWTimer);
    return;
}

void Hv_Common_HwTimer_StartHardWareTimer(HwTmr* pstHWTimer)
{
    HV_LOG_DEBUG(TIMER, "Start hardware timer.");
    Hv_Drv_Tmr_Start(pstHWTimer);
}

void Hv_Common_HwTimer_CleanupHardWareTimer(HwTmr* pstHWTimer)
{
    HV_LOG_DEBUG(TIMER, "Cleanup hardware timer.");
    Hv_Drv_Tmr_ClearInterrupt(pstHWTimer);
    Hv_Drv_Tmr_Cleanup(pstHWTimer);
    return;
}

void Hv_Common_HwTimer_Handler(UINT32 arg)
{
    UNUSED(arg);

    /* Increase the counter */
    gui64TimerCount++;

    /* Judge and process the timerout */
    HvCommonHwTimer* entry = gpstTimerList;
    for (; entry; entry = entry->next)
    {
        /* Sorted list, just process with the front part. */
        if (Hv_Common_HwTimer_GetHardWareTimerCount() < entry->periodMs)
        {
            return;
        }
        /* Remove expired timer from list */
        gpstTimerList = entry->next;

        /* Call callback */
        if (entry->callbackFn)
        {
            entry->callbackFn(entry);
        }
    }

    /* Stop hardware timer when timer list is NULL */
    if ((gpstTimerList == NULL) && (HV_TRUE == gucHWTimerIsStart))
    {
        Hv_Common_HwTimer_StopHardWareTimer(gpstHWTimer);     /* Stop harware timer */
        gui64TimerCount = 0;                            /* Clear the timer counter */
        gucHWTimerIsStart = HV_FALSE;
    }
    return;
}

static HwTmr* Hv_Common_HwTimer_HardWareTimerInit(VOID)
{
    HV_LOG_DEBUG(TIMER, "Init hardware timer.");
    HwTmrConfig stTmrInit = {0};
    HwTmr *self = NULL;
    HV_MEMSET(&stTmrInit, 0, sizeof(HwTmrConfig));
    /* initialize as interval timer */
    stTmrInit.enChannel = TMR_CH_0;
    stTmrInit.enClockDividedEnable = TMRC_CCDE_DIV_ENABLE;
    stTmrInit.enDivMode = TMR_DIV_CCD_2;
    stTmrInit.enClrEnable = TMRC_CLR_ENABLE;
    stTmrInit.enIntEnable = TMRIVL_TIIE_INT_ENABLE;
    stTmrInit.enZeroClrEnable = TMRIVL_TZCE_ZERO_CLR_ENABLE;
    stTmrInit.pfTmrCb = Hv_Common_HwTimer_Handler;
    self = Hv_Drv_Tmr_Init(&stTmrInit);
    HV_ASSERT_VALID_PTR_RET(self, NULL);
    Hv_Drv_Tmr_SetMsCounter(self, 1); // 1ms loop timer
    Hv_Drv_Tmr_SetInterrupt(self);

    return self;
}

HvCommonHwTimer* Hv_Common_HwTimer_Create(HvCommonHwTimer* pstTimer)
{
    if (NULL == gpstHWTimer)
    {
        gpstHWTimer = Hv_Common_HwTimer_HardWareTimerInit();
        HV_ASSERT_VALID_PTR_RET(gpstHWTimer, NULL);
        HV_LOG_DEBUG(TIMER, "Init hardware timer.");
    }

    HvCommonHwTimer* self = NULL;
    HV_ASSERT_VALID_PTR_RET(pstTimer, NULL);

    self = HV_MALLOC_OBJECT(HvCommonHwTimer);
    HV_ASSERT_VALID_PTR_RET(self, NULL);

    gui32HWTimerNumber++;

    HV_LOG_DEBUG(TIMER, "Create hw timer gui32HWTimerNumber = %d.", gui32HWTimerNumber);

    self->name = pstTimer->name;
    self->periodMs = pstTimer->periodMs;
    self->timerEvent = pstTimer->timerEvent;
    self->callbackFn = ((pstTimer->callbackFn == NULL) ? Hv_DispatchHWTimerEvent : (pstTimer->callbackFn));
    self->param = pstTimer->param;
    self->next = NULL;

    return self;
}

void Hv_Common_HwTimer_Start(HvCommonHwTimer* pstTimer)
{
    HV_ASSERT_VALID_PTR_VOID(pstTimer);
    HV_ASSERT_VALID_PTR_VOID(gpstHWTimer);

    if (HV_FALSE == gucHWTimerIsStart)
    {
        HV_LOG_DEBUG(TIMER, "Start hardware timer.");
        Hv_Common_HwTimer_StartHardWareTimer(gpstHWTimer);
        gucHWTimerIsStart = HV_TRUE;
    }

    HvCommonHwTimer** nextTimer = &gpstTimerList;

    /* Remove the existing target timer. */
    Hv_Vos_TaskEnterCritical();
    for (; *nextTimer; nextTimer = &(*nextTimer)->next)
    {
        if (pstTimer == *nextTimer) {
            *nextTimer = pstTimer->next; /* remove from list */
            break;
        }
    }
    Hv_Vos_TaskExitCritical();

    /* Set timer period. */
    pstTimer->periodMs += Hv_Common_HwTimer_GetHardWareTimerCount();

    /* Insert timer. */
    Hv_Vos_TaskEnterCritical();
    for (nextTimer = &gpstTimerList; ; nextTimer = &(*nextTimer)->next)
    {
        if (!(*nextTimer)){
            pstTimer->next = NULL;
            *nextTimer = pstTimer;
            break;
        }
        /* Sorted by periodMS */
        if (pstTimer->periodMs < (*nextTimer)->periodMs)
        {
            pstTimer->next = *nextTimer;
            *nextTimer = pstTimer;
            break;
        }
    }
    Hv_Vos_TaskExitCritical();
    return;
}

void Hv_Common_HwTimer_Stop(HvCommonHwTimer* pstTimer)
{
    HV_ASSERT_VALID_PTR_VOID(pstTimer);
    HvCommonHwTimer** nextTimer = &gpstTimerList;

    /* Find and remove timer. */
    Hv_Vos_TaskEnterCritical();
    for (; *nextTimer; nextTimer = &(*nextTimer)->next)
    {
        HvCommonHwTimer* entry = *nextTimer;
        if (entry == pstTimer)
        {
            *nextTimer = pstTimer->next;
            break;
        }
    }
    Hv_Vos_TaskExitCritical();

    /* Stop hardware timer when timer list is NULL */
    if ((gpstTimerList == NULL) && (HV_TRUE == gucHWTimerIsStart))
    {
        Hv_Common_HwTimer_StopHardWareTimer(gpstHWTimer);     /* Stop harware timer */
        gui64TimerCount = 0;                            /* Clear the timer counter */
        gucHWTimerIsStart = HV_FALSE;
        HV_LOG_DEBUG(TIMER, "Hardware Timer list is NULL, then stop hardware timer.");
    }
    return;
}

void Hv_Common_HwTimer_Delete(HvCommonHwTimer* pstTimer)
{
    HV_ASSERT_VALID_PTR_VOID(pstTimer);
    Hv_Common_HwTimer_Stop(pstTimer); /* Before free object, need stop and remove from timerlist */
    HV_FREE_OBJECT(pstTimer);
    gui32HWTimerNumber--;

    HV_LOG_DEBUG(TIMER, "Delete hardware timer gui32HWTimerNumber = %d.", gui32HWTimerNumber);

    if ((0 == gui32HWTimerNumber) && (gpstHWTimer != NULL))
    {
        Hv_Common_HwTimer_CleanupHardWareTimer(gpstHWTimer);
        gpstHWTimer = NULL;
        HV_LOG_DEBUG(TIMER, "Delete hardware timer.");
    }
    return;
}

