// src/Input.cpp

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

// 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;

// Gamepad static members
int              Input::gamepad_id_ = -1;
GLFWgamepadstate Input::gamepad_state_{};
GLFWgamepadstate Input::gamepad_prev_state_{};
bool             Input::gamepad_connected_ = false;

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

    // Scan for connected gamepad (PS5 DualSense, PS4 DualShock, or any)
    for (int jid = GLFW_JOYSTICK_1; jid <= GLFW_JOYSTICK_LAST; ++jid) {
        if (glfwJoystickPresent(jid) && glfwJoystickIsGamepad(jid)) {
            gamepad_id_ = jid;
            gamepad_connected_ = true;
            break;
        }
    }
}

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

    // Poll gamepad state
    gamepad_prev_state_ = gamepad_state_;
    if (gamepad_id_ >= 0 && glfwJoystickPresent(gamepad_id_)) {
        if (glfwJoystickIsGamepad(gamepad_id_)) {
            gamepad_connected_ = true;
            glfwGetGamepadState(gamepad_id_, &gamepad_state_);
        } else {
            gamepad_connected_ = false;
        }
    } else {
        gamepad_connected_ = false;
        // Try to find a new gamepad
        for (int jid = GLFW_JOYSTICK_1; jid <= GLFW_JOYSTICK_LAST; ++jid) {
            if (glfwJoystickPresent(jid) && glfwJoystickIsGamepad(jid)) {
                gamepad_id_ = jid;
                gamepad_connected_ = true;
                glfwGetGamepadState(gamepad_id_, &gamepad_state_);
                break;
            }
        }
    }
}

// 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_;
}

// Gamepad methods
bool Input::IsGamepadConnected() {
    return gamepad_connected_;
}

bool Input::IsGamepadButtonPressed(GamepadButton button) {
    int b = static_cast<int>(button);
    return gamepad_connected_ &&
           gamepad_state_.buttons[b] == GLFW_PRESS &&
           gamepad_prev_state_.buttons[b] == GLFW_RELEASE;
}

bool Input::IsGamepadButtonDown(GamepadButton button) {
    return gamepad_connected_ &&
           gamepad_state_.buttons[static_cast<int>(button)] == GLFW_PRESS;
}

float Input::GetGamepadAxis(GamepadAxis axis, float deadzone) {
    if (!gamepad_connected_) return 0.0f;
    float val = gamepad_state_.axes[static_cast<int>(axis)];
    if (std::fabs(val) < deadzone) return 0.0f;
    // Remap past deadzone to 0..1 range
    float sign = val > 0.0f ? 1.0f : -1.0f;
    return sign * (std::fabs(val) - deadzone) / (1.0f - deadzone);
}

glm::vec2 Input::GetLeftStick(float deadzone) {
    return {GetGamepadAxis(GamepadAxis::LeftX, deadzone),
            GetGamepadAxis(GamepadAxis::LeftY, deadzone)};
}

glm::vec2 Input::GetRightStick(float deadzone) {
    return {GetGamepadAxis(GamepadAxis::RightX, deadzone),
            GetGamepadAxis(GamepadAxis::RightY, deadzone)};
}

float Input::GetL2() {
    if (!gamepad_connected_) return 0.0f;
    // GLFW reports triggers as -1 (released) to 1 (fully pressed), remap to 0..1
    return (gamepad_state_.axes[static_cast<int>(GamepadAxis::L2)] + 1.0f) * 0.5f;
}

float Input::GetR2() {
    if (!gamepad_connected_) return 0.0f;
    return (gamepad_state_.axes[static_cast<int>(GamepadAxis::R2)] + 1.0f) * 0.5f;
}
