
#include <string.h>

#include "t1001_vtimer.h"
#include "t1001_sleep.h"
#include "t1001_rtc.h"
#include "omw_clkcal.h"

/*Minimum threshold to safely program the timer */
#define VTIMER_MARGIN (10)

#define VTIMER_MAX_VALUE (0xFFFFFFFFU)
#define VTIMER_MAX_VALUE_OVERFLOW (VTIMER_MAX_VALUE >> 1)

#define VTIMER_CLKCAL_INPROCESS (1)
#define VTIMER_CLKCAL_DONE (2)

#ifndef OMW_OTP_ROM_APP
volatile VTIMER_CONTEXT VTIMER_Context;
#else
extern volatile VTIMER_CONTEXT VTIMER_Context;
#endif

void debug_timer_list(void);

static VTIMER_HandleType* _timer_root(void)
{
    return VTIMER_Context.rootNode;
}

static uint32_t _timer_us2tick(uint32_t us)
{
    return (us * CLKCAL_XT24M_FREQ / VTIMER_Context.clkSpeed) + 1;
}

static uint32_t _timer_ms2tick(uint32_t ms)
{
    return (ms * (1000 * CLKCAL_XT24M_FREQ / VTIMER_Context.clkSpeed));
}

static uint32_t _timer_tick2ms(uint32_t tick)
{
    return (tick * VTIMER_Context.clkSpeed / (CLKCAL_XT24M_FREQ * 1000));
}

static uint8_t _timer_past(uint32_t time0, uint32_t time1)
{
    if(time0 <= time1)
    {
        return (time1 - time0) < VTIMER_MAX_VALUE_OVERFLOW;
    }

    return (time0 - time1) > VTIMER_MAX_VALUE_OVERFLOW;
}

static uint32_t _timer_add(uint32_t time0, uint32_t time1)
{
    // TODO: check time overflow
    return time0 + time1;
}

static int32_t _timer_sub(uint32_t time0, uint32_t time1)
{
    uint32_t diff;
    if(time0 >= time1)
    {
        diff = time0 - time1;
        if(diff > VTIMER_MAX_VALUE_OVERFLOW)
        {
            return diff - VTIMER_MAX_VALUE - 1;
        }
        return diff;
    }
    diff = time1 - time0;

    if(diff > VTIMER_MAX_VALUE_OVERFLOW)
    {
        return VTIMER_MAX_VALUE + 1 - diff;
    }
    return -diff;
}

static void _timer_target_set(uint8_t isEnable, uint32_t timeout)
{
    RTC_ITConfig(RTC_IT_Timeout, isEnable);
    if(isEnable)
    {
        RTC_SetTimerTarget(timeout);
    }
}

static void _timer_refresh_timeout(void)
{
    VTIMER_HandleType *t = _timer_root();

    if(t)
    {
        uint32_t timeout = t->expiryTime;
        uint32_t limit = _timer_add(VTIMER_GetCurrentSysTime(), VTIMER_MARGIN);
        if(_timer_past(timeout, limit))
        {
            timeout = limit;
        }
        _timer_target_set(1, timeout);

#ifdef VTIMER_DEBUG
        printf("_timer_refresh_timeout(), currentTime: 0x%x, timeout: 0x%x\r\n", VTIMER_GetCurrentSysTime(), timeout);
#endif
    }
    else
    {
        _timer_target_set(0, 0);
    }
}

static void _timer_remove(VTIMER_HandleType* handle)
{
    ATOMIC_SECTION_BEGIN();
    VTIMER_HandleType *current = _timer_root();
    VTIMER_HandleType *prev = NULL;

#ifdef VTIMER_DEBUG
    printf("_timer_remove(), current: 0x%x, handle: 0x%x\r\n", current, handle);
#endif

    while ((current != NULL) && (current != handle))
    {
        prev = current;
        current = current->next;
    }

    if(current == handle)
    {
        if(prev)
        {
            prev->next = handle->next;
        }
        else
        {
            VTIMER_Context.rootNode = current->next;

            _timer_refresh_timeout();
        }
    }
    ATOMIC_SECTION_END();

    debug_timer_list();
}

static uint8_t _timer_check_in_queue(VTIMER_HandleType* handle)
{
    VTIMER_HandleType *current = _timer_root();

    while ((current != NULL) && (current != handle))
    {
        current = current->next;
    }

    if(current == handle)
    {
        return 1;
    }

    return 0;
}


static void _timer_insert(VTIMER_HandleType *handle)
{
    ATOMIC_SECTION_BEGIN();
    VTIMER_HandleType *current = _timer_root();
    VTIMER_HandleType *prev = NULL;

#ifdef VTIMER_DEBUG
    printf("_timer_insert(), current: 0x%x, handle: 0x%x\r\n", current, handle);
#endif
    while ((current!=NULL) && _timer_past(current->expiryTime, handle->expiryTime))
    {
        prev = current;
        current = current->next;
    }

    handle->next = current;

    if (prev == NULL)
    {
        /* We are the new root */
        VTIMER_Context.rootNode = handle;

        _timer_refresh_timeout();
    }
    else
    {
        prev->next = handle;
    }
    ATOMIC_SECTION_END();

    debug_timer_list();
}


static void _timer_expire(void)
{
    VTIMER_HandleType *t;
    while ((t = _timer_root()) != NULL && _timer_past(t->expiryTime, VTIMER_GetCurrentSysTime()))
    {
        _timer_remove(t);

        if(t->callback)
        {
            t->callback(t->userData);
        }
    }
}

static void _start_timer(VTIMER_HandleType *timerHandle, uint32_t time)
{
#ifdef VTIMER_DEBUG
    printf("_start_timer(), time: 0x%x, current: 0x%x\r\n", time, VTIMER_GetCurrentSysTime());
#endif
    /* Force stop timer */
    _timer_remove(timerHandle);
    /* The timer is already started */
    timerHandle->expiryTime = time;

    _timer_insert(timerHandle);
}


void debug_timer_list(void)
{
#ifdef VTIMER_DEBUG
    VTIMER_HandleType *current = _timer_root();
    printf("debug_timer_list(), root: 0x%x\r\n", current);
    while(current != NULL) {
        printf("debug_timer_list(), current: 0x%x, next: 0x%x\r\n", current, current->next);
        current = current->next;
    }
#endif
}

//precision: < 32us
void VTIMER_startTimerUs(VTIMER_HandleType *timerHandle, uint32_t usRelTimeout)
{
    (void)VTIMER_StartTimerSysTime(timerHandle, _timer_add(VTIMER_GetCurrentSysTime(), _timer_us2tick(usRelTimeout)));
}

/**
 * @brief       Starts a one-shot virtual timer for the given relative timeout value expressed in ms
 * @param[in]   timerHandle: The virtual timer
 * @param[in]   msRelTimeout: The relative time, from current time, expressed in ms
 * @return      0 if the timerHandle is valid. 1 if the timerHandle is not valid. It is already started.
 */
int VTIMER_StartTimerMs(VTIMER_HandleType *timerHandle, uint32_t msRelTimeout)
{
    return VTIMER_StartTimerSysTime(timerHandle, _timer_add(VTIMER_GetCurrentSysTime(), _timer_ms2tick(msRelTimeout)));
}

/**
 * @brief      Stops the one-shot virtual timer specified if found
 * @param[in]  timerHandle: The virtual timer
 */
void VTIMER_StopTimer(VTIMER_HandleType *timerHandle)
{
    _timer_remove(timerHandle);
}

/**
 * @brief      Check the timer start.
 * @param[in]  timerHandle: The virtual timer
 */
uint8_t VTIMER_CheckTimerStart(VTIMER_HandleType *timerHandle)
{
    return _timer_check_in_queue(timerHandle);
}

/**
 * @brief  This function returns the current reference time expressed in system time units.
 *         The returned value can be used as absolute time parameter where needed in the other
 *         VTIMER* APIs
 * @return absolute current time expressed in system time units.
 */
uint32_t VTIMER_GetCurrentSysTime(void)
{
    return RTC_GetCurrentSysTime();
}

/**
 * @brief  This function returns the current target time expressed in system time units.
 *
 * @return absolute current time expressed in system time units.
 */
uint32_t VTIMER_GetTimerTarget(void)
{
    return RTC_GetTimerTarget();
}

/**
 * @brief  Check two absolute times past: sysTime1<sysTime2.
 * @param[in]  sysTime2: Absolute time expressed in internal time units.
 * @param[in]  sysTime1: Absolute time expressed in internal time units.
 * @return resulting 1 means past(sysTime1<sysTime2).
 */
uint8_t VTIMER_TimerPast(uint32_t sysTime1, uint32_t sysTime2)
{
    return _timer_past(sysTime1, sysTime2);
}

/**
 * @brief This function returns the sum of an absolute time and a signed relative time.
 * @param[in]  sysTime: Absolute time expressed in internal time units.
 * @param[in]  msTime: Signed relative time expressed in ms.
 * @return 32bit resulting absolute time expressed in internal time units.
 */
uint32_t VTIMER_AddSysTimeMs(uint32_t sysTime, int32_t msTime)
{
    return _timer_add(sysTime, _timer_ms2tick(msTime));
}


/**
 * @brief  Returns the difference between two absolute times: sysTime1-sysTime2.
 * @param[in]  sysTime2: Absolute time expressed in internal time units.
 * @param[in]  sysTime1: Absolute time expressed in internal time units.
 * @return resulting signed relative time expressed in internal time units.
 */
int32_t VTIMER_DiffSysTime(uint32_t sysTime1, uint32_t sysTime2)
{
    return _timer_sub(sysTime1, sysTime2);
}

/**
 * @brief  Returns the difference between two absolute times: sysTime1-sysTime2.
 * The resulting value is expressed in ms.
 * @param[in]  sysTime2: Absolute time expressed in internal time units.
 * @param[in]  sysTime1: Absolute time expressed in internal time units.
 * @return resulting signed relative time expressed in ms.
 */
int32_t VTIMER_DiffSysTimeMs(uint32_t sysTime1, uint32_t sysTime2)
{
    return _timer_tick2ms(VTIMER_DiffSysTime(sysTime1, sysTime2));
}

/**
 * @brief Starts a one-shot virtual timer for the given absolute timeout value
 *        expressed in internal time units.
 * @param[in] timerHandle: The virtual timer
 * @param[in] time: Absolute time expressed in STU.
 * @return 0 if the timerHandle is valid. 1 if the timerHandle is not valid. It is already started.
*/
int VTIMER_StartTimerSysTime(VTIMER_HandleType *timerHandle, uint32_t time)
{
    if(timerHandle == NULL)
    {
        return 1;
    }
    _start_timer(timerHandle, time);
    return 0;
}

// static uint32_t div64_32(uint32_t a[], uint32_t b)
// {
// //假设你的64位数是一个32位数组，a[0]为低32位，a[1]为高32位
//     uint32_t i, j, k, m, c[2] = { 0 };
//     for (i = 2, j = k = 0; i; i--)
//     {
//         m = 0x80000000;                    //位探针，从最高位右移到最低位
//         for (; m; m >>= 1)
//         {
//             j = (k >= 0x80000000);         //j为进位，k为余数
//             k <<= 1;                       //余数左移一位
//             k |= ((m&a[i - 1]) && 1);      //将被除数当前位补进余数最低位
//             c[1] <<= 1;                    //商高位左移1位为低位左移溢出做准备
//             c[1] |= (c[0] >= 0x80000000);  //低位左移溢出到高位
//             c[0] <<= 1;                    //低位左移一次准备做除法
//             c[0] |= (j || k >= b);         //如果余数大于被除数则商低位补1
//             if (j)k = k + ~b + 1;          //余数达到33位则“余数=当前余数+除数补码”
//             else if (k >= b)k -= b;        //当前余数为32位且大于被除数则“余数=当前余数-除数”
//         }
//     }//64位除以32位运算完成，商在数组c中

//     return c[0];

//     // a[0] = c[0];
//     // a[1] = c[1];                          //商以数组参数a返回
//     // return k;                             //返回余数
// }

// uint32_t  big_data_mmd(uint32_t a, uint32_t b, uint32_t c)
// {
//     uint64_t  dd  = ((uint64_t)a) * b;

// #if 0
//     uint32_t  rlt = 0;
//     const uint32_t  pp  = (0xFFFFFFFFUL / c);
//     const uint32_t  cut = pp * c;

//     do
//     {
//         if (dd <= 0xFFFFFFFFUL)
//         {
//             rlt += (((uint32_t)dd) / c);
//             return rlt;
//         }

//         dd -= cut;
//         rlt += pp;
//     }while(1);
// #else
//     if (dd <= 0xFFFFFFFFUL) return (((uint32_t)dd) / c);

//     return div64_32((uint32_t *)(&dd), c);
// #endif
// }

uint32_t VTIMER_ConvertUsToTick(uint32_t us)
{
    // return big_data_mmd(us, VTIMER_Context.clkSpeed, 1000000);
    // return (uint64_t) us * VTIMER_Context.clkSpeed / 1000000;
    return (us * CLKCAL_XT24M_FREQ / VTIMER_Context.clkSpeed);
}

uint32_t VTIMER_ConvertTickToUs(uint32_t tick)
{
    // return big_data_mmd(tick, 1000000, VTIMER_Context.clkSpeed);
    // return (uint64_t) tick * 1000000 / VTIMER_Context.clkSpeed;
    return (tick * VTIMER_Context.clkSpeed / CLKCAL_XT24M_FREQ);
}

uint32_t VTIMER_ConvertTickToMs(uint32_t tick)
{
    // return big_data_mmd(tick, 1000, VTIMER_Context.clkSpeed);
    // return (uint64_t) tick * 1000 / VTIMER_Context.clkSpeed;
    return (tick * VTIMER_Context.clkSpeed / (CLKCAL_XT24M_FREQ * 1000));
}

void delay_us(uint32_t us);
// void VTIMER_ClkCali_Delay(int d);

#ifndef OMW_OTP_ROM_APP
#ifdef OMW_OTP_ROM
#undef __RAM_CODE_SECTION
#define __RAM_CODE_SECTION
#endif

// // Delay, unit: about 6us
// #ifdef OMW_EN_DEEP_SLEEP
// __RAM_CODE_SECTION
// #endif
// void VTIMER_ClkCali_Delay(int d){
//     volatile int i, j;
//     for(i=0; i<d; i++)
//         for(j=0; j<12; j++);
// }
#endif

/**
 * @brief Initialize the timer module. It must be placed in the initialization
 *         section of the application.
*/
void VTIMER_Init(void)
{
    memset((void *)&VTIMER_Context, 0, sizeof(VTIMER_Context));

    // LPM_CTRL->RTC_CTRL_b.RTC_EN = 1;  //RTC has been EN on SystemInit()

    // // Give a default value
    // VTIMER_Context.clkSpeed = 32768;

    // VTIMER_Context.isClkCal = VTIMER_CLKCAL_INPROCESS;
    // // Clock Cal.
    // SYS_CTRL->CLKCAL_CLKSEL = (CAL_SEL_XT24M << 8) | (CAL_SEL_RC32K);

    // SYS_CTRL->CLKCAL_CNT0 = 24000000;

    // // Start Cal.
    // SYS_CTRL->CLKCAL_CTRL |= SYS_CTRL_CLKCAL_CTRL_CLKCAL_ENABLE_Msk | SYS_CTRL_CLKCAL_CTRL_CLKCAL_INT_EN_Msk;

    for (int i = 0; i < CLK_CAL_WIN_SZ; i++)
    {
        VTIMER_ClkCali_Trigger(CLKCAL_CLK_RC32K_CYCS_SH);

        delay_us(60);
        // VTIMER_ClkCali_Delay(10); // delay 60us to wait clk stable

        SYS_CTRL->CLKCAL_CTRL = (1<<0); // enable calibration

        VTIMER_ClkCali_Wait();

        REG_CLR_BIT(&SYS_CTRL->CLKCAL_CTRL, 0);
        omw_clkcal_reset();

        delay_us(1000);
    }

    // VTIMER_Context.nxt_clkSpeed = VTIMER_Context.clk_cal_win[0];
    VTIMER_Context.clkSpeed = VTIMER_Context.nxt_clkSpeed;
}

#ifndef OMW_OTP_ROM_APP

#define CLKCAL_CLK_CORNER       4       // 200K
#define CLKCAL_CLK_XT24M        3       // 24M
#define CLKCAL_CLK_XT32K        2       // 32K
#define CLKCAL_CLK_RC24M        1       // 24M
#define CLKCAL_CLK_RC32K        0       // 32K

#ifdef OMW_EN_DEEP_SLEEP
__RAM_CODE_SECTION
#endif
void VTIMER_ClkCali_Trigger(uint32_t cycles_sh)
{
    int refclk = CLKCAL_CLK_XT24M; //XTAL 24M
    int calclk = CLKCAL_CLK_RC32K;
    int cyc = (1 << cycles_sh);

    int t = ((1<<calclk) << 8) | (1<<refclk);

    SYS_CTRL->CLKCAL_CLKSEL = t;
    SYS_CTRL->CLKCAL_CNT0 = cyc;

    VTIMER_Context.cal_cycls_sh = (cycles_sh - CLKCAL_CLK_RC32K_FIX_SH);
}

#ifdef OMW_EN_DEEP_SLEEP
__RAM_CODE_SECTION
#endif
void VTIMER_ClkCali_Wait()
{
    int t;
    // wait
    while(1){
        t = SYS_CTRL->CLKCAL_CTRL;
        t = t & (1<<4);
        if(t) break;
    }

    uint8_t  cur_idx = VTIMER_Context.clk_cal_idx;
    VTIMER_Context.ttl_clk_cnts -= VTIMER_Context.clk_cal_win[cur_idx];
    VTIMER_Context.clk_cal_win[cur_idx] = (SYS_CTRL->CLKCAL_CNT1 >> VTIMER_Context.cal_cycls_sh);
    VTIMER_Context.ttl_clk_cnts += VTIMER_Context.clk_cal_win[cur_idx];
    VTIMER_Context.clk_cal_idx = (cur_idx + 1) & (CLK_CAL_WIN_SZ - 1);

    if (VTIMER_Context.clk_cal_win[CLK_CAL_WIN_SZ - 1] != 0)
    {
        VTIMER_Context.nxt_clkSpeed = (VTIMER_Context.ttl_clk_cnts >> CLK_CAL_WIN_SZ_BITS);
    }
}
#endif

/**
 * @brief  IRQ Handler work.
 * @retval None
 */
void VTIMER_IRQHandler(void)
{
    if(RTC_GetITStatus(RTC1_IT_STATUS_Timeout))
    {
        RTC_ClearITPendingBit(RTC1_IT_STATUS_Timeout);
        _timer_expire();
    }
}

/**
 * @brief  IRQ Handler work.
 * @retval None
 */
// void VTIMER_CLKCALIRQHandler(void)
// {
//     // printf("VTIMER_ClkCalIRQHandler\r\n");
//     if((SYS_CTRL->CLKCAL_CTRL & SYS_CTRL_CLKCAL_CTRL_CLKCAL_INT_Msk) != 0)
//     {
//         SYS_CTRL->CLKCAL_CTRL |= SYS_CTRL_CLKCAL_CTRL_CLKCAL_INT_CLR_Msk;

//         VTIMER_Context.clkSpeed = SYS_CTRL->CLKCAL_CNT1;
//         VTIMER_Context.isClkCal = VTIMER_CLKCAL_DONE;

//         // printf("CLKCAL_CTRL: 0x%x, CLKCAL_CNT0: 0x%x, CLKCAL_CNT1: 0x%x, real_freq: %d\r\n", SYS_CTRL->CLKCAL_CTRL, SYS_CTRL->CLKCAL_CNT0, SYS_CTRL->CLKCAL_CNT1, VTIMER_Context.clkSpeed);
//         SYS_CTRL->CLKCAL_CTRL = 0;
//     }
// }
#ifdef OMW_EN_DEEP_SLEEP
SleepModes Timer_SleepMode_Check(SleepModes sleepMode)
{
    // if(VTIMER_Context.isClkCal == VTIMER_CLKCAL_INPROCESS)
    // {
    //     return SLEEPMODE_CPU_HALT;
    // }

    if(_timer_root() == NULL)
    {
        return SLEEPMODE_NOTIMER;
    }


    uint32_t target = VTIMER_GetTimerTarget();
    int32_t duration = VTIMER_DiffSysTime(target, VTIMER_GetCurrentSysTime());

    if(duration < t1001_sleep_get_protect_time())
    {
        return SLEEPMODE_CPU_HALT;
    }
    return SLEEPMODE_WAKETIMER;
}
#endif