#include "button.h"
#include "app_timer.h"

static const uint8_t m_board_btn_list[BUTTONS_NUMBER]       = BUTTONS_LIST;
static alps_event_callback_t   m_registered_callback         = NULL;
static bsp_button_event_cfg_t m_events_list[BUTTONS_NUMBER] = {{BSP_EVENT_NOTHING, BSP_EVENT_NOTHING, BSP_EVENT_NOTHING}};

APP_TIMER_DEF(m_bsp_button_tmr);
static void button_event_handler(uint8_t pin_no, uint8_t button_action);

static const app_button_cfg_t alps_buttons[BUTTONS_NUMBER] =
{
    {BUTTON_A, false, BUTTON_PULL, button_event_handler},
    {BUTTON_B, false, BUTTON_PULL, button_event_handler},
    {BUTTON_MENU, false, BUTTON_PULL, button_event_handler},
};


bool bsp_button_is_pressed(uint32_t button)
{
    if (button < BUTTONS_NUMBER)
    {
        return button_get_state(button);
    }
    else
    {
        //If button is not present always return false
        return false;
    }
}


uint32_t button_enable(void)
{
   return app_button_enable();
}

uint32_t button_disable()
{
    return app_button_disable();
}


static uint32_t button_wakeup_configure(uint32_t button_idx, bool enable)
{
    nrf_gpio_pin_sense_t sense = enable ?
                     (BUTTONS_ACTIVE_STATE ? NRF_GPIO_PIN_SENSE_HIGH : NRF_GPIO_PIN_SENSE_LOW) :
                     NRF_GPIO_PIN_NOSENSE;
    nrf_gpio_cfg_sense_set(button_index_to_pin(button_idx), sense);

    return NRF_SUCCESS;
}
uint32_t button_wakeup_enable(uint32_t button_idx)
{
    return button_wakeup_configure(button_idx, true);
}

uint32_t button_wakeup_disable(uint32_t button_idx)
{
    return button_wakeup_configure(button_idx, false);
}

/**@brief Function for handling button events.
 *
 * @param[in]   pin_no          The pin number of the button pressed.
 * @param[in]   button_action   Action button.
 */
static void button_event_handler(uint8_t pin_no, uint8_t button_action)
{
    alps_event_t        event  = BSP_EVENT_NOTHING;
    uint32_t           button = 0;
    uint32_t           err_code;
    static uint8_t     current_long_push_pin_no;              /**< Pin number of a currently pushed button, that could become a long push if held long enough. */
    static alps_event_t release_event_at_push[BUTTONS_NUMBER]; /**< Array of what the release event of each button was last time it was pushed, so that no release event is sent if the event was bound after the push of the button. */

    button = button_pin_to_index(pin_no);

    if (button < BUTTONS_NUMBER)
    {
        switch (button_action)
        {
        case APP_BUTTON_PUSH:
            event = m_events_list[button].push_event;
            if (m_events_list[button].long_push_event != BSP_EVENT_NOTHING)
            {
                err_code = app_timer_start(m_bsp_button_tmr, APP_TIMER_TICKS(BSP_LONG_PUSH_TIMEOUT_MS), (void*)&current_long_push_pin_no);
                if (err_code == NRF_SUCCESS)
                {
                    current_long_push_pin_no = pin_no;
                }
            }
            release_event_at_push[button] = m_events_list[button].release_event;
            break;
        case APP_BUTTON_RELEASE:
            (void)app_timer_stop(m_bsp_button_tmr);
            if (release_event_at_push[button] == m_events_list[button].release_event)
            {
                event = m_events_list[button].release_event;
            }
            break;
        case BSP_BUTTON_ACTION_LONG_PUSH:
            event = m_events_list[button].long_push_event;
        }
    }

    if ((event != BSP_EVENT_NOTHING) && (m_registered_callback != NULL))
    {
        m_registered_callback(event);
    }
}

/**@brief Handle events from button timer.
 *
 * @param[in]   p_context   parameter registered in timer start function.
 */
static void button_timer_handler(void * p_context)
{
    button_event_handler(*(uint8_t *)p_context, BSP_BUTTON_ACTION_LONG_PUSH);
}


/**@brief Assign specific event to button.
 */
uint32_t button_action_event_assign(uint32_t button, bsp_button_action_t action, alps_event_t event)
{
    uint32_t err_code = NRF_SUCCESS;

    if (button < BUTTONS_NUMBER)
    {
        if (event == BSP_EVENT_DEFAULT)
        {
            // Setting default action: BSP_EVENT_KEY_x for PUSH actions, BSP_EVENT_NOTHING for RELEASE and LONG_PUSH actions.
            event = (action == BSP_BUTTON_ACTION_PUSH) ? (alps_event_t)(BSP_EVENT_KEY_0 + button) : BSP_EVENT_NOTHING;
        }
        switch (action)
        {
            case BSP_BUTTON_ACTION_PUSH:
                m_events_list[button].push_event = event;
                break;
            case BSP_BUTTON_ACTION_LONG_PUSH:
                m_events_list[button].long_push_event = event;
                break;
            case BSP_BUTTON_ACTION_RELEASE:
                m_events_list[button].release_event = event;
                break;
            default:
                err_code = NRF_ERROR_INVALID_PARAM;
                break;
        }
    }
    else
    {
        err_code = NRF_ERROR_INVALID_PARAM;
    }

    return err_code;
}


bool button_get_state(uint32_t button_idx)
{
    ASSERT(button_idx < BUTTONS_NUMBER);
    bool pin_set = nrf_gpio_pin_read(m_board_btn_list[button_idx]) ? true : false;
    return (pin_set == (BUTTONS_ACTIVE_STATE ? true : false));
}


uint32_t button_pin_to_index(uint32_t pin_number)
{
    uint32_t i;
    uint32_t ret = 0xFFFFFFFF;
    for (i = 0; i < BUTTONS_NUMBER; ++i)
    {
        if (m_board_btn_list[i] == pin_number)
        {
            ret = i;
            break;
        }
    }
    return ret;
}

uint32_t button_index_to_pin(uint32_t button_idx)
{
    ASSERT(button_idx < BUTTONS_NUMBER);
    return m_board_btn_list[button_idx];
}


uint32_t button_init(alps_event_callback_t event_callback)
{
    uint32_t num;
    uint32_t err_code = NRF_SUCCESS;

    for (num = 0; ((num < BUTTONS_NUMBER) && (err_code == NRF_SUCCESS)); num++)
    {
        err_code = button_action_event_assign(num, BSP_BUTTON_ACTION_PUSH, BSP_EVENT_DEFAULT);
    }

    if (err_code == NRF_SUCCESS)
    {
        err_code = app_button_init((app_button_cfg_t *)alps_buttons,
                                   BUTTONS_NUMBER,
                                   APP_TIMER_TICKS(50));
    }

    if (err_code == NRF_SUCCESS)
    {
        err_code = app_button_enable();
    }

    if (err_code == NRF_SUCCESS)
    {
        m_registered_callback = event_callback;
        err_code = app_timer_create(&m_bsp_button_tmr,
                                    APP_TIMER_MODE_SINGLE_SHOT,
                                    button_timer_handler);
    }

    return err_code;
}

