#pragma once
#define GLFW_INCLUDE_NONE
#include <GLFW/glfw3.h>
#include <unordered_set>
#include <array>
#include <glm/glm.hpp>

// Modern input system
class Input {
public:
    enum class KeyState : int {
        Released = GLFW_RELEASE,
        Pressed  = GLFW_PRESS,
        Held     = GLFW_REPEAT
    };

    enum class MouseButton : int {
        Left   = GLFW_MOUSE_BUTTON_LEFT,
        Right  = GLFW_MOUSE_BUTTON_RIGHT,
        Middle = GLFW_MOUSE_BUTTON_MIDDLE
    };

    static void Initialize(GLFWwindow* window);
    static void Update();

    [[nodiscard]] static bool IsKeyPressed(int key);
    [[nodiscard]] static bool IsKeyHeld(int key);
    [[nodiscard]] static bool IsKeyReleased(int key);

    [[nodiscard]] static bool IsMouseButtonPressed(MouseButton button);
    [[nodiscard]] static glm::vec2 GetMousePosition();
    [[nodiscard]] static glm::vec2 GetMouseDelta();
    [[nodiscard]] static float     GetScrollOffset();

    [[nodiscard]] static constexpr bool IsValidKey(int key) noexcept {
        return key >= 0 && key < GLFW_KEY_LAST;
    }

private:
    static GLFWwindow*                 window_;
    static std::unordered_set<int>     pressed_keys_;
    static std::unordered_set<int>     held_keys_;
    static std::unordered_set<int>     released_keys_;
    static std::array<bool, GLFW_MOUSE_BUTTON_LAST> mouse_buttons_;
    static glm::vec2                   mouse_position_;
    static glm::vec2                   mouse_delta_;
    static float                       scroll_offset_;

    static void KeyCallback(GLFWwindow* window, int key, int scancode, int action, int mods);
    static void MouseButtonCallback(GLFWwindow* window, int button, int action, int mods);
    static void CursorPositionCallback(GLFWwindow* window, double xpos, double ypos);
    static void ScrollCallback(GLFWwindow* window, double xoffset, double yoffset);
};

// Out-of-class definitions
inline bool Input::IsKeyPressed(int key) {
    return IsValidKey(key) && pressed_keys_.contains(key);
}
inline bool Input::IsKeyHeld(int key) {
    return IsValidKey(key) && held_keys_.contains(key);
}
inline bool Input::IsKeyReleased(int key) {
    return IsValidKey(key) && released_keys_.contains(key);
}
