#include "pm/pm.h"
#include "driver/chip/hal_wakeup.h"
#include "driver/chip/hal_gpio.h"
#include "driver/chip/hal_crypto.h"
#include "kernel/os/os_time.h"

#include "tkl_sleep.h"
#include "tkl_wakeup.h"

#define EVENT_BIT(pos)                        (1U << (pos))

/**
 * @brief wake up source set
 * 
 * @param[in] param: wake up source set,
 * 
 * @return OPRT_OK on success. Others on error, please refer to tuya_error_code.h
 */
OPERATE_RET tkl_wakeup_source_set(CONST TUYA_WAKEUP_SOURCE_BASE_CFG_T  *param)
{
	TUYA_GPIO_NUM_E gpio_num;
	uint8_t wakeup_io;
	GPIO_InitParam init_param;
	uint8_t pull_type;

	if (NULL == param) {
		return OPRT_OS_ADAPTER_INVALID_PARM;
	}

	if (   (TUYA_WAKEUP_SOURCE_TIMER != param->source)
		&& (TUYA_WAKEUP_SOURCE_GPIO != param->source) ) {
		return OPRT_OS_ADAPTER_INVALID_PARM;
	}
	
	if (TUYA_WAKEUP_SOURCE_TIMER == param->source) {
		HAL_Wakeup_SetTimer_mS(param->wakeup_para.timer_param.ms);
		pm_enter_mode(PM_MODE_HIBERNATION);
	} else {

		gpio_num = param->wakeup_para.gpio_param.gpio_num;
		if((gpio_num > TUYA_GPIO_NUM_23) || (gpio_num < TUYA_GPIO_NUM_10)) {
			return OPRT_OS_ADAPTER_INVALID_PARM;
		} 

		wakeup_io = gpio_num - 10; /* X806 offset 10 for wakeup IO */
		memset(&init_param, 0, sizeof(init_param));
		init_param.driving = GPIO_DRIVING_LEVEL_0;
		init_param.mode = GPIOx_Pn_F6_EINT;

		if(param->wakeup_para.gpio_param.level == TUYA_GPIO_LEVEL_LOW) {
			init_param.pull = GPIO_PULL_UP;
			pull_type = WKUPIO_WK_MODE_FALLING_EDGE;
		} else if(param->wakeup_para.gpio_param.level == TUYA_GPIO_LEVEL_HIGH) {
			init_param.pull = GPIO_PULL_DOWN;
			pull_type = WKUPIO_WK_MODE_RISING_EDGE;
		} else {
			return OPRT_OS_ADAPTER_NOT_SUPPORTED;
		}

		HAL_GPIO_Init(GPIO_PORT_A, gpio_num, &init_param);
		HAL_Wakeup_SetIO(wakeup_io, pull_type, init_param.pull);

		HAL_PRCM_SetWakeupDebClk0(0);
		HAL_PRCM_SetWakeupIOxDebSrc(wakeup_io, 0);
		HAL_PRCM_SetWakeupIOxDebounce(wakeup_io, 1);
		
		HAL_Wakeup_SetIO(wakeup_io, pull_type, init_param.pull);
	}

	return OPRT_OK;
}

/**
 * @brief wake up source clear
 * 
 * @param[in] param:  wake up source clear,
 * 
 * @return OPRT_OK on success. Others on error, please refer to tuya_error_code.h
 */
OPERATE_RET tkl_wakeup_source_clear(CONST TUYA_WAKEUP_SOURCE_BASE_CFG_T *param)
{
	TUYA_GPIO_NUM_E gpio_num;
	uint8_t wakeup_io;

	if(NULL == param) {
		return OPRT_OS_ADAPTER_INVALID_PARM;
	}

	if (TUYA_WAKEUP_SOURCE_GPIO != param->source) {
		return OPRT_OS_ADAPTER_NOT_SUPPORTED;
	}

	gpio_num = param->wakeup_para.gpio_param.gpio_num;

	if((gpio_num > TUYA_GPIO_NUM_23) || (gpio_num < TUYA_GPIO_NUM_10)) {
        return OPRT_OS_ADAPTER_NOT_SUPPORTED;
	}
	
	wakeup_io = gpio_num - 10; /* X806 offset 10 for wakeup IO */

	HAL_PRCM_SetWakeupDebClk0(0);
	HAL_PRCM_SetWakeupIOxDebSrc(wakeup_io, 0);
	HAL_PRCM_SetWakeupIOxDebounce(wakeup_io, 0);

	HAL_GPIO_DisableIRQ(GPIO_PORT_A, gpio_num);
	HAL_GPIO_DeInit(GPIO_PORT_A, gpio_num);
	return OPRT_OK;
}

/**
 * @brief wake up source get
 * 
 * @param[in] param:  wake up source get,
 * 
 * @return OPRT_OK on success. Others on error, please refer to tuya_error_code.h
 */
OPERATE_RET tkl_wakeup_source_type_get(TUYA_WAKEUP_SOURCE_BASE_CFG_T *param)
{
	int i;
	uint32_t event;

	if (NULL == param) {
		return OPRT_INVALID_PARM;
	}

	event = HAL_Wakeup_GetEvent();
	if (event & PM_WAKEUP_SRC_WKTIMER) {
		param->source = TUYA_WAKEUP_SOURCE_TIMER;
	} else {
		for (i = 0; i < 14; i++) {
			if (event & EVENT_BIT(i)) {
				param->source = TUYA_WAKEUP_SOURCE_GPIO;
				param->wakeup_para.gpio_param.gpio_num = i + 10;
				break;
			}
		} 
	}

	return OPRT_OK;
}
