/**
******************************************************************************************************************************************************************************************
*@file                       dm_button_gpio.c
*@author         
*@data                       
*@version                    v1.0
*@par Copyright(c):
*
*@par History:
*     version:author,date,desc
*     v1.0   :create this file
*
*******************************************************************************************************************************************************************************************
*/
#include "sdk_include.h"
#define DM_BTN_BUILD_ID(row, col) ((dm_btn_id_t)row | ((dm_btn_id_t)col << 8))
#define DM_BTN_MAX_IO_NUM         (32)
#define DM_BTN_MAX_MULTIPLE       (6)

enum
{
    DM_BTN_STATE_IDLE,
	DM_BTN_STATE_WORK,
	DM_BTN_STATE_PRESS,
	DM_BTN_STATE_RELEASE,
};
enum
{
    DM_BTN_IDLE,
	DM_BTN_PRESSED,
	DM_BTN_RELEASED,
};

typedef struct
{
    dm_btn_id_t     btn_id;
	uint8_t         scan_times;
	uint8_t         scan_valid_times;
    uint8_t         btn_status;
	bool            action;
}dm_btn_debounce_t;

typedef struct
{
    dm_gpio_btn_conf         	btn_param;
	dm_gpio_btn_cbk          	button_cbk;
	sdk_event_cbk       	dm_connect_event_cbk;
    volatile uint8_t      		state;
} dm_gpio_btn_env_t;

static dm_gpio_btn_env_t dm_gpio_btn_env;
static dm_btn_debounce_t dm_btn_debounce[DM_BTN_MAX_MULTIPLE];
static uint32_t  last_iomap[DM_BTN_MAX_IO_NUM];


/**
*  brief: search same btm_id button
*
*  parameter: button id
*  return   : if find the same btn_id button,return the array postion, or NULL
**/
static dm_btn_debounce_t* _dm_btn_debounce_find(dm_btn_id_t btn_id)
{
    uint32_t i = 0;
    for(i = 0; i< DM_BTN_MAX_MULTIPLE; i++)
    {
         if(dm_btn_debounce[i].btn_id == btn_id)
            return (&(dm_btn_debounce[i]));
    }
    return NULL;
}

/**
*  brief:  find a empty position in the debounce array
*  
*  return: NULL,can,t find a empty position(id=0 if empty), or return positon
**/
static dm_btn_debounce_t* _dm_btn_debounce_find_position()
{
    uint32_t i = 0;
    for(i = 0; i < DM_BTN_MAX_MULTIPLE; i++)
    {
        if(dm_btn_debounce[i].btn_id == 0)
            return (&(dm_btn_debounce[i]));
    }
    return NULL;
}

/**
* brief: add a button in debounce array
*
* parameter[0]: button id number
* parameter[1]: button  level stataus, ture is high, false is low.
* 
*/
static bool _dm_btn_debounce_add(dm_btn_id_t btn_id, bool action)
{
    dm_btn_debounce_t* deb;
    //dbg_printf("btn_id=0x%x,0x%x\r\n",btn_id,action);
    ///< return NULL, no find. or find value pointer.
    deb = _dm_btn_debounce_find(btn_id);
    if(deb != NULL)
    {
        //< if this button already report, status is pressed. action is release. again into debounce.
        if((deb->btn_status == DM_BTN_PRESSED) && (action == false))
        {
            deb->btn_status       = DM_BTN_RELEASED;
            deb->action           = false;
            deb->scan_times       = 0;
            deb->scan_valid_times = 0;
        }
        //< if status if DM_BTN_IDLE, times++.
        if((deb->btn_status == DM_BTN_IDLE) && (action == true))
        {
            deb->scan_valid_times++;
            deb->action = action;
        }

        //< don't care released debounce
        if((deb->btn_status == DM_BTN_RELEASED) && (action == true))
        {
            deb->btn_status       = DM_BTN_PRESSED;
            deb->action           = true;
            deb->scan_times       = 0;
            deb->scan_valid_times = 0;
        }
        return true;
    }
    
    //< find empty position insert this btn_id.
    deb = _dm_btn_debounce_find_position();
    if(deb == NULL)
        return false;
    deb->btn_id           = btn_id;
    deb->action           = action;
    deb->btn_status       = DM_BTN_IDLE;
    deb->scan_times       = 0;
    deb->scan_valid_times = 1;
    return TRUE;
}

/**
* brief: 
*
* return: true ,judge debounce array if empty, or no empty
*/
static bool _dm_btn_debounce_empty()
{
    uint32_t deb_index = 0;
    for(deb_index = 0; deb_index < DM_BTN_MAX_MULTIPLE; deb_index++)
    {
        if(dm_btn_debounce[deb_index].btn_id != 0)
            return false;
    }
    return true;
}

/**
*  brief: set gpio state before scanning.
*         row input, col output 1.
*/
static void _dm_gpio_into_default_state()
{
    user_gpio_dir_in(dm_gpio_btn_env.btn_param.interrupt_io_map, GPIO_Int_Type_RisingEdge);
    if(dm_gpio_btn_env.btn_param.col_io_map != 0)
        user_gpio_dir_out_hig(dm_gpio_btn_env.btn_param.col_io_map, dm_gpio_btn_env.btn_param.col_io_map);
}

static void dm_close_timer(UINT8 event,void *param)
{
     dbg_printf("BTN is empyt!\n\r");
    _dm_gpio_into_default_state();
	timer_enable_ctrl_cmd(TIMER2, TIMER_STOP);	// timer0 1 让位给pwm预留
    cpu_prevent_sleep_clear(TIMER2_ONGOING);
    dm_gpio_btn_env.state = DM_BTN_STATE_IDLE;
}

void debug_io(uint32_t val)
{
#if KB_IO_DEBUG_ENABLE

	uint32_t row = (val & 0xFF);
	uint32_t col = (val & 0xFF00) >> 8;
	char *lable[4]={"A","B","C","D"};
	dbg_printf("row GPIO%s_%d col GPIO%s_%d \n",lable[row/8],row%8,lable[col/8],col%8);
#endif
}

/**
*  brief: manage the debounce array. close the timer.
*         if the button debounce succeed, the button will send a message
*         to reporter callback function.
*/
uint16_t closet_btn_timer = 0;
static void _dm_btn_debounce_done()
{
    uint8_t btn_idx = 0;
    dm_gpio_btn_report  evt;

    for(btn_idx = 0; btn_idx < DM_BTN_MAX_MULTIPLE; btn_idx++)
    {
        if(dm_btn_debounce[btn_idx].btn_id == 0)
            continue;

        //dbg_printf("btn=0x%x, stat=0x%x, times=0x%x,valid=0x%x\n",dm_btn_debounce[btn_idx].btn_id,dm_btn_debounce[btn_idx].btn_status,dm_btn_debounce[btn_idx].scan_times,dm_btn_debounce[btn_idx].scan_valid_times);
        // btn_id != 0 state is idle, scan times increase.
        if(dm_btn_debounce[btn_idx].btn_status != DM_BTN_PRESSED)
            dm_btn_debounce[btn_idx].scan_times++;
        if(dm_btn_debounce[btn_idx].btn_status == DM_BTN_RELEASED)
            dm_btn_debounce[btn_idx].scan_valid_times++;
		
        if((dm_btn_debounce[btn_idx].scan_times >= dm_gpio_btn_env.btn_param.scan_times))
        {
            if(dm_btn_debounce[btn_idx].scan_valid_times >= dm_gpio_btn_env.btn_param.scan_valid_times)
            {
                // debounce finished, last action if true.the button is pressed.or relased.
                if(dm_btn_debounce[btn_idx].action == true && dm_btn_debounce[btn_idx].btn_status == DM_BTN_IDLE)
                {
					dm_btn_debounce[btn_idx].btn_status = DM_BTN_PRESSED;
					dm_btn_debounce[btn_idx].action     = false;

                    evt.btn_id     = (dm_btn_debounce[btn_idx]).btn_id;
                    evt.action = DM_BTN_ACT_PRESSED;
#if KEYVALUE_DBG_ENABLE					
                    dbg_printf("pressed btn 0x%04X ",evt.btn_id);
#endif					
                    if(dm_gpio_btn_env.button_cbk != NULL)
						dm_gpio_btn_env.button_cbk(&evt);
                }
                if(dm_btn_debounce[btn_idx].action == false && dm_btn_debounce[btn_idx].btn_status == DM_BTN_RELEASED)
                {
					/// report evt, clear reseased
                    evt.btn_id     = dm_btn_debounce[btn_idx].btn_id;
                    evt.action = DM_BTN_ACT_RELEASED;  
#if KEYVALUE_DBG_ENABLE					
                    dbg_printf("release btn 0x%04X ",evt.btn_id);
#endif					
                    if(dm_gpio_btn_env.button_cbk != NULL)
						dm_gpio_btn_env.button_cbk(&evt);
					
                    dm_btn_debounce[btn_idx].btn_id            = 0;
                    dm_btn_debounce[btn_idx].btn_status        = DM_BTN_IDLE;
                    dm_btn_debounce[btn_idx].scan_times        = 0;
                    dm_btn_debounce[btn_idx].scan_valid_times  = 0;
                    dm_btn_debounce[btn_idx].action            = false;
                }
            }
            else
            {
            	//dbg_printf("***********delete 0x%x***************\n",evt.btn_id);
                dm_btn_debounce[btn_idx].btn_id            = 0;
                dm_btn_debounce[btn_idx].btn_status        = DM_BTN_IDLE;
                dm_btn_debounce[btn_idx].scan_times        = 0;
                dm_btn_debounce[btn_idx].scan_valid_times  = 0;
                dm_btn_debounce[btn_idx].action            = false;
            }
        }
    }
    if(_dm_btn_debounce_empty())
    {
    	closet_btn_timer ++;
    }else{
    	closet_btn_timer = 0;
	}
	
	if(closet_btn_timer > 200){
		FsmEvent_ExternalFx(dm_close_timer,NULL);
	}
}


/*
*   @brief: exchange key value
*   0x01
*   0x02,0x03,
*   0x04,0x05,0x06,
*   0x08,0x09,0x0a,0x0c,
*   0x10,0x11,0x12,0x14,0x18,
*/

uint16_t get_key_value(uint16_t btn_id)
{
    uint8_t i = 0;
	const unsigned short key_map[15][2]={
	   { 0x0c12,0x01},
	   { 0x0b12,0x02},
	   { 0x0a12,0x03},
	   { 0x0912,0x04},
	   { 0x0812,0x05},
	   { 0x0c11,0x06},
	   { 0x0b11,0x07},
	   { 0x0a11,0x08},
	   { 0x0911,0x09},
	   { 0x0811,0x0a},
	   { 0x0c10,0x0b},
	   { 0x0b10,0x0c},
	   { 0x0a10,0x0d},
	   { 0x0910,0x0e},
	   { 0x0810,0x0f}
	};
	
	for(i=0;i<15;i++){
		if(key_map[i][0] == btn_id){
			return key_map[i][1]; 
		}
	}

	return 0;
}

void user_gpio_callback(uint8_t intter_num)
{
    if(dm_gpio_btn_env.state == DM_BTN_STATE_IDLE)
	{
	    timer_enable_ctrl_cmd(TIMER2, TIMER_START);
        cpu_prevent_sleep_set(TIMER2_ONGOING);
	}
	dm_gpio_btn_env.state = DM_BTN_STATE_WORK;
//	if(intter_num == GPIO_Int2)
//	{
//		timer_enable_ctrl_cmd(TIMER1,TIMER_START);
//		GPIO_SetInterruptSourceAndType(2,0,GPIO_Int_Type_Disable);
//		
//	}
}
extern char connect_ing;
#if KB_IO_DEBUG_ENABLE
	uint32_t debug_io_buf[32];
	uint32_t debug_io_found;
#endif
void keyboard_connect_event(SDK_EVENT event,uint32_t timer);

void gpio_timer_callback(timer_idx_t id)
{
    uint32_t row,btn_id;
    uint32_t col_index = 0;
    uint32_t row_index = 0;
#if KB_IO_DEBUG_ENABLE
	int i;
#endif

	
	static uint8_t conn_pressed_cnt = 0;
	if(dm_gpio_btn_env.btn_param.scan_io_map){
		
//		dbg_printf("chk con %x:: ",dm_gpio_btn_env.btn_param.scan_io_map);
		user_gpio_dir_in(dm_gpio_btn_env.btn_param.scan_io_map, GPIO_Int_Type_Disable);
		if(user_gpio_get_pins_value(dm_gpio_btn_env.btn_param.scan_io_map)){
			dbg_printf("conected :: ");
//			dbg_printf("*");

			if(conn_pressed_cnt < 100){
				conn_pressed_cnt ++;
				if(conn_pressed_cnt == 3){
//					dbg_printf("conected :: ");
					if(dm_gpio_btn_env.dm_connect_event_cbk){
						dm_gpio_btn_env.dm_connect_event_cbk(EVENT_RECONNECT,500);
					}
				}
			}
			return;	
		}else{
			conn_pressed_cnt = 0;
		}
	}

    if(dm_gpio_btn_env.btn_param.col_io_map != 0)
    {
        ///< all row set input
        user_gpio_dir_in(dm_gpio_btn_env.btn_param.interrupt_io_map, GPIO_Int_Type_Disable);
        for(col_index = 0; col_index < DM_BTN_MAX_IO_NUM; col_index++)
        {
// 测试过程中发现部件键盘io互斥，按下YUI三个键，第三个键是没反应的        
//			if(TEST_BIT(dm_gpio_btn_env.btn_param.col_io_map,col_index))
            if(((0x00000001) << col_index) & dm_gpio_btn_env.btn_param.col_io_map)
			{
                user_gpio_dir_out_hig(dm_gpio_btn_env.btn_param.col_io_map, ((0x00000001) << col_index));
                row = user_gpio_get_pins_value(dm_gpio_btn_env.btn_param.row_io_map);
                for(row_index = 0; row_index < DM_BTN_MAX_IO_NUM; row_index++)
                {
                    btn_id = DM_BTN_BUILD_ID(row_index, col_index);
                    ///< if this bit last is 1, now is 0; action is relased.
                    if(((( 0x00000001) << row_index) & last_iomap[col_index]) && \
                       ((((0x00000001) << row_index) & row) != (0x00000001) << row_index))
                    {
//                        dbg_printf("1.0x%x,0x%x,0x%x\n",row_index,last_iomap[col_index],row);
                        _dm_btn_debounce_add(btn_id,FALSE);
#if KB_IO_DEBUG_ENABLE
						debug_io_found = 0;
						for(i=0;i<32;i++){
							if(debug_io_buf[i] == btn_id){
								debug_io_found = 1;
								debug_io_buf[i] = 0;
								dbg_printf("Release ::");
								debug_io(btn_id);
								dbg_printf("\n");
								break;
							}
						}
						if(debug_io_found == 0){
							dbg_printf("Error unknow key release ::\n");
						}
#endif						
                    }

                    ///< if this bit is 1, action is pressed.
                    if(((0x00000001) << row_index) & row)
                    {
                       //dbg_printf("1.0x%x,0x%x\r\n",row_index,last_iomap[col_index]);
                        _dm_btn_debounce_add(btn_id,TRUE);

#if KB_IO_DEBUG_ENABLE
						debug_io_found = 0;
						for(i=0;i<32;i++){
							if(debug_io_buf[i] == btn_id){
								debug_io_found = 1;
								break;
							}
						}
						if(debug_io_found == 0){
							for(i=0;i<32;i++){
								if(debug_io_buf[i] == 0){
									debug_io_buf[i] = btn_id;
									dbg_printf("Pressed :: row_val %x col %d row %x ",row,col_index,row_index);
									debug_io(btn_id);
									dbg_printf("\n");
									break;
								}
							}
						}
#endif						
                    }
                }
                ///< store this col's row value.
                if(row != last_iomap[col_index])
                    last_iomap[col_index] = row;
            }
        }
    }
    else
    {
        row = user_gpio_get_pins_value(dm_gpio_btn_env.btn_param.row_io_map);
		
        for(row_index = 0; row_index < DM_BTN_MAX_IO_NUM; row_index++)
        {
            if(((0x00000001) << row_index) & dm_gpio_btn_env.btn_param.row_io_map)
            {
                btn_id = DM_BTN_BUILD_ID(row_index, col_index);

                ///< if this bit last is 0, now is 1; action is relased.
                if(((0x00000001 << row_index) & row) == (0x00000001 << row_index))
                    _dm_btn_debounce_add(btn_id,FALSE);

                ///< if this bit is 0, action is pressed.
                if(!(((0x00000001) << row_index) & row)){
                    _dm_btn_debounce_add(btn_id,TRUE);
#if KB_IO_DEBUG_ENABLE
					debug_io(btn_id);
#endif
                }
            }
        }
        ///< store this col's row value.
        if(row != last_iomap[col_index])
            last_iomap[col_index] = row;

    }
    _dm_btn_debounce_done();
}


void dm_set_gpio_wake(uint32_t pins_map)
{
	int i=0,k=0;
	for(i=0; i<32; i++)
	{
		if((pins_map & ((0x00000001)<< i)) && (k<4))
		{
			syscfg_cpusleep_cmd_wakeup_src((wakeup_mcu_type)(src_int0_wakeup_mcu+k),ENABLE);
			syscfg_deepsleep_set_gpiopin_wakeup_src((irq_bank)(GPIO_Int0+k),WakeUpType_32K_AntiShake,(gpio_pin)i);
			gpio_set_irq_src_type((irq_bank)(GPIO_Int0+k),(gpio_pin)i,GPIO_Int_Type_RisingEdge);
			k++;
		}
	}
}

void dm_button_init(void)
{
    memset(&dm_gpio_btn_env, 0, sizeof(dm_gpio_btn_env_t));
	memset(&dm_btn_debounce, 0, sizeof(dm_btn_debounce_t));
	memset(&last_iomap, 0, sizeof(last_iomap));

    user_gpio_init();
}

void dm_button_keyboard_config(dm_gpio_btn_conf *p_conf)
{
	timer_config_t   kb_timer_param;

	memcpy(&dm_gpio_btn_env.btn_param, p_conf, sizeof(dm_gpio_btn_conf));
	dm_gpio_btn_env.button_cbk 			= p_conf->button_cbk;
	dm_gpio_btn_env.dm_connect_event_cbk	= p_conf->event_cbk;
	dm_gpio_btn_env.state      			= DM_BTN_STATE_IDLE;

    /// timer init
    kb_timer_param.timer_idx = IDX_TIMER2;
	kb_timer_param.timer_param.timer_period = p_conf->scan_interval;
	timer_config(TIMER2, &kb_timer_param, gpio_timer_callback);

    user_gpio_cfg_parameter_t p_parameter;
    p_parameter.gpio_handler = user_gpio_callback;
    user_gpio_config(&p_parameter);
    _dm_gpio_into_default_state();
	dm_set_gpio_wake(p_conf->interrupt_io_map);
}

uint32_t get_kb_led_pin(kb_led_id type)
{
	switch(type){
		case cap_led:      		/// 扫描按键
			return dm_gpio_btn_env.btn_param.cap_led_io_map;
		case num_led:      		/// 扫描按键
			return dm_gpio_btn_env.btn_param.num_led_io_map;
		case scroll_led:      	/// 扫描按键
			return dm_gpio_btn_env.btn_param.scroll_led_io_map;
		case power_led:   		/// 扫描按键
			return dm_gpio_btn_env.btn_param.power_led_io_map;
		case power2_led:
			return dm_gpio_btn_env.btn_param.power2_led_io_map;
		case bt_led:      		/// 扫描按键
			return dm_gpio_btn_env.btn_param.bt_led_io_map;
		default:
			break;
	}
	return 0;
}

void close_all_kb_led(void)
{
	user_gpio_dir_out_hig(dm_gpio_btn_env.btn_param.cap_led_io_map
		|dm_gpio_btn_env.btn_param.num_led_io_map
		|dm_gpio_btn_env.btn_param.scroll_led_io_map
		|dm_gpio_btn_env.btn_param.power_led_io_map
		|dm_gpio_btn_env.btn_param.power2_led_io_map
		|dm_gpio_btn_env.btn_param.bt_led_io_map,
		0);
}

void reg_dm_button_gpio(dm_gpio_btn_conf *pdm_button_param,dm_gpio_btn_cbk  cbk,sdk_event_cbk	con_cbk)
{
		/*pairlist_read_flash*/
	//	pairlist_read_flash();
		/*button init*/
		dm_button_init();
		dm_gpio_btn_env.btn_param.scan_interval	= pdm_button_param->scan_interval;
		dm_gpio_btn_env.btn_param.scan_times		= pdm_button_param->scan_times;
		dm_gpio_btn_env.btn_param.scan_valid_times	= pdm_button_param->scan_interval;
	
	// D1 C7 A0-6	B0-6	列
		dm_gpio_btn_env.btn_param.col_io_map		= pdm_button_param->col_io_map;
	// C1-6 B7 D0		行
		dm_gpio_btn_env.btn_param.row_io_map		= pdm_button_param->row_io_map;
	
	//	POWER		LED D5
	//	BT			LED D6	 bt
	//	CAPSLOCK	LED D7 cap
		dm_gpio_btn_env.btn_param.scan_io_map 		= pdm_button_param->scan_io_map;	/// 蓝牙连接按键 PC0 输入
		dm_gpio_btn_env.btn_param.power_led_io_map	= pdm_button_param->power_led_io_map;	/// 电源指示灯 PD7	
		dm_gpio_btn_env.btn_param.bt_led_io_map	= pdm_button_param->bt_led_io_map;	/// 蓝牙指示灯 PD5	
		dm_gpio_btn_env.btn_param.cap_led_io_map	= pdm_button_param->cap_led_io_map; 	//	led 大写灯 PD6 
		dm_gpio_btn_env.btn_param.interrupt_io_map	= pdm_button_param->interrupt_io_map;
	
		dm_gpio_btn_env.btn_param.button_cbk 		= cbk;									//报告回调函数
		dm_gpio_btn_env.btn_param.event_cbk 		= con_cbk;								//连接回调函数

		
		dm_button_keyboard_config(&dm_gpio_btn_env.btn_param);
}
