#include "input.h"
#include "debug.h"
#include <stdint.h>

typedef struct InputAction {
    const uint8_t* positive;
    const uint8_t* negative;
    int last;
    InputActionDelegate delegate;
} InputAction;

static InputAction* _actions = NULL;
static size_t _actions_len = 0;
static size_t _actions_cap = 0;

static const uint8_t* _keys;

void input_init() {
    _actions = malloc(8 * sizeof(InputAction));
    if(_actions == NULL) {
        LOG_ERROR("Failed to allocate memory for input actions array");
        return;
    }
    _actions_cap = 8;

    _keys = SDL_GetKeyboardState(NULL);
}

void input_clean() {
    free(_actions);
    _actions_len = 0;
    _actions_cap = 0;
}

static
void _key_changed_event(SDL_Event evt) {
    const uint8_t* keyptr = _keys + evt.key.keysym.scancode;
    for(InputAction* action = _actions; action < _actions + _actions_len; ++action) {
        if(keyptr == action->positive || keyptr == action->negative) {
            int val = *action->positive - *action->negative;
            if(val != action->last) {
                action->last = val;
                action->delegate(val);
            }
        }
    }
}

void input_handle_event(SDL_Event event) {
    switch(event.type) {
        default:return;
        case SDL_KEYDOWN:
        case SDL_KEYUP:
            _key_changed_event(event);
        break;
    }
}

static
void _resize_actions_if_needed(size_t needed_amount) {
    if(_actions_cap > needed_amount)
        return;

    size_t next_amount = _actions_cap;
    while(next_amount <= needed_amount) {
        next_amount *= 2;
    }

    InputAction* new = realloc(_actions, next_amount);
    if(new == NULL) {
        LOG_ERROR("Failed to allocate enough space for adding a new input actions");
        return;
    }
    _actions = new;
    _actions_cap = next_amount;
}

void input_add_axis_action(SDL_Scancode negative, SDL_Scancode positive, InputActionDelegate delegate) {
    _resize_actions_if_needed(_actions_len + 1);
    _actions[_actions_len] = (InputAction){
        .negative = _keys + negative,
        .positive = _keys + positive,
        .delegate = delegate
    };
    ++_actions_len;
}

void input_add_key_action(SDL_Scancode key, InputActionDelegate delegate) {
    _resize_actions_if_needed(_actions_len + 1);
    _actions[_actions_len] = (InputAction) {
        .negative = _keys + SDL_SCANCODE_UNKNOWN,
        .positive = _keys + key,
        .delegate = delegate
    };
    ++_actions_len;
}

static
void _remove_element(InputAction* item) {
    InputAction* next = item + 1;

    if(next < _actions + _actions_len) {
        memmove(item, next, (_actions + _actions_len) - next);
    }

    --_actions_len;
}

void input_remove_actions(InputActionDelegate delegate) {
    for(InputAction* action = _actions; action < _actions + _actions_len; ++action) {
        if(action->delegate == delegate) {
            _remove_element(action);
        }
    }
}