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

extern uint8_t g_sleep_change_flag ;					//zhq  note:
extern uint8_t g_sleep_timer_flag ;						//zhq  note:
extern uint8_t nosleep_when_wakeup ;			//zhq  note:


#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					//zhq  note: 
static uint32_t * uart0_save_buf;
#endif
#ifdef OMW_SAVE_UART1_REGS
static uint32_t * uart1_save_buf;
#endif

uint32_t sleep_wakeup_reason;

static uint32_t addr_list[] =
{
    /////////////////////////////////////////////// REGS///////////////////////////////////
    /* Device regs */
    #ifdef OMW_SAVE_UART0_REGS
    (uint32_t)&CLIC->CLICINT[UART0_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 */

    /* ... */
};

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

__WEAK SleepModes App_SleepMode_Check(SleepModes sleepMode)
{
    if (omw_app_can_sleep())  return SLEEPMODE_NOTIMER;
    else return SLEEPMODE_RUNNING;
}

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

// 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(0x40010300, QSPI_REMAP_REG_VAL);

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

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

    *(uint32_t *)0x40000024 = val_0x40000024;
}

extern uint8_t  gpio_vdd_pin1;
extern uint8_t  gpio_vdd_pin2;

static uint8_t 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;

    #ifdef CONFIG_OTP_PROGRAM
    otp_init_ptr = otp_pwr_on;
    #endif

    #ifdef CONFIG_FLASH_PROGRAM
    qspi_regs_restore_ptr = qspi_regs_restore;
    #endif

    gpio_regs_restore_before_rel_gpio_hold_ptr = gpio_regs_restore_before_rel_gpio_hold;

    g_save_buf = reg_save_buf;
    is_vtimer_to = 0;

	if (sleepMode == SLEEPMODE_NOTIMER)
	{
        #ifdef OMW_EN_FORCE_SLEEP
        if (gpioWakeBitMask || gpionWakeBitMask)
        {
            t1001_cfg_wakeup_gpio(gpioWakeBitMask, 0);
            t1001_cfg_wakeup_gpio(gpionWakeBitMask, 1);

            omw_sleep_set_force_sleep();
        }
        #endif

		return 0;
	}

    if (!calc_and_set_sleep_dur()) return 0;

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

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

    #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

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

    ll_conn_process_before_sleep();

    T1001_ChipSleepCriticalWork();

    __disable_irq();

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

    return 1;
}

uint8_t T1001_Sleep(SleepModes sleepMode,
                      uint32_t gpioWakeBitMask,
                      uint32_t gpionWakeBitMask)
{
    uint32_t WDT_CFG_saved;
    SleepModes app_sleepMode, bt_sleepMode, timer_sleepMode, sleepMode_allowed;

    sleep_wakeup_reason = 0;
    /* Mask all the interrupt */
    __disable_irq();

    bt_sleepMode = (SleepModes)Bt_SleepMode_Check();
    timer_sleepMode = Timer_SleepMode_Check(sleepMode);
    app_sleepMode = App_SleepMode_Check(sleepMode);
    sleepMode_allowed = (SleepModes)MIN(app_sleepMode, sleepMode);
    sleepMode_allowed = (SleepModes)MIN(timer_sleepMode, sleepMode_allowed);
    sleepMode_allowed = (SleepModes)MIN(bt_sleepMode, sleepMode_allowed);

    if (sleepMode_allowed == SLEEPMODE_RUNNING) {
        __enable_irq();
        return sleepMode_allowed;
    }

    if (sleepMode_allowed == SLEEPMODE_CPU_HALT) {
        goto sleep_cpu_halt;
    }

    unused_gpio_mask_when_sleep = OMW_WHEN_SLEEP_GPIO_MASK;

    if(!T1001_InternalSleep(sleepMode_allowed, gpioWakeBitMask, gpionWakeBitMask))
    {
        sleepMode_allowed = SLEEPMODE_CPU_HALT;
        goto sleep_cpu_halt;
    }
	
	g_sleep_change_flag = 1;					//zhq  note: 		
	nosleep_when_wakeup = 1;				//zhq  note: 	
	g_sleep_timer_flag	=1;						//zhq  note: 						

	
    __enable_irq();

    return sleepMode_allowed;

sleep_cpu_halt:

    /* Store the watchdog configuration and the disable it to avoid reset during CPU halt. */
    WDT_CFG_saved = SYS_CTRL->WDT_CFG;
    SYS_CTRL->WDT_CFG = 0;

    __WFI();

    /* Restore the watchdog functionality. */
    SYS_CTRL->WDT_CFG = WDT_CFG_saved;

    __enable_irq();

    return sleepMode_allowed;
}

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

}

#endif
