#pragma once

#include <SDL3/SDL.h>

#include <string>  // for string, basic_string
#include <vector>  // for vector

// Valores de repetición
constexpr bool REPEAT_TRUE = true;
constexpr bool REPEAT_FALSE = false;

// Métodos de entrada
constexpr int INPUT_USE_KEYBOARD = 0;
constexpr int INPUT_USE_GAMECONTROLLER = 1;
constexpr int INPUT_USE_ANY = 2;

enum inputs_e {
    // Inputs obligatorios
    input_null,
    input_up,
    input_down,
    input_left,
    input_right,
    input_pause,
    input_exit,
    input_accept,
    input_cancel,

    // Inputs personalizados
    input_fire_left,
    input_fire_center,
    input_fire_right,
    input_window_fullscreen,
    input_window_inc_size,
    input_window_dec_size,

    // GPU / shaders (hotkeys provisionales hasta que haya menú de opciones)
    input_prev_preset,
    input_next_preset,
    input_toggle_shader,
    input_toggle_shader_type,

    // Input obligatorio
    input_number_of_inputs
};

enum i_disable_e {
    d_notDisabled,
    d_forever,
    d_keyPressed
};

class Input {
   private:
    struct keyBindings_t {
        Uint8 scancode;  // Scancode asociado
        bool active;     // Indica si está activo
    };

    struct GameControllerBindings_t {
        SDL_GamepadButton button;  // GameControllerButton asociado
        bool active;               // Indica si está activo
    };

    // Objetos y punteros
    std::vector<SDL_Gamepad *> connectedControllers;     // Vector con todos los mandos conectados
    std::vector<SDL_JoystickID> connectedControllerIds;  // Instance IDs paralelos para mapear eventos

    // Variables
    std::vector<keyBindings_t> keyBindings;                        // Vector con las teclas asociadas a los inputs predefinidos
    std::vector<GameControllerBindings_t> gameControllerBindings;  // Vector con las teclas asociadas a los inputs predefinidos
    std::vector<std::string> controllerNames;                      // Vector con los nombres de los mandos
    int numGamepads;                                               // Numero de mandos conectados
    std::string dbPath;                                            // Ruta al archivo gamecontrollerdb.txt
    bool verbose;                                                  // Indica si ha de mostrar mensajes
    i_disable_e disabledUntil;                                     // Tiempo que esta deshabilitado
    bool enabled;                                                  // Indica si está habilitado

    // Construye el nombre visible de un mando (name truncado + sufijo #N)
    std::string buildControllerName(SDL_Gamepad *pad, int padIndex);

    // Constructor privado (usar Input::init)
    explicit Input(const std::string &file);

    // Instancia única
    static Input *instance;

   public:
    // Singleton API
    static void init(const std::string &gameControllerDbPath);  // Crea la instancia
    static void destroy();                                      // Libera la instancia
    static auto get() -> Input *;                               // Obtiene el puntero a la instancia

    // Destructor
    ~Input();

    // Actualiza el estado del objeto
    void update();

    // Asigna inputs a teclas
    void bindKey(Uint8 input, SDL_Scancode code);

    // Asigna inputs a botones del mando
    void bindGameControllerButton(Uint8 input, SDL_GamepadButton button);

    // Comprueba si un input esta activo
    bool checkInput(Uint8 input, bool repeat = true, int device = INPUT_USE_ANY, int index = 0);

    // Comprueba si hay almenos un input activo
    bool checkAnyInput(int device = INPUT_USE_ANY, int index = 0);

    // Busca si hay un mando conectado
    bool discoverGameController();

    // Procesa un evento SDL_EVENT_GAMEPAD_ADDED. Devuelve true si el mando se ha añadido
    // (no estaba ya registrado) y escribe el nombre visible en outName.
    bool handleGamepadAdded(SDL_JoystickID jid, std::string &outName);

    // Procesa un evento SDL_EVENT_GAMEPAD_REMOVED. Devuelve true si se ha encontrado y
    // eliminado, y escribe el nombre visible en outName.
    bool handleGamepadRemoved(SDL_JoystickID jid, std::string &outName);

    // Comprueba si hay algun mando conectado
    bool gameControllerFound();

    // Obten el numero de mandos conectados
    int getNumControllers();

    // Obten el nombre de un mando de juego
    std::string getControllerName(int index);

    // Establece si ha de mostrar mensajes
    void setVerbose(bool value);

    // Deshabilita las entradas durante un periodo de tiempo
    void disableUntil(i_disable_e value);

    // Hablita las entradas
    void enable();
};