#include <malloc.h>
#include "key.h"


#define EVENT_CB(ev)   if(key->cb[ev])key->cb[ev](key)
#define PRESS_REPEAT_MAX_NUM  15


static Key_t *head_key = NULL;

static void key_state_switch(Key_t *key);


void key_init(Key_t *key, uint8_t key_id, uint8_t active_level, uint8_t(*read_pin)(uint8_t)) {
    memset(key, 0, sizeof(Key_t));
    key->event = (uint8_t) NONE_PRESS;
    key->hal_read_pin = read_pin;
    key->key_level = !active_level;
    key->active_level = active_level;
    key->key_id = key_id;
}


void key_attach(Key_t *key, PressEvent event, Key_Callback_t cb, bool start) {
    // 如果事件类型为ALL_EVENT，则将回调函数cb分别赋值给所有事件类型
    if (event == KEY_ALL_EVENT) {
        for (uint8_t i = KEY_PRESS_UP; i < number_of_event; i++) {
            key->cb[i] = cb;
        }
    } else {
        // 否则，将回调函数cb赋值给指定的事件类型
        key->cb[event] = cb;
    }
    if (start)key_start(key);
}

PressEvent get_key_event(Key_t *key) {
    return (PressEvent) (key->event);
}

static void key_state_switch(Key_t *key) {
    // 读取按键的GPIO电平
    uint8_t read_gpio_level = key->hal_read_pin(key->key_id);

    // 如果按键状态大于0，则增加ticks计数
    if ((key->state) > 0) key->ticks++;

    // 按键去抖动处理
    if (read_gpio_level != key->key_level) {
        if (++(key->debounce_cnt) >= DEBOUNCE_TICKS) {
            key->key_level = read_gpio_level;
            key->debounce_cnt = 0;
        }
    } else {
        key->debounce_cnt = 0;
    }

    // 状态机处理按键事件
    switch (key->state) {
        case 0:
            if (key->key_level == key->active_level) {
                key->event = (uint8_t) KEY_PRESS_DOWN;
                EVENT_CB(KEY_PRESS_DOWN);
                key->ticks = 0;
                key->repeat = 1;
                key->state = 1;
            } else {
                key->event = (uint8_t) NONE_PRESS;
            }
            break;

        case 1:
            if (key->key_level != key->active_level) {
                key->event = (uint8_t) KEY_PRESS_UP;
                EVENT_CB(KEY_PRESS_UP);
                key->ticks = 0;
                key->state = 2;
            } else if (key->ticks > LONG_TICKS) {
                key->event = (uint8_t) KEY_LONG_PRESS_START;
                EVENT_CB(KEY_LONG_PRESS_START);
                key->state = 5;
            }
            break;

        case 2:
            if (key->key_level == key->active_level) {
                key->event = (uint8_t) KEY_PRESS_DOWN;
                EVENT_CB(KEY_PRESS_DOWN);
                if (key->repeat != PRESS_REPEAT_MAX_NUM) {
                    key->repeat++;
                }
                EVENT_CB(KEY_PRESS_REPEAT);
                key->ticks = 0;
                key->state = 3;
            } else if (key->ticks > SHORT_TICKS) {
                if (key->repeat == 1) {
                    key->event = (uint8_t) KEY_SINGLE_CLICK;
                    EVENT_CB(KEY_SINGLE_CLICK);
                } else if (key->repeat == 2) {
                    key->event = (uint8_t) KEY_DOUBLE_CLICK;
                    EVENT_CB(KEY_DOUBLE_CLICK);
                }
                key->state = 0;
            }
            break;

        case 3:
            if (key->key_level != key->active_level) {
                key->event = (uint8_t) KEY_PRESS_UP;
                EVENT_CB(KEY_PRESS_UP);
                if (key->ticks < SHORT_TICKS) {
                    key->ticks = 0;
                    key->state = 2;
                } else {
                    key->state = 0;
                }
            } else if (key->ticks > SHORT_TICKS) {
                key->state = 1;
            }
            break;

        case 5:
            if (key->key_level == key->active_level) {
                key->event = (uint8_t) KEY_LONG_PRESS_HOLD;
                EVENT_CB(KEY_LONG_PRESS_HOLD);
            } else {
                key->event = (uint8_t) KEY_PRESS_UP;
                EVENT_CB(KEY_PRESS_UP);
                key->state = 0;
            }
            break;

        default:
            key->state = 0;
            break;
    }
}

int key_start(Key_t *key) {
    Key_t *target = head_key;
    while (target) {
        if (target == key) return -1;
        target = target->next;
    }
    key->next = head_key;
    head_key = key;
    return 0;
}

void key_stop(Key_t *key) {
    Key_t **curr;
    for (curr = &head_key; *curr;) {
        Key_t *entry = *curr;
        if (entry == key) {
            *curr = entry->next;
#if KEY_STOP_FREE
            free(entry);
#endif
            return;
        } else {
            curr = &entry->next;
        }
    }
}

void key_ticks(void) {
    Key_t *target;
    for (target = head_key; target; target = target->next) {
        key_state_switch(target);
    }
}