#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"

#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 */

    /* ... */
};

#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)

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]];
    }
}

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
}

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];
    }
}

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
	#ifndef XS_SOP16
	REG_WRT(0x40010208, QSPI_FLASH_FUNC_REG1_VAL);
    REG_WRT(0x4001020C, QSPI_FLASH_FUNC_REG2_VAL);
	#else
    REG_WRT(0x40010204, QSPI_FLASH_FUNC_REG1_VAL);
    REG_WRT(0x40010208, QSPI_FLASH_FUNC_REG2_VAL);
	REG_WRT(0x40010300, QSPI_REMAP_REG_VAL);
	#endif
    #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;
    }
}

__RAM_CODE_SECTION
void omw_dpslp_bf_slp()
{

}

__RAM_CODE_SECTION
void omw_dpslp_af_slp()
{
    #ifdef OMW_EN_WDG
    FEED_WTD(1);
    #endif
}
uint32_t val_0x40000024;

__RAM_CODE_SECTION
void otp_pwr_on()
{
    otp_init();

    REG_WRT(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;
}

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();


    T1001_ChipSleepCriticalWork();

    //gpio hold has been released
    t1001_sleep_restore_reg_info_ext();

    // 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);

__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;
}

#ifdef OMW_CPU_E902
__WEAK void omw_dpslp_bf_slp()
{

}

__WEAK void omw_dpslp_af_slp()
{

}
#endif
#endif
