#include <stdint.h>
#include <stdlib.h>
#include <string.h>

#include "t1001.h"
#include "t1001_rtc.h"
#include "t1001_uart.h"
#include "omw_gpio.h"

#include "t1001_sleep.h"
#include "hci_driver.h"

#include "t1001_vtimer.h"
#include "t1001_bt_timer.h"
#include "omw_timer.h"
#ifdef OMW_EN_DEEP_SLEEP
#define T1001_SLEEP_MAX_ADDR_NR_EXT (sizeof(addr_list) / sizeof(uint32_t))

static uint32_t *g_save_buf;

#ifdef OMW_SAVE_UART0_REGS
static uint32_t *uart0_save_buf;
#endif
#ifdef OMW_SAVE_UART1_REGS
static uint32_t *uart1_save_buf;
#endif

volatile uint32_t sleep_wakeup_reason;

static uint32_t addr_list[] =
    {
/////////////////////////////////////////////// REGS///////////////////////////////////
/* Device regs */
#if defined(OMW_SAVE_UART1_REGS) && defined(OMW_CMD_CTRL_EN_IT_RX)
        (uint32_t)&CLIC->CLICINT[UART1_IRQn],
#endif

        0x400000B4, 0x400000B8,

        (uint32_t)&CLIC->CLICINT[RTC_IRQn],

        /* GPIO regs */
        0x40010200, 0x40010204, 0x40010208, 0x4001020C, 0x40010210, 0x40010214, 0x40010300, 0x40010304,
        0x40010010, 0x40010030, 0x40010040, 0x40010170, 0x40010180, 0x40010190, 0x400101A0, 0x40010140,

        /* SPIS regs */
		
		/* TIM0 regs */
		(uint32_t)&CLIC->CLICINT[TIMER0_IRQn],
		0x41008000,0x41008004,0x41008008,0x4100800C,0x41008010,0x41008014,0x41008018,0x4100801C,0x41008020,0x41008024,
        /* ... */
};

#ifdef OMW_RSTR_MORE_CR_REGS
uint32_t more_cr_regs[] =
    {}
#endif

#if defined(OMW_SAVE_UART0_REGS) || defined(OMW_SAVE_UART1_REGS)
#include "t1001_uart.h"

#define IER_DLL 5
#define IER_DLF (sizeof(uart_addr_idx_list) - 1)
uint32_t Timer4_Load_Count=0;
uint32_t Timer0_Load_Count=0;
uint8_t uart_addr_idx_list[] =
    {
        OMW_UART_LCR,
        OMW_UART_LCR_EXT,
        OMW_UART_FCR,
        OMW_UART_MCR,
        OMW_UART_IER,
        OMW_UART_DLL,
        OMW_UART_DLH,
        OMW_UART_DLF,
};

void wait_uart_not_busy(uint32_t *uart_base_addr);

static void t1001_sleep_save_uart_reg_info(uint32_t *uart, uint32_t *save_buf)
{
    volatile uint32_t *uart_reg_base = (volatile uint32_t *)uart;

    for (int i = 0; i < sizeof(uart_addr_idx_list); i++)
    {
        if (i == IER_DLL)
        {
            wait_uart_not_busy(uart);

            uart_reg_base[OMW_UART_LCR] |= 0x80; // LCR_DLAB
        }

        save_buf[i] = uart_reg_base[uart_addr_idx_list[i]];
    }
}



static void hc_timer_sleep_save_reg(void){
	Timer0_Load_Count = *(volatile uint32_t*)(0x41008000);
	omw_timer_en(TIMER0,DISABLE);
	omw_timer_it_en(TIMER0,DISABLE);
	omw_timer_it_clr();
}

static void hc_timer_sleep_restore_reg(void){
	*(volatile uint32_t*)(0x41008000) = Timer0_Load_Count;
	omw_timer_it_clr();
	omw_timer_en(TIMER0,ENABLE);
	omw_timer_it_en(TIMER0,ENABLE);
}


void t1001_sleep_restore_uart_reg_info(uint32_t *uart, uint32_t *save_buf)
{
    volatile uint32_t *uart_reg_base = (volatile uint32_t *)uart;

    for (int i = 0; i < sizeof(uart_addr_idx_list); i++)
    {
        if (i == IER_DLL)
        {
            wait_uart_not_busy(uart);

            uart_reg_base[OMW_UART_LCR] |= 0x80; // LCR_DLAB
        }

        uart_reg_base[uart_addr_idx_list[i]] = save_buf[i];

        if (i == IER_DLF)
        {
            uart_reg_base[OMW_UART_LCR] &= (~0x80); // LCR_DLAB
        }
    }
}
#endif

static void t1001_sleep_save_reg_info_ext()
{
    for (uint8_t i = 0; i < T1001_SLEEP_MAX_ADDR_NR_EXT; i++)
    {
        g_save_buf[i] = *(uint32_t *)addr_list[i];
    }

#ifdef OMW_RSTR_MORE_CR_REGS
    for (int j = 0; j < sizeof(more_cr_regs) / 4; j++)
    {
        g_save_buf[j + T1001_SLEEP_MAX_ADDR_NR_EXT] = *(uint32_t *)more_cr_regs[j];
    }
#endif

#ifdef OMW_SAVE_UART0_REGS
    // maybe need to de-init uart gpio
    t1001_sleep_save_uart_reg_info(OMW_UART0, uart0_save_buf);
#endif

#ifdef OMW_SAVE_UART1_REGS
    // maybe need to de-init uart gpio
    t1001_sleep_save_uart_reg_info(OMW_UART1, uart1_save_buf);
#endif
	hc_timer_sleep_save_reg();
}

static void t1001_sleep_restore_reg_info_ext()
{
#ifdef OMW_SAVE_UART0_REGS
    t1001_sleep_restore_uart_reg_info(OMW_UART0, uart0_save_buf);
#endif

#ifdef OMW_SAVE_UART1_REGS
    t1001_sleep_restore_uart_reg_info(OMW_UART1, uart1_save_buf);
#endif

    for (uint8_t i = 0; i < T1001_SLEEP_MAX_ADDR_NR_EXT; i++)
    {
        *(uint32_t *)addr_list[i] = g_save_buf[i];
    }
	hc_timer_sleep_restore_reg();
}

uint8_t omw_app_can_sleep();

#ifdef OMW_BLE_HOST_V0
uint8_t bt_conn_can_sleep_check();
#endif

uint8_t App_SleepMode_Check()
{
    if (!omw_app_can_sleep())
        return 0;

#ifdef OMW_BLE_HOST_V0
    if (!bt_conn_can_sleep_check())
        return 0;
#endif

    return 1;
}

SleepModes Bt_SleepMode_Check(void);
#ifdef OMW_BLE_HOST_V0
uint8_t hci_core_sleep_check();
#endif

__RAM_CODE_SECTION
SleepModes Bt_SleepMode_Check(void)
{
#ifdef OMW_BLE_HOST_V0
    if (!hci_core_sleep_check())
        return SLEEPMODE_RUNNING;
#endif

    return Bt_Controller_SleepMode_Check();
}

#ifdef CONFIG_OTP_PROGRAM
extern void (*otp_init_ptr)(void);
void (*otp_init)(void) = (void *)0x00000102;
#endif

// #ifdef CONFIG_FLASH_PROGRAM
//  Description: set flash power GPIO state, from output '1' to '0'
//  NOTE:
//    1) if more than one GPIO, invoke this function more than one times
__RAM_CODE_SECTION
void flash_power_off(int p)
{
#ifdef CONFIG_FLASH_PROGRAM
    int t;
    t = GPIO_INOUT->GPIO_O;
    t = t & (~(1 << p));
    GPIO_INOUT->GPIO_O = t;
#endif
}

__RAM_CODE_SECTION
void qspi_regs_restore()
{
#ifdef CONFIG_FLASH_PROGRAM
    //   REG_WRT(0x40010208, QSPI_VDD_FUNC_REG_VAL);

    
    //
    REG_WRT(0x40010208, QSPI_FLASH_FUNC_REG1_VAL);
    REG_WRT(0x4001020C, QSPI_FLASH_FUNC_REG2_VAL);
    //
    // REG_WRT(0x4001020C, QSPI_FLASH_FUNC_REG1_VAL);
    // REG_WRT(0x40010210, QSPI_FLASH_FUNC_REG2_VAL);


    //    REG_WRT(0x40010300, QSPI_REMAP_REG_VAL);
    //
    //    REG_WRT(0x40010030, QSPI_VDD_OE_REG_VAL);
    //    REG_WRT(0x40010040, QSPI_VDD_O_REG_VAL);
#endif
}

extern void (*restore_more_ext_cr_regs)(void);
extern void (*qspi_regs_restore_ptr)();

extern void (*gpio_regs_restore_before_rel_gpio_hold_ptr)();

__RAM_CODE_SECTION
void gpio_regs_restore_before_rel_gpio_hold()
{ // the gpio regs restore here, will not be put into addr_list
}

uint32_t calc_and_set_sleep_dur();
void ll_conn_process_before_sleep();
void T1001_ChipSleepCriticalWork(void);
extern uint8_t is_vtimer_to;

#define MAX_GPIO_NR 24
extern uint32_t unused_gpio_mask_when_sleep;

void t1001_cfg_wakeup_gpio(uint32_t gpio_mask, uint8_t is_n_wakeup)
{
    // unused_gpio_mask_when_sleep &= ~gpio_mask;

    for (int i = 0; i < MAX_GPIO_NR; i++)
    {
        if ((gpio_mask >> i) & 0x1)
        {
            uint32_t func_val = is_n_wakeup ? 0x20 : 0x40;
            // TODO: if has remap, need to cfg remap regs
            omw_gpio_set_func(i, func_val);
        }
    }

    /* Setup the Wakeup Source */
    if (!is_n_wakeup)
    {
        AON_CTRL->WAKEUP_CTRL0 = gpio_mask;
    }
    else
    {
        AON_CTRL->WAKEUP_CTRL1 = gpio_mask;
    }
}

uint32_t val_0x40000024;

__RAM_CODE_SECTION
void otp_pwr_on()
{
    // xtal current
    REG_WRT_BITS(0x40080110, 14, 11, 8); // init or wake-up

#ifdef OMW_EN_WDG
    FEED_WTD(1);
#endif

    otp_init();

    *(uint32_t *)0x40000024 = val_0x40000024;
}

#ifdef OMW_RSTR_MORE_CR_REGS
__RAM_CODE_SECTION
static void restore_more_regs_when_not_wakeup_fully()
{
    for (int j = 0; j < sizeof(more_cr_regs) / 4; j++)
    {
        *(uint32_t *)more_cr_regs[j] = g_save_buf[j + T1001_SLEEP_MAX_ADDR_NR_EXT];
    }
}
#endif

extern uint8_t gpio_vdd_pin1;
extern uint8_t gpio_vdd_pin2;

void omw_sleep_init()
{
    unused_gpio_mask_when_sleep = OMW_WHEN_SLEEP_GPIO_MASK;

#ifdef CONFIG_OTP_PROGRAM
    otp_init_ptr = otp_pwr_on;
#endif

#ifdef CONFIG_FLASH_PROGRAM
    qspi_regs_restore_ptr = qspi_regs_restore;
#endif

#ifdef OMW_RSTR_MORE_CR_REGS
    restore_more_ext_cr_regs = restore_more_regs_when_not_wakeup_fully;
#endif

#ifdef OMW_FLASH_VDDIO_PIN1
    gpio_vdd_pin1 = OMW_FLASH_VDDIO_PIN1;
#endif
#ifdef OMW_FLASH_VDDIO_PIN2
    gpio_vdd_pin2 = OMW_FLASH_VDDIO_PIN2;
#endif

    gpio_regs_restore_before_rel_gpio_hold_ptr = gpio_regs_restore_before_rel_gpio_hold;
}
extern uint8_t app_can_sleep;
static void T1001_InternalSleep(SleepModes sleepMode, uint32_t gpioWakeBitMask, uint32_t gpionWakeBitMask)
{
    uint32_t reg_save_buf[T1001_SLEEP_MAX_ADDR_NR_EXT];

    val_0x40000024 = *(uint32_t *)0x40000024;
    g_save_buf = reg_save_buf;
    is_vtimer_to = 0;

    if (sleepMode == SLEEPMODE_NOTIMER)
    {
        if (!gpioWakeBitMask && !gpionWakeBitMask)
            return; // no wakeup gpio, can not sleep

        is_vtimer_to = 1;
        AON_CTRL->SLEEP_TIME = SYS_CTRL->AON_RTC + 0xFFFFFFFF;
    }
    else if (!calc_and_set_sleep_dur())
        return; // sleep time is not valid, can not sleep

#ifdef OMW_SAVE_UART0_REGS
    uint32_t uart0_save_buf_lc[sizeof(uart_addr_idx_list)];
    uart0_save_buf = uart0_save_buf_lc;
#endif

#ifdef OMW_SAVE_UART1_REGS
    uint32_t uart1_save_buf_lc[sizeof(uart_addr_idx_list)];
    uart1_save_buf = uart1_save_buf_lc;
#endif

    t1001_sleep_save_reg_info_ext();

    if (gpioWakeBitMask)
        t1001_cfg_wakeup_gpio(gpioWakeBitMask, 0);
    if (gpionWakeBitMask)
        t1001_cfg_wakeup_gpio(gpionWakeBitMask, 1);

    // Clear sleep flag.
    AON_CTRL->CPU_RST_CLR = 0x3ff;
    AON_CTRL->WAKEUP_CLEAR = 0x1000000;

    ll_conn_process_before_sleep();

    REG_WRT_BITS(0x40080110, 14, 11, 15); // xtal current, before sleep
	
    T1001_ChipSleepCriticalWork(); // sleep
	

    // gpio hold has been released
    t1001_sleep_restore_reg_info_ext(); // 

	app_can_sleep = 0;

    // save wakeup info, avoid error in avtive, must clear here.
    sleep_wakeup_reason = AON_CTRL->CPU_RST_RCD;

    // Clear sleep flag.
    AON_CTRL->CPU_RST_CLR = 0x3ff;

}

uint8_t bt_fast_chk_sleep();
void delay_us(uint32_t us);
void T1001_Sleep(SleepModes sleepMode,
                 uint32_t gpioWakeBitMask,
                 uint32_t gpionWakeBitMask)
{
    SleepModes bt_sleepMode, timer_sleepMode, sleepMode_allowed;

    sleep_wakeup_reason = 0;

    __disable_irq();

    //    if (!bt_fast_chk_sleep()) goto __NO_SLEEP;
    if (!App_SleepMode_Check())
        goto __NO_SLEEP;
    //    if ((bt_sleepMode = Bt_SleepMode_Check()) < SLEEPMODE_WAKETIMER) goto __NO_SLEEP;
    //    if ((timer_sleepMode = Timer_SleepMode_Check(sleepMode)) < SLEEPMODE_WAKETIMER) goto __NO_SLEEP;

    sleepMode_allowed = (SleepModes)MIN(bt_sleepMode, timer_sleepMode);
    sleepMode_allowed = SLEEPMODE_NOTIMER;

__RE_SLEEP:

    T1001_InternalSleep(sleepMode_allowed, gpioWakeBitMask, gpionWakeBitMask);

    if (sleepMode_allowed == SLEEPMODE_NOTIMER)
    {
        if (sleep_wakeup_reason)
        { // wakup
            if (AON_CTRL->WAKEUP_RECORD != 0)
            { // wakeup by gpio
                REG_SET_BIT(0x42000100, 4);
            }
            else
            { // wakeup by 0xFFFFFFFF time out
                sleep_wakeup_reason = 0;
                delay_us(1000); // for re-sleep issue
                goto __RE_SLEEP;
            }
        }
        // else no sleep
    }

__NO_SLEEP:
    __enable_irq();

    return;
}

uint16_t T1001_WakeupSource(void)
{
    return sleep_wakeup_reason;
}

uint32_t T1001_GpioWakeupSource(void)
{
    return AON_CTRL->WAKEUP_RECORD;
}

uint32_t omw_sleep_clr_wkup_rec()
{
    uint32_t w_rec = AON_CTRL->WAKEUP_RECORD;

    if (w_rec)
    {
        AON_CTRL->WAKEUP_CLEAR = w_rec;
        for (volatile int i = 0; i < 500; i++)
            ;
        AON_CTRL->WAKEUP_CLEAR = 0;
    }

    return w_rec;
}
#else
void flash_power_off(int p)
{
}

void qspi_regs_restore()
{
}

uint32_t ll_conn_is_connected_before_sleep()
{
    return 0;
}

uint32_t ll_adv_process_wake_up()
{
    return 0;
}

uint32_t ll_conn_process_wake_up()
{
    return 0;
}

void unused_gpio_mask_parse_and_set(unsigned int m)
{
}

uint32_t omw_sleep_clr_wkup_rec()
{
    return 0;
}

uint16_t T1001_WakeupSource(void)
{
    return 0;
}

__WEAK uint8_t bt_fast_chk_sleep()
{
    return 0;
}
#endif
