#include "include.h"
#include "driver_lowpwr.h"
#include "driver_gpio.h"
#include "ble_user_service.h"
#include "bsp_io_key.h"

#if SLEEP_SFR_SAVE
/*
    Users save the registers that need to be backed up.
    Registers that are not backed up will lose their values after they enter deep sleep
*/
#define LOWPWR_SFR_USER_SAVE

#ifdef LOWPWR_SFR_USER_SAVE
typedef struct {
    u32 funcomcon[6];

    u32 uart0baud;
    u16 uart0con;
} lowpwr_sfr_user_backup_t;

lowpwr_sfr_user_backup_t sfr_user_backup;

AT(.com_sleep.lv2)
void lowpwr_sleep_sfr_user_save(void)
{
    sfr_user_backup.funcomcon[0] = FUNCOMCON0;
	sfr_user_backup.funcomcon[1] = FUNCOMCON1;
	sfr_user_backup.funcomcon[2] = FUNCOMCON4;
	sfr_user_backup.funcomcon[3] = FUNCOMCON5;
	sfr_user_backup.funcomcon[4] = FUNCOMCON6;
	sfr_user_backup.funcomcon[5] = FUNCIMCON0;

	sfr_user_backup.uart0baud = UART0BAUD;
	sfr_user_backup.uart0con = UART0CON;
}

AT(.com_sleep.lv2)
void lowpwr_sleep_sfr_user_resume(void)
{
    FUNCOMCON0 = sfr_user_backup.funcomcon[0];
    FUNCOMCON1 = sfr_user_backup.funcomcon[1];
	FUNCOMCON4 = sfr_user_backup.funcomcon[2];
	FUNCOMCON5 = sfr_user_backup.funcomcon[3];
	FUNCOMCON6 = sfr_user_backup.funcomcon[4];
    FUNCIMCON0 = sfr_user_backup.funcomcon[5];

    UART0BAUD = sfr_user_backup.uart0baud;
    UART0CON = sfr_user_backup.uart0con;
    UART0CPND = BIT(17) | BIT(16);                  //CLR Pending
}
#endif
#endif

AT(.com_text.sleep_tick)
void lowpwr_tout_ticks(void)
{
#if SYS_SLEEP_LEVEL
    if(sys_cb.sleep_delay != -1L && sys_cb.sleep_delay > 0) {
        sys_cb.sleep_delay--;
    }
#endif

#if SYS_OFF_TIME
    if(sys_cb.pwroff_delay != -1L && sys_cb.pwroff_delay > 0) {
        sys_cb.pwroff_delay--;
    }
#endif
}

#if SYS_SLEEP_LEVEL

AT(.com_sleep.sleep)
void sys_sleep_cb(u8 lpclk_type)
{
    //注意！！！！！！！！！！！！！！！！！
    //此函数只能调用com_sleep或com_text函数

    //此处关掉影响功耗的模块

    lowpwr_sleep_proc(lpclk_type);                  //enter sleep

    //唤醒后，恢复模块功能
}

void sleep_pwrdown_timer_cb(uint32_t handle)
{
    #if 1/* TRACE_EN */
    GPIO_PORT_GET(BSP_UART_DEBUG_EN)->de |= GPIO_PIN_GET(BSP_UART_DEBUG_EN);
    printf("sleep pwrdown timeout\n");
    #endif
    func_pwroff();
}

void lowpwr_sleep_wakeup_config(void)
{
    lowpwr_wakeup_typedef config;

    lowpwr_wakeup_disable();

    config.source = WK_LP_WK0 | WK_LP_BT;
#if BSP_CHARGE_EN || FUNC_USBD_EN
    config.source |= WK_LP_VUSB;
#endif
#if BSP_IOKEY_EN
    config.source |= WK_LP_GPIO;
    config.gpiox = NULL;
    config.gpio_pin = 0;
#endif
    config.edge = WK_EDGE_FALLING;
    config.lp_type = LP_TYPE_SLEEP;
    lowpwr_wakeup_config_2(&config);

#if BSP_IOKEY_EN
    for (u8 i = 0; i < IO_KEY_COL_TABLE_SIZE; i++) {
        config.gpiox = key_io_table_column[i].gpiox;
        config.gpio_pin = key_io_table_column[i].gpio_pin;
        config.gpio_pupd = GPIO_PUPD_PU10K; // same with init in bsp_io_key.c
        lowpwr_gpio_wakeup_config(&config);
    }
#if IO_KEY_SCAN_MODE
    config.edge = WK_EDGE_RISING;
    for (u8 i = 0; i < IO_KEY_ROW_TABLE_SIZE; i++) {
        config.gpiox = key_io_table_row[i].gpiox;
        config.gpio_pin = key_io_table_row[i].gpio_pin;
        config.gpio_pupd = GPIO_PUPD_PD10K;
        lowpwr_gpio_wakeup_config(&config);
    }
#endif // IO_KEY_SCAN_MODE
#endif // BSP_IOKEY_EN
}

AT(.text.lowpwr.sleep)
static void sfunc_sleep(void)
{
    uint32_t sysclk;
    u8 pg_de;
    u16 pa_de, pb_de;

    printf("%s\n", __func__);

    ble_enter_sleep_proc();
    delay_5ms(1);
    bt_enter_sleep();

    sys_set_tmr_enable(0, 0);
    sysclk = sys_clk_get();
    sys_clk_set(SYS_24M);

    #if SYS_OFF_TIME
    bt_timer_mem_t sleep_pwrdown_timer;
    bt_timer_handle_t sleep_pwrdown_timer_handle;
    bt_alarm_timer_mem_extend(&sleep_pwrdown_timer, sizeof(sleep_pwrdown_timer));
    bt_alarm_timer_acquire(&sleep_pwrdown_timer_handle, 100 * sys_cb.pwroff_delay, 0, sleep_pwrdown_timer_cb);
    bt_alarm_timer_start(sleep_pwrdown_timer_handle);
    printf("wk and pwr after: %ds\n", 100 * sys_cb.pwroff_delay / 1000);
    #endif

    //io analog input
    pa_de = GPIOADE;
    pb_de = GPIOBDE;
    pg_de = GPIOGDE;
    GPIOADE = 0;
    GPIOBDE = 0;
    GPIOGDE = 0x3F; //MCP FLASH

    lowpwr_wakeup_disable();
    lowpwr_sleep_wakeup_config();

    sys_cb.sleep_sta = 1;
    while (bt_is_sleep()) {
        WDT_CLR();

        bt_sleep_proc();

        #if SYS_OFF_TIME
        {
            if (func_cb.sta == FUNC_PWROFF) {
                break;
            }

            if (0/* Condition: reset pwr timer */) {
                bt_alarm_timer_modify_interval(sleep_pwrdown_timer_handle, 6666, 0);
            }
        }
        #endif

        if (lowpwr_is_wakeup_pending(WK_LP_BT)) {
            break;
        }

#if BSP_IOKEY_EN
        if(gpio_is_edge_pending()){
            break;
        }
#endif

        if(ble_proc_pending()){
            break;
        }
    }

    sys_cb.sleep_sta = 0;

    #if SYS_OFF_TIME
    bt_alarm_timer_mem_free(&sleep_pwrdown_timer, sizeof(sleep_pwrdown_timer));
    #endif

    sys_clk_set(sysclk);

    //Reinitialize all peripherals, because all register configurations are lost after low power
    bsp_periph_init();

    printf("wakeup\n");

    GPIOADE = pa_de;
    GPIOBDE = pb_de;
    GPIOGDE = pg_de;

    lowpwr_wakeup_disable();

    sys_set_tmr_enable(1, 1);

    bt_exit_sleep();
    ble_exit_sleep_proc();
    printf("sleep_exit\n");
}

AT(.text.app.proc.sleep)
bool sleep_process(is_sleep_func is_sleep)
{
    if (is_sleep()) {
        if (sys_cb.sleep_delay == -1L) {
            return false;
        }
        if(sys_cb.sleep_delay == 0) {
            sfunc_sleep();
            lowpwr_sleep_delay_reset();
            lowpwr_pwroff_delay_reset();
            return true;
        }
    } else {
        lowpwr_sleep_delay_reset();
    }
    return false;
}
#endif

void lowpwr_pwroff_wakeup_config(void)
{
    lowpwr_wakeup_typedef config;

    lowpwr_wakeup_disable();

    config.source = WK_LP_WK0;
#if BSP_CHARGE_EN || FUNC_USBD_EN
    config.source |= WK_LP_VUSB;
#endif
#if BSP_IOKEY_EN
    config.source |= WK_LP_GPIO;
    config.gpiox = NULL;
    config.gpio_pin = 0;
#endif
    config.edge = WK_EDGE_FALLING;
    config.lp_type = LP_TYPE_POWOFF;

    lowpwr_wakeup_config_2(&config);

#if BSP_IOKEY_EN
    for (u8 i = 0; i < IO_KEY_COL_TABLE_SIZE; i++) {
        config.gpiox = key_io_table_column[i].gpiox;
        config.gpio_pin = key_io_table_column[i].gpio_pin;
        config.gpio_pupd = GPIO_PUPD_PU10K; // same with init in bsp_io_key.c
        lowpwr_gpio_wakeup_config(&config);
    }
#if IO_KEY_SCAN_MODE
    config.edge = WK_EDGE_RISING;
    for (u8 i = 0; i < IO_KEY_ROW_TABLE_SIZE; i++) {
        config.gpiox = key_io_table_row[i].gpiox;
        config.gpio_pin = key_io_table_row[i].gpio_pin;
        config.gpio_pupd = GPIO_PUPD_PD10K;
        lowpwr_gpio_wakeup_config(&config);
    }
#endif // IO_KEY_SCAN_MODE
#endif // BSP_IOKEY_EN
}


void sfunc_pwroff(void)
{
    GPIOADE = 0;
    GPIOBDE = 0;

    lowpwr_pwroff_wakeup_config();

    pwroff_do();
}

AT(.text.lowpwr.pwroff)
void func_pwroff(void)
{
    printf("%s\n", __func__);

    if (SOFT_POWER_ON_OFF) {
        sfunc_pwroff();
    }
}
