// src/Input.cpp

#include "Input.h"
#include <GLFW/glfw3.h>

// Static member definitions
GLFWwindow* Input::window_ = nullptr;
std::unordered_set<int> Input::pressed_keys_;
std::unordered_set<int> Input::held_keys_;
std::unordered_set<int> Input::released_keys_;
std::array<bool, GLFW_MOUSE_BUTTON_LAST> Input::mouse_buttons_{};
glm::vec2 Input::mouse_position_{0.0f, 0.0f};
glm::vec2 Input::mouse_delta_{0.0f, 0.0f};
float     Input::scroll_offset_ = 0.0f;

void Input::Initialize(GLFWwindow* window) {
    window_ = window;
    glfwSetKeyCallback(window, KeyCallback);
    glfwSetMouseButtonCallback(window, MouseButtonCallback);
    glfwSetCursorPosCallback(window, CursorPositionCallback);
    glfwSetScrollCallback(window, ScrollCallback);
}

void Input::Update() {
    // Clear previous frame states
    released_keys_.clear();
    scroll_offset_ = 0.0f;
    mouse_delta_ = {0.0f, 0.0f};

    // Process held keys: keys that were pressed last frame and still down
    for (auto key : held_keys_) {
        if (glfwGetKey(window_, key) == GLFW_PRESS) {
            // still held
        } else {
            released_keys_.insert(key);
        }
    }
    // Update held_keys_: all currently pressed
    held_keys_.clear();
    pressed_keys_.clear(); // consumed this frame
}

// GLFW callbacks
void Input::KeyCallback(GLFWwindow* window, int key, int scancode, int action, int mods) {
    if (action == GLFW_PRESS) {
        pressed_keys_.insert(key);
        held_keys_.insert(key);
    } else if (action == GLFW_RELEASE) {
        released_keys_.insert(key);
        held_keys_.erase(key);
    }
}

void Input::MouseButtonCallback(GLFWwindow* window, int button, int action, int mods) {
    if (button < 0 || button >= GLFW_MOUSE_BUTTON_LAST) return;
    if (action == GLFW_PRESS) {
        mouse_buttons_[button] = true;
    } else if (action == GLFW_RELEASE) {
        mouse_buttons_[button] = false;
    }
}

void Input::CursorPositionCallback(GLFWwindow* window, double xpos, double ypos) {
    glm::vec2 newPos{static_cast<float>(xpos), static_cast<float>(ypos)};
    mouse_delta_ = newPos - mouse_position_;
    mouse_position_ = newPos;
}

void Input::ScrollCallback(GLFWwindow* window, double xoffset, double yoffset) {
    scroll_offset_ = static_cast<float>(yoffset);
}

bool Input::IsMouseButtonPressed(MouseButton button) {
    return mouse_buttons_[static_cast<int>(button)];
}

glm::vec2 Input::GetMousePosition() {
    return mouse_position_;
}

glm::vec2 Input::GetMouseDelta() {
    return mouse_delta_;
}

float Input::GetScrollOffset() {
    return scroll_offset_;
}
