#include "device/key_driver.h"
#include "system/event.h"
#include "system/init.h"
#include "iokey.h"
#include "adkey.h"
#include "slidekey.h"
#include "irkey.h"
#include "touch_key.h"
#include "system/timer.h"
#include "asm/power_interface.h"
#include "app_config.h"
#include "rdec_key.h"
#if TCFG_KEY_TONE_EN
#include "tone_player.h"
#endif

#if(TCFG_IRSENSOR_ENABLE == 1)
#include "irSensor/ir_manage.h"
#endif

#define LOG_TAG_CONST       KEY
#define LOG_TAG             "[KEY]"
#define LOG_ERROR_ENABLE
#define LOG_DEBUG_ENABLE
#define LOG_INFO_ENABLE
//#define LOG_DUMP_ENABLE
//#define LOG_CLI_ENABLE
#include "debug.h"

#define KEY_EVENT_CLICK_ONLY_SUPPORT	1 	//是否支持某些按键只响应单击事件


#if TCFG_SPI_LCD_ENABLE
#ifndef ALL_KEY_EVENT_CLICK_ONLY
#define ALL_KEY_EVENT_CLICK_ONLY	1 	//是否全部按键只响应单击事件
#endif
#else
#ifndef ALL_KEY_EVENT_CLICK_ONLY
#define ALL_KEY_EVENT_CLICK_ONLY	0 	//是否全部按键只响应单击事件
#endif
#endif

static volatile u8 is_key_active = 0;


extern u32 timer_get_ms(void);

//=======================================================//
// 按键值重新映射函数:
// 用户可以实现该函数把一些按键值重新映射, 可用于组合键的键值重新映射
//=======================================================//
int __attribute__((weak)) key_event_remap(struct sys_event *e)
{
    return true;
}

//=======================================================//
// 按键扫描函数: 扫描所有注册的按键驱动
//=======================================================//
static void key_driver_scan(void *_scan_para)
{
    struct key_driver_para *scan_para = (struct key_driver_para *)_scan_para;

    u8 key_event = 0;
    u8 cur_key_value = NO_KEY;
    u8 key_value = 0;
    struct sys_event e;
    static u8 poweron_cnt = 0;

    //为了滤掉adkey与mic连在一起时电容充放电导致的开机按键误判,一般用于type-c耳机
    /* if (poweron_cnt <= 250) { */
    /*     poweron_cnt++; */
    /*     return; */
    /* } */

    cur_key_value = scan_para->get_value();
    /* if (cur_key_value != NO_KEY) { */
    /*     printf(">>>cur_key_value: %d\n", cur_key_value); */
    /* } */

    if (cur_key_value != NO_KEY) {
        is_key_active = 35;      //35*10Ms
    } else if (is_key_active) {
        is_key_active --;
    }
//===== 按键消抖处理
    if (cur_key_value != scan_para->filter_value && scan_para->filter_time) {	//当前按键值与上一次按键值如果不相等, 重新消抖处理, 注意filter_time != 0;
        scan_para->filter_cnt = 0; 		//消抖次数清0, 重新开始消抖
        scan_para->filter_value = cur_key_value;	//记录上一次的按键值
        return; 		//第一次检测, 返回不做处理
    } 		//当前按键值与上一次按键值相等, filter_cnt开始累加;
    if (scan_para->filter_cnt < scan_para->filter_time) {
        scan_para->filter_cnt++;
        return;
    }
    //为了滤掉adkey与mic连在一起时电容充放电导致的按键误判,一般用于type-c耳机
    /* if ((cur_key_value != scan_para->last_key) && (scan_para->last_key != NO_KEY) && (cur_key_value != NO_KEY)) { */
    /*     return; */
    /* } */
//===== 按键消抖结束, 开始判断按键类型(单击, 双击, 长按, 多击, HOLD, (长按/HOLD)抬起)
    if (cur_key_value != scan_para->last_key) {
        if (cur_key_value == NO_KEY) {  //cur_key = NO_KEY; last_key = valid_key -> 按键被抬起

#if MOUSE_KEY_SCAN_MODE
            /* if (scan_para->press_cnt >= scan_para->long_time) {  //长按/HOLD状态之后被按键抬起; */
            key_event = KEY_EVENT_UP;
            key_value = scan_para->last_key;
            goto _notify;  	//发送抬起消息
            /* } */
#else
            if (scan_para->press_cnt >= scan_para->long_time) {  //长按/HOLD状态之后被按键抬起;
                key_event = KEY_EVENT_UP;
                key_value = scan_para->last_key;
                goto _notify;  	//发送抬起消息
            }
#endif

            scan_para->click_delay_cnt = 1;  //按键等待下次连击延时开始
        } else {  //cur_key = valid_key, last_key = NO_KEY -> 按键被按下
            scan_para->press_cnt = 1;  //用于判断long和hold事件的计数器重新开始计时;
            if (cur_key_value != scan_para->notify_value) {  //第一次单击/连击时按下的是不同按键, 单击次数重新开始计数
                scan_para->click_cnt = 1;
                scan_para->notify_value = cur_key_value;
            } else {
                scan_para->click_cnt++;  //单击次数累加
            }
        }
        goto _scan_end;  //返回, 等待延时时间到
    } else { 	//cur_key = last_key -> 没有按键按下/按键长按(HOLD)
        if (cur_key_value == NO_KEY) {  //last_key = NO_KEY; cur_key = NO_KEY -> 没有按键按下
            if (scan_para->click_cnt > 0) {  //有按键需要消息需要处理

#if ALL_KEY_EVENT_CLICK_ONLY//彩屏方案支持单击
                key_event = KEY_EVENT_CLICK;  //单击
                key_value = scan_para->notify_value;
                goto _notify;

#endif

#if KEY_EVENT_CLICK_ONLY_SUPPORT 	//是否支持某些按键只响应单击事件
                if (scan_para->notify_value & BIT(7)) {  //BIT(7)按键特殊处理标志, 只发送单击事件, 也可以用于其它扩展
                    key_event = KEY_EVENT_CLICK;  //单击
                    key_value = scan_para->notify_value;
                    goto _notify;
                }
#endif
                if (scan_para->click_delay_cnt > scan_para->click_delay_time) { //按键被抬起后延时到
                    //TODO: 在此可以添加任意多击事件
                    if (scan_para->click_cnt >= 5) {
                        key_event = KEY_EVENT_FIRTH_CLICK;  //五击
                    } else if (scan_para->click_cnt >= 4) {
                        key_event = KEY_EVENT_FOURTH_CLICK;  //4击
                    } else if (scan_para->click_cnt >= 3) {
                        key_event = KEY_EVENT_TRIPLE_CLICK;  //三击
                    } else if (scan_para->click_cnt >= 2) {
                        key_event = KEY_EVENT_DOUBLE_CLICK;  //双击
                    } else {
                        key_event = KEY_EVENT_CLICK;  //单击
                    }
                    key_value = scan_para->notify_value;
                    goto _notify;
                } else {	//按键抬起后等待下次延时时间未到
                    scan_para->click_delay_cnt++;
                    goto _scan_end; //按键抬起后延时时间未到, 返回
                }
            } else {
                goto _scan_end;  //没有按键需要处理
            }
        } else {  //last_key = valid_key; cur_key = valid_key, press_cnt累加用于判断long和hold
            scan_para->press_cnt++;
            if (scan_para->press_cnt == scan_para->long_time) {
                key_event = KEY_EVENT_LONG;
            } else if (scan_para->press_cnt == scan_para->hold_time) {
                key_event = KEY_EVENT_HOLD;
                scan_para->press_cnt = scan_para->long_time;
            } else {
                goto _scan_end;  //press_cnt没到长按和HOLD次数, 返回
            }
            //press_cnt没到长按和HOLD次数, 发消息
            key_value = cur_key_value;
            goto _notify;
        }
    }

_notify:
#if TCFG_IRSENSOR_ENABLE
    if (get_irSensor_event() == IR_SENSOR_EVENT_FAR) {  //未佩戴的耳机不响应按键
        return;
    }
#endif
    key_value &= ~BIT(7);  //BIT(7) 用作按键特殊处理的标志
    e.type = SYS_KEY_EVENT;
    e.u.key.init = 1;
    e.u.key.type = scan_para->key_type;//区分按键类型
    e.u.key.event = key_event;
    e.u.key.value = key_value;
    e.u.key.tmr = timer_get_ms();


    scan_para->click_cnt = 0;  //单击次数清0
    scan_para->notify_value = NO_KEY;

    e.arg  = (void *)DEVICE_EVENT_FROM_KEY;
    /* printf("key_value: 0x%x, event: %d\n", key_value, key_event); */
    if (key_event_remap(&e)) {
        sys_event_notify(&e);
#if TCFG_KEY_TONE_EN
        audio_key_tone_play();
#endif
    }
_scan_end:
    scan_para->last_key = cur_key_value;
    return;
}


//wakeup callback
void key_active_set(u8 port)
{
    is_key_active = 35;      //35*10Ms
}

//=======================================================//
// 按键初始化函数: 初始化所有注册的按键驱动
//=======================================================//
int key_driver_init(void)
{
    int err;

#if TCFG_IOKEY_ENABLE
    extern const struct iokey_platform_data iokey_data;
    extern struct key_driver_para iokey_scan_para;
    err = iokey_init(&iokey_data);
#ifdef TCFG_IOKEY_TIME_REDEFINE
    extern struct key_driver_para iokey_scan_user_para;
    if (err == 0) {
        sys_s_hi_timer_add((void *)&iokey_scan_user_para, key_driver_scan, iokey_scan_user_para.scan_time); //注册按键扫描定时器
    }
#else
    if (err == 0) {
        sys_s_hi_timer_add((void *)&iokey_scan_para, key_driver_scan, iokey_scan_para.scan_time); //注册按键扫描定时器
    }
#endif
#endif

#if TCFG_ADKEY_ENABLE
    extern const struct adkey_platform_data adkey_data;
    extern struct key_driver_para adkey_scan_para;
    err = adkey_init(&adkey_data);
    if (err == 0) {

        sys_s_hi_timer_add((void *)&adkey_scan_para, key_driver_scan, adkey_scan_para.scan_time); //注册按键扫描定时器
    }
#endif

#if TCFG_IRKEY_ENABLE
    extern const struct irkey_platform_data irkey_data;
    extern struct key_driver_para irkey_scan_para;
    err = irkey_init(&irkey_data);
    if (err == 0) {
        sys_s_hi_timer_add((void *)&irkey_scan_para, key_driver_scan, irkey_scan_para.scan_time); //注册按键扫描定时器
    }
#endif

#if TCFG_TOUCH_KEY_ENABLE
    extern const struct touch_key_platform_data touch_key_data;
    extern struct key_driver_para touch_key_scan_para;
    err = touch_key_init(&touch_key_data);
    if (err == 0) {
        sys_s_hi_timer_add((void *)&touch_key_scan_para, key_driver_scan, touch_key_scan_para.scan_time); //注册按键扫描定时器
    }
#endif

#if TCFG_ADKEY_RTCVDD_ENABLE
    extern const struct adkey_rtcvdd_platform_data adkey_rtcvdd_data;
    extern struct key_driver_para adkey_rtcvdd_scan_para;
    err = adkey_rtcvdd_init(&adkey_rtcvdd_data);
    if (err == 0) {
        sys_s_hi_timer_add((void *)&adkey_rtcvdd_scan_para, key_driver_scan, adkey_rtcvdd_scan_para.scan_time); //注册按键扫描定时器
    }
#endif

#if TCFG_RDEC_KEY_ENABLE
    extern const struct rdec_platform_data rdec_key_data;
    extern struct key_driver_para rdec_key_scan_para;
    err = rdec_key_init(&rdec_key_data);
    if (err == 0) {
        sys_s_hi_timer_add((void *)&rdec_key_scan_para, key_driver_scan, rdec_key_scan_para.scan_time); //注册按键扫描定时器
    }
#endif

#if TCFG_CTMU_TOUCH_KEY_ENABLE
#include "asm/ctmu.h"
    extern const struct ctmu_touch_key_platform_data ctmu_touch_key_data;
    extern struct key_driver_para ctmu_touch_key_scan_para;
    extern int ctmu_touch_key_init(const struct ctmu_touch_key_platform_data * ctmu_touch_key_data);
    err = ctmu_touch_key_init(&ctmu_touch_key_data);
    if (err == 0) {
        sys_s_hi_timer_add((void *)&ctmu_touch_key_scan_para, key_driver_scan, ctmu_touch_key_scan_para.scan_time); //注册按键扫描定时器
    }
#endif /* #if TCFG_CTMU_TOUCH_KEY_ENABLE */

#if TCFG_SLIDE_KEY_ENABLE
    extern const struct slidekey_platform_data slidekey_data;
    extern int slidekey_init(const struct slidekey_platform_data * slidekey_data);
    err = slidekey_init(&slidekey_data);
    if (err == 0) {
    }
#endif//TCFG_SLIDE_KEY_ENABLE

#if TCFG_6083_ADKEY_ENABLE
    extern int adkey_init_6083();
    err = adkey_init_6083();
    if (err == 0) {
    }
#endif
    return 0;
}

static u8 key_idle_query(void)
{
    return !is_key_active;
}
#if !TCFG_LP_TOUCH_KEY_ENABLE
REGISTER_LP_TARGET(key_lp_target) = {
    .name = "key",
    .is_idle = key_idle_query,
};
#endif /* #if !TCFG_LP_TOUCH_KEY_ENABLE */

