#include <stddef.h>
#include "Sys_Scheduler_Lib.h"

typedef struct
{
    uint32_t u32msRocBak;
    uint32_t u32Task2msCnt;
    uint32_t u32Task5msCnt;
    uint32_t u32Task10msCnt;
    uint32_t u32Task20msCnt;
    uint32_t u32Task50msCnt;
    uint32_t u32Task100msCnt;
} Sys_Scheduler_st_t;

typedef struct 
{
    uint32_t u32Pending;
    uint32_t u32usCounter;
    Sys_Timer_st_t *pstList;
}Sys_Timer_Ctrl_st_t;

#define   SYS_TIMER_MODE_IDLE               (0UL)
#define   SYS_TIMER_MODE_RUN                (1UL)
#define   SYS_TIMER_MODE_LOOP               (2UL)

uint32_t            g_u32SysIntInterval      = 0UL;
uint32_t            g_u32SysusCounter        = 0UL;
volatile uint32_t   g_u32SysmsRollingCounter = 0UL;

Sys_Scheduler_st_t  g_stSysScheduler;
Sys_Timer_Ctrl_st_t g_stSysTimerCtrl;

uint32_t Sys_Get_ms_Rolling_Counter(void)
{
    uint32_t u32Counter[2];

    do
    {
        u32Counter[0] = g_u32SysmsRollingCounter;
        u32Counter[1] = g_u32SysmsRollingCounter;
    }while (u32Counter[0] != u32Counter[1]);
    
    return u32Counter[0];
}

int32_t Sys_Get_Running_Time(Sys_Time_st_t *pstTime)
{
    int32_t  i32Result;
    uint32_t u32msCounter;

    i32Result = -1;
    if (pstTime != NULL)
    {
        u32msCounter = Sys_Get_ms_Rolling_Counter();

        pstTime->u32Hour  = u32msCounter / 3600000UL;
        u32msCounter      = u32msCounter % 3600000UL;

        pstTime->u32Min   = u32msCounter / 60000UL;
        u32msCounter      = u32msCounter % 60000UL;

        pstTime->u32Sec   = u32msCounter / 1000UL;
        u32msCounter      = u32msCounter % 1000UL;

        pstTime->u32mSec  = u32msCounter;
        
        i32Result = 0;
    }

    return i32Result;
}

int32_t Sys_Timer_Start(Sys_Timer_st_t *pHandle, uint32_t u32Interval, uint32_t u32Loop, Sys_Timer_Cb_pfn_t pfnCallBack)
{
    int32_t  i32Result;
    uint32_t u32Exist;
    Sys_Timer_st_t *pstSearch;

    i32Result = -1;
    if (pHandle != NULL)
    {
        pHandle->u32Run = SYS_TIMER_MODE_IDLE;

        pHandle->u32Cnt      = 0UL;
        pHandle->u32Dst      = u32Interval;
        pHandle->pfnCallBack = pfnCallBack;

        if (g_stSysTimerCtrl.pstList == NULL)
        {
            g_stSysTimerCtrl.u32Pending = 1UL;
            
            pHandle->pstNext = NULL;
            g_stSysTimerCtrl.pstList = pHandle;

            g_stSysTimerCtrl.u32Pending = 0UL;
        }
        else if (g_stSysTimerCtrl.pstList == pHandle)
        {
            /* Nothing to do */
        }
        else
        {
            u32Exist  = 0UL;
            pstSearch = g_stSysTimerCtrl.pstList;
            while((pstSearch->pstNext != NULL) && (u32Exist == 0UL))
            {
                pstSearch = pstSearch->pstNext;
                if (pstSearch == pHandle)
                {
                    u32Exist = 1UL;
                }
            }

            if (u32Exist == 0UL)
            {
                g_stSysTimerCtrl.u32Pending = 1UL;
                
                pHandle->pstNext = NULL;
                pstSearch->pstNext = pHandle;
                
                g_stSysTimerCtrl.u32Pending = 0UL;
            }
        }

        if (u32Loop)
        {
            pHandle->u32Run = SYS_TIMER_MODE_LOOP;
        }
        else
        {
            pHandle->u32Run = SYS_TIMER_MODE_RUN;
        }

        i32Result = 0;
    }

    return i32Result;
}

int32_t Sys_Timer_Stop(Sys_Timer_st_t *pHandle)
{
    int32_t  i32Result;
    Sys_Timer_st_t *pstSearch;

    i32Result = -1;
    if (pHandle != NULL)
    {
        if (g_stSysTimerCtrl.pstList == NULL)
        {
            /* Nothing to do */
        }
        else if (g_stSysTimerCtrl.pstList == pHandle)
        {
            g_stSysTimerCtrl.u32Pending = 1UL;

            pHandle->u32Run = SYS_TIMER_MODE_IDLE;
            g_stSysTimerCtrl.pstList = pHandle->pstNext;
            
            g_stSysTimerCtrl.u32Pending = 0UL;

            i32Result = 0;
        }
        else
        {
            pstSearch = g_stSysTimerCtrl.pstList;
            while ((pstSearch->pstNext != NULL) && (pstSearch->pstNext != pHandle))
            {
                pstSearch = pstSearch->pstNext;
            }

            if (pstSearch->pstNext == pHandle)
            {
                g_stSysTimerCtrl.u32Pending = 1UL;

                pHandle->u32Run = SYS_TIMER_MODE_IDLE;
                pstSearch->pstNext = pHandle->pstNext;
                
                g_stSysTimerCtrl.u32Pending = 0UL;

                i32Result = 0;
            }
        }
    }

    return i32Result;
}

int32_t Sys_Timer_Get_Status(Sys_Timer_st_t *pHandle)
{
    int32_t  i32Result;
    
    i32Result = -1;
    if (pHandle != NULL)
    {
        if (pHandle->u32Run)
        {
            i32Result = 1;
        }
        else
        {
            i32Result = 0;
        }
    }

    return i32Result;
}

int32_t Sys_Timer_Get_Counter(Sys_Timer_st_t *pHandle, uint32_t *pu32Counter)
{
    int32_t  i32Result;
    volatile uint32_t *pu32Cnt;
    uint32_t u32Counter[2];
    
    i32Result = -1;
    if ((pHandle != NULL) && (pu32Counter != NULL))
    {
        pu32Cnt = (volatile uint32_t *)(&pHandle->u32Cnt);

        do
        {
            u32Counter[0] = *pu32Cnt;
            u32Counter[1] = *pu32Cnt;
        } while (u32Counter[0] != u32Counter[1]);

        *pu32Counter = u32Counter[0];
        i32Result = 0;
    }
    
    return i32Result;
}

void Sys_Scheduler_Start(uint32_t u32SchCycle)
{
    g_u32SysIntInterval = u32SchCycle;
    g_u32SysusCounter   = 0UL;
    g_u32SysmsRollingCounter = 0UL;

    g_stSysTimerCtrl.u32Pending      = 0UL;
    g_stSysTimerCtrl.u32usCounter    = 0UL;
    g_stSysTimerCtrl.pstList         = NULL;

    g_stSysScheduler.u32msRocBak     = Sys_Get_ms_Rolling_Counter();
    g_stSysScheduler.u32Task2msCnt   = 0UL;
    g_stSysScheduler.u32Task5msCnt   = 0UL;
    g_stSysScheduler.u32Task10msCnt  = 1UL;
    g_stSysScheduler.u32Task20msCnt  = 3UL;
    g_stSysScheduler.u32Task50msCnt  = 5UL;
    g_stSysScheduler.u32Task100msCnt = 7UL;
}

void Sys_Scheduling_Service(void)
{
    uint32_t u32msROC;
    uint32_t u32msDelta;
    
    Sys_Pseudo_Real_Time_Tasks();

    u32msROC = Sys_Get_ms_Rolling_Counter();
    if (u32msROC >= g_stSysScheduler.u32msRocBak)
    {
        u32msDelta = u32msROC - g_stSysScheduler.u32msRocBak;
    }
    else
    {
        u32msDelta = 0xFFFFFFFFUL - g_stSysScheduler.u32msRocBak + u32msROC + 0x00000001UL;
    }
    g_stSysScheduler.u32msRocBak = u32msROC;

    g_stSysScheduler.u32Task2msCnt += u32msDelta;
    if (g_stSysScheduler.u32Task2msCnt >= 2UL)
    {
        g_stSysScheduler.u32Task2msCnt %= 2UL;
        Sys_2ms_Tasks();
    }

    g_stSysScheduler.u32Task5msCnt += u32msDelta;
    if (g_stSysScheduler.u32Task5msCnt >= 5UL)
    {
        g_stSysScheduler.u32Task5msCnt %= 5UL;
        Sys_5ms_Tasks();
    }

    g_stSysScheduler.u32Task10msCnt += u32msDelta;
    if (g_stSysScheduler.u32Task10msCnt >= 10UL)
    {
        g_stSysScheduler.u32Task10msCnt %= 10UL;
        Sys_10ms_Tasks();
    }

    g_stSysScheduler.u32Task20msCnt += u32msDelta;
    if (g_stSysScheduler.u32Task20msCnt >= 20UL)
    {
        g_stSysScheduler.u32Task20msCnt %= 20UL;
        Sys_20ms_Tasks();
    }

    g_stSysScheduler.u32Task50msCnt += u32msDelta;
    if (g_stSysScheduler.u32Task50msCnt >= 50UL)
    {
        g_stSysScheduler.u32Task50msCnt %= 50UL;
        Sys_50ms_Tasks();
    }

    g_stSysScheduler.u32Task100msCnt += u32msDelta;
    if (g_stSysScheduler.u32Task100msCnt >= 100UL)
    {
        g_stSysScheduler.u32Task100msCnt %= 100UL;
        Sys_100ms_Tasks();
    }
}

void Sys_Scheduler_ISR(void)
{
    uint32_t u32msInc;
    Sys_Timer_st_t *pstTimer;
    Sys_Timer_st_t *pstPrev;

    g_u32SysusCounter += g_u32SysIntInterval;
    if (g_u32SysusCounter >= 1000UL)
    {
        u32msInc = g_u32SysusCounter / 1000UL;
        g_u32SysusCounter = g_u32SysusCounter % 1000UL;
        g_u32SysmsRollingCounter += u32msInc;
    }

    g_stSysTimerCtrl.u32usCounter += g_u32SysIntInterval;
    if (g_stSysTimerCtrl.u32Pending == 0UL)
    {
        pstPrev  = NULL;
        pstTimer = g_stSysTimerCtrl.pstList;
        while (pstTimer != NULL)
        {
            if (pstTimer->u32Run)
            {
                /* if (pstTimer->u32Cnt + u32Interval < 0xFFFFFFFFUL) */
                if (pstTimer->u32Cnt < 0xFFFFFFFFUL - g_stSysTimerCtrl.u32usCounter)
                {
                    pstTimer->u32Cnt += g_stSysTimerCtrl.u32usCounter;
                }
                else
                {
                    pstTimer->u32Cnt = 0xFFFFFFFFUL;
                }
                
                if (pstTimer->u32Cnt >= pstTimer->u32Dst)
                {
                    if (pstTimer->u32Run == SYS_TIMER_MODE_RUN)
                    {
                        pstTimer->u32Run = SYS_TIMER_MODE_IDLE;

                        if (pstPrev == NULL)
                        {
                            g_stSysTimerCtrl.pstList = pstTimer->pstNext;
                        }
                        else
                        {
                            pstPrev->pstNext = pstTimer->pstNext;
                        }
                    }
                    else
                    {
                        pstTimer->u32Cnt = 0UL;
                    }

                    if (pstTimer->pfnCallBack != NULL)
                    {
                        pstTimer->pfnCallBack();
                    }
                }
            }

            pstPrev  = pstTimer;        
            pstTimer = pstTimer->pstNext;
        }
        
        g_stSysTimerCtrl.u32usCounter = 0UL;
    }
}