/**
******************************************************************************************************************************************************************************************
*@file                       dm_button.c
*@author         
*@data                       
*@version                    v1.0
*@par Copyright(c):
*
*@par History:
*     version:author,date,desc
*     v1.0   :create this file
*
*******************************************************************************************************************************************************************************************
*/
#include <stdint.h>
#include <stdbool.h>
#include "cpu.h"
#include "user_keyboard.h"
#include "user_timer.h"
#include "dm_button.h"
#include "cpu_deepsleep.h"
#include "app_trace.h"
#if (DM_BUTTON_KEYBOARD)

#define DM_BTN_BUILD_ID(row, col) ((dm_btn_id_t)row | ((dm_btn_id_t)col << 8))
#define DM_BTN_MAX_IO_NUM         (16)
#define DM_BTN_MAX_MULTIPLE       (1)

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_btn_conf_t         btn_param;
	dm_btn_cbk_t          button_cbk;
    volatile uint8_t      state;
} dm_btn_env_t;

static dm_btn_env_t      dm_btn_env;
static dm_btn_debounce_t dm_btn_debounce[DM_BTN_MAX_MULTIPLE];
static uint32_t          last_iomap;


/**
*  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;
    //// 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_IDLE;
            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;
}
void kb_timer_debounce_handler(uint8_t event, void * arg)
{
    keyboard_enable_ctrl(ENABLE);
}

/**
*  brief: manage the debounce array. close the timer.
*         if the button debounce succeed, the button will send a message
*         to reporter callback function.
*/
static void _dm_btn_debounce_done()
{
    uint8_t btn_idx = 0;
    dm_btn_report_t  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\r\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_btn_env.btn_param.scan_times))
        {
            if(dm_btn_debounce[btn_idx].scan_valid_times >= dm_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)
                {
                    
                	//dbg_printf("******pressed one button*******\r\n");
					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(dm_btn_env.button_cbk != NULL)
						dm_btn_env.button_cbk(&evt);
                }
                if(dm_btn_debounce[btn_idx].action == false && dm_btn_debounce[btn_idx].btn_status == DM_BTN_RELEASED)
                {
                	//dbg_printf("******relased one button*******\r\n");
					/// report evt, clear reseased
                    evt.btn_id     = dm_btn_debounce[btn_idx].btn_id;
                    evt.action = DM_BTN_ACT_RELEASED;  
                    if(dm_btn_env.button_cbk != NULL)
						dm_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***************\r\n");
                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())
    {
        //dbg_printf("BTN is empyt!\n\r");
        dm_btn_env.state = DM_BTN_STATE_IDLE;
		timer_enable_ctrl_cmd(TIMER0, TIMER_STOP);
	    keyboard_enable_ctrl(ENABLE);
        cpu_prevent_sleep_clear(TIMER0_ONGOING);
        
    }
}


/*
*   0x01
*   0x02,0x03,
*   0x04,0x05,0x06,
*   0x08,0x09,0x0a,0x0c,
*   0x10,0x11,0x12,0x14,0x18,
*/

uint16_t get_key_value(uint16_t btn_id)
{
    uint16_t value = 0;
    switch(btn_id & 0xff)
	{
		case 0x1:
		{
            value = 0x01;
		}break;
		case 0x2:
		{
			value = 0x02;
		}break;
		case 0x3:
		{
			value = 0x03;
		}break;
		case 0x4:
		{
			value = 0x04;
		}break;
		case 0x5:
		{
			value = 0x05;
		}break;
		case 0x6:
		{
			value = 0x06;
		}break;
		case 0x8:
		{
			value = 0x07;
		}break;
		case 0x9:
		{
			value = 0x08;
		}break;
		case 0xa:
		{
			value = 0x09;
		}break;
		case 0xc:
		{
			value = 0x0a;
		}break;
		case 0x10:
		{
			value = 0x0b;
		}break;
		case 0x11:
		{
			value = 0x0c;
		}break;
		case 0x12:
		{
			value = 0x0d;
		}break;
		case 0x14:
		{
			value = 0x0e;
		}break;
		case 0x18:
		{
			value = 0x0f;
		}break;
		default:
		{
       
		}break;
		
	};
	return value;
}


void btn_debounce_hander(uint8_t event, uint16_t* p_io)
{
    uint8_t     io_idx = 0;
	dm_btn_id_t btn_id = 0;

    //dbg_printf("io=0x%x,last_io=0x%x\r\n",*p_io,last_iomap);
    
	if(*p_io != 0x00)
	{
	    btn_id = DM_BTN_BUILD_ID(*p_io, 0xff);
	    //dbg_printf("addr 0x%x\r\n",btn_id);
        _dm_btn_debounce_add(btn_id, true);
	}
	if(*p_io != last_iomap)
	{
	    btn_id = DM_BTN_BUILD_ID(last_iomap, 0xff);
	    //dbg_printf("addf 0x%x\r\n",btn_id);
        _dm_btn_debounce_add(btn_id, false);
	}
	else
	{
        //dbg_printf("io=0x%x,last_io=0x%x\r\n",*p_io,last_iomap);
	}
	last_iomap = *p_io;
	_dm_btn_debounce_done();
	FREE(p_io);
    
}


void keyboard_callback(uint16_t io)
{
    //dbg_printf("STATE= 0x%x\r\n",dm_btn_env.state);
	
   
	uint16_t * kb_io = (uint16_t*)NEW(sizeof(uint16_t));
	*kb_io = io;
	FsmEvent_ExternalCx(NULL, btn_debounce_hander,kb_io);
	if(dm_btn_env.state == DM_BTN_STATE_IDLE)
	{
	    timer_enable_ctrl_cmd(TIMER0, TIMER_START);
        cpu_prevent_sleep_set(TIMER0_ONGOING);
	}
	dm_btn_env.state = DM_BTN_STATE_WORK;

}
void kb_timer_callback(void)
{
   // dbg_printf("t state=0x%x\r\n",dm_btn_env.state);
	if(dm_btn_env.state == DM_BTN_STATE_PRESS)
	{
	    //dbg_printf("t p\r\n");
        uint16_t * kb_io = (uint16_t*)NEW(sizeof(uint16_t));
		*kb_io = 0;
		FsmEvent_ExternalCx(NULL, btn_debounce_hander,kb_io);
	}
	else
	{
	    //dbg_printf("t kb\r\n");
		dm_btn_env.state = DM_BTN_STATE_PRESS;
		keyboard_enable_ctrl(ENABLE);
		//FsmEventCx(NULL, kb_timer_debounce_handler,NULL);
	    
	}
    
}

void dm_button_init(void)
{
    memset(&dm_btn_env, 0, sizeof(dm_btn_env_t));
	memset(&dm_btn_debounce, 0, sizeof(dm_btn_debounce_t));
	memset(&last_iomap, 0, sizeof(last_iomap));
}

void dm_button_config(dm_btn_conf_t *p_conf)
{
    keyboard_param_t kb_param;
	timer_config_t   kb_timer_param;

	memcpy(&dm_btn_env.btn_param, p_conf, sizeof(dm_btn_env_t));
	dm_btn_env.button_cbk = p_conf->cbk;
	dm_btn_env.state      = DM_BTN_STATE_IDLE;

    /// timer init
    kb_timer_param.timer_idx = IDX_TIMER0;
	kb_timer_param.timer_param.timer_period = p_conf->scan_interval;
	timer_config(TIMER0, &kb_timer_param, kb_timer_callback);

    /// keyboard init
	kb_param.cbk		   = keyboard_callback;
	kb_param.gpio_map	   = p_conf->row_io_map;
	kb_param.scan_interval = KB_INTERVAL_48MS;
	keyboard_config(&kb_param);
	keyboard_enable_ctrl(ENABLE);

	/// gpio keyboard init
}

#endif//< DM_BUTTON
