#pragma once

#include <SDL3/SDL.h>  // Para SDL_GamepadButton, SDL_ScaleMode

#include <algorithm>  // Para copy
#include <array>      // Para array
#include <string>     // Para allocator, string
#include <unordered_map>
#include <vector>  // Para vector

#include "difficulty.h"  // Para Code
// #include "gamepad_config_manager.h"
#include "input.h"                 // Para InputAction, InputDevice
#include "lang.h"                  // Para Code
#include "manage_hiscore_table.h"  // Para HiScoreEntry

static constexpr int INVALID_INDEX = -1;

namespace Options {
// --- Opciones de ventana ---
struct WindowOptions {
        std::string caption;  // Texto que aparece en la barra de título de la ventana
        int zoom{2};          // Valor por el que se multiplica el tamaño de la ventana
        int max_zoom{2};      // Tamaño máximo para que la ventana no sea mayor que la pantalla

        // Constructor por defecto con valores iniciales
        WindowOptions()
            : caption("Coffee Crisis Arcade Edition") {}
};

// --- Opciones de vídeo ---
struct VideoOptions {
        SDL_ScaleMode scale_mode{SDL_ScaleMode::SDL_SCALEMODE_NEAREST};  // Filtro usado para el escalado de la imagen
        bool fullscreen{false};                                          // Indica si se usa pantalla completa
        bool vsync{true};                                                // Indica si se usa vsync
        bool integer_scale{true};                                        // Indica si se usa escalado entero
        bool shaders{false};                                             // Indica si se usan shaders para los filtros de vídeo
        std::string info;                                                // Información sobre el modo de vídeo

        // Constructor por defecto con valores iniciales
        VideoOptions() = default;
};

// --- Opciones de música ---
struct MusicOptions {
        bool enabled{true};  // Indica si la música suena o no
        int volume{100};     // Volumen de la música

        // Constructor por defecto
        MusicOptions() = default;
};

// --- Opciones de sonido ---
struct SoundOptions {
        bool enabled{true};  // Indica si los sonidos suenan o no
        int volume{100};     // Volumen de los sonidos

        // Constructor por defecto
        SoundOptions() = default;
};

// --- Opciones de audio ---
struct AudioOptions {
        MusicOptions music;  // Opciones para la música
        SoundOptions sound;  // Opciones para los efectos de sonido
        bool enabled{true};  // Indica si el audio está activo o no
        int volume{100};     // Volumen general del audio

        // Constructor por defecto
        AudioOptions() = default;
};

// --- Opciones de configuración ---
struct SettingsOptions {
        Difficulty::Code difficulty{Difficulty::Code::NORMAL};  // Dificultad del juego
        Lang::Code language{Lang::Code::VALENCIAN};             // Idioma usado en el juego
        bool autofire{true};                                    // Indicador de autofire
        bool shutdown_enabled{false};                           // Especifica si se puede apagar el sistema
        std::vector<HiScoreEntry> hi_score_table;               // Tabla de mejores puntuaciones
        std::vector<int> last_hi_score_entry;                   // Últimas posiciones de entrada en la tabla
        std::string config_file;                                // Ruta al fichero donde guardar la configuración y las opciones del juego
        std::string controllers_file;                           // Ruta al fichero con las configuraciones de los mandos

        // Constructor por defecto con valores iniciales
        SettingsOptions()
            : last_hi_score_entry({INVALID_INDEX, INVALID_INDEX}) {}

        // Reinicia las últimas entradas de puntuación
        void clearLastHiScoreEntries() {
            last_hi_score_entry.at(0) = INVALID_INDEX;
            last_hi_score_entry.at(1) = INVALID_INDEX;
        }
};

struct GamepadOptions {
        std::shared_ptr<Input::Gamepad> gamepad = nullptr;  // Referencia al mando
        std::string name;                                   // Nombre del mando
        int player_id;                                      // Jugador asociado al mando

        GamepadOptions(int custom_player_id = INVALID_INDEX)
            : player_id(custom_player_id) {}
};

// --- Opciones pendientes de aplicar ---
struct PendingChanges {
        Lang::Code new_language{Lang::Code::VALENCIAN};             // Idioma en espera de aplicar
        Difficulty::Code new_difficulty{Difficulty::Code::NORMAL};  // Dificultad en espera de aplicar
        bool has_pending_changes{false};                            // Indica si hay cambios pendientes

        // Constructor por defecto con valores iniciales
        PendingChanges() = default;
};

// --- Variables globales ---
extern WindowOptions window;                     // Opciones de la ventana
extern SettingsOptions settings;                 // Opciones del juego
extern VideoOptions video;                       // Opciones de vídeo
extern AudioOptions audio;                       // Opciones de audio
extern std::vector<GamepadOptions> controllers;  // Opciones de mando para cada jugador
extern PendingChanges pending_changes;           // Opciones que se aplican al cerrar

// --- Funciones de configuración ---
void init();                                            // Inicializa las opciones del programa
void setConfigFile(const std::string &file_path);       // Establece el fichero de configuración
void setControllersFile(const std::string &file_path);  // Establece el fichero de configuración de mandos
auto loadFromFile() -> bool;                            // Carga el fichero de configuración
auto saveToFile() -> bool;                              // Guarda el fichero de configuración
void setKeyboardToPlayer(int player_id);                // Asigna el teclado al jugador
void swapKeyboard();                                    // Intercambia el teclado de jugador
void swapControllers();                                 // Intercambia los jugadores asignados a los dos primeros mandos
auto getPlayerWhoUsesKeyboard() -> int;                 // Averigua quién está usando el teclado
void applyPendingChanges();                             // Aplica los cambios pendientes copiando los valores a sus variables
void checkPendingChanges();                             // Verifica si hay cambios pendientes

}  // namespace Options