//
// Created by slinky on 5/3/26.
//

#include "InputManager.h"

bool InputManager::mouseInit = false;
float InputManager::lastMousePositionX = 0;
float InputManager::lastMousePositionY = 0;

std::unordered_map<std::string, std::unique_ptr<InputAction>> registeredActions = {};
static bool currentKeyStatesData[512] = {};
static bool previousKeyStatesData[512] = {};

static std::vector<MouseDeltaCallback> mouse_listeners = {};

static void set_key_state(int key, bool state);

void InputManager::mouse_pos_callback(GLFWwindow *window, double x, double y) {
    const auto xpos = static_cast<float>(x);
    const auto ypos = static_cast<float>(y);

    if (!mouseInit) {
        lastMousePositionX = xpos;
        lastMousePositionY = ypos;
        mouseInit = true;
    }

    float xOff = xpos - lastMousePositionX;
    float yOff = lastMousePositionY - ypos;

    lastMousePositionX = xpos;
    lastMousePositionY = ypos;

    for (const auto& cb : mouse_listeners) {
        cb(xOff, yOff);
    }
}

void InputManager::add_mouse_listener(MouseDeltaCallback callback) {
    mouse_listeners.push_back(callback);
}

void InputManager::key_callback(GLFWwindow* window, int key, int scancode, int action, int mods) {
    if (action == GLFW_REPEAT) return;

    set_key_state(key, action == GLFW_PRESS);
}

void InputManager::generate_input_action(std::string_view actionName, std::initializer_list<InputRequirement> requirements) {
    if (registeredActions.contains(actionName.data())) {
        return;
    }

    InputAction action;
    for (const auto& requirement : requirements) {
        action.requirements.push_back(requirement);
    }
    const std::string key = actionName.data();
    registeredActions[key] = std::make_unique<InputAction>(action);
}

bool InputManager::check_action_performed(std::string_view actionName) {
    if (!registeredActions.contains(actionName.data())) {
        return false;
    }

    InputAction* action = registeredActions[actionName.data()].get();

    if (!action) {
        return false;
    }

    for (const auto&[key, state] : action->requirements) {
        switch (state) {
            case KEY_DOWN:
                if (!key_down(key))
                    return false;
                break;
            case KEY_HELD:
                if (!key_held(key))
                    return false;
                break;
            case KEY_PRESSED:
                if (!key_pressed(key))
                    return false;
                break;
            case KEY_RELEASED:
                if (!key_released(key))
                    return false;
                break;
        }
    }

    return true;
}

bool InputManager::key_down(int key) {
    return currentKeyStatesData[key];
}
bool InputManager::key_up(int key) {
    return !currentKeyStatesData[key];
}
bool InputManager::key_pressed(int key) {
    return !previousKeyStatesData[key] && currentKeyStatesData[key];
}
bool InputManager::key_released(int key) {
    return previousKeyStatesData[key] && !currentKeyStatesData[key];
}
bool InputManager::key_held(int key) {
    return previousKeyStatesData[key] && currentKeyStatesData[key];
}

void set_key_state(int key, bool state) {
    previousKeyStatesData[key] = currentKeyStatesData[key];
    currentKeyStatesData[key] = state;
}