#pragma once

#include <SDL3/SDL.h>  // Para SDL_FRect

#include <cstddef>  // Para size_t
#include <memory>   // Para shared_ptr
#include <string>   // Para string
#include <utility>  // Para move
#include <vector>   // Para vector

#include "core/rendering/sprite/animated_sprite.hpp"  // Para AnimationsFileBuffer
#include "core/rendering/text.hpp"                    // Para Text, TextFile
#include "core/rendering/texture.hpp"                 // Para Texture
#include "core/system/demo.hpp"                       // Para DemoData

struct JA_Music_t;
struct JA_Sound_t;

// --- Clase Resource: gestiona todos los recursos del juego (singleton) ---
class Resource {
   public:
    // --- Enum para el modo de carga ---
    enum class LoadingMode {
        PRELOAD,   // Carga todos los recursos al inicio
        LAZY_LOAD  // Carga los recursos bajo demanda
    };

    // --- Métodos de singleton ---
    static void init(LoadingMode mode = LoadingMode::PRELOAD);  // Inicializa el objeto Resource con modo de carga
    static void destroy();                                      // Libera el objeto Resource
    static auto get() -> Resource*;                             // Obtiene el puntero al objeto Resource

    // --- Métodos de acceso a recursos ---
    auto getSound(const std::string& name) -> JA_Sound_t*;                     // Obtiene el sonido por nombre
    auto getMusic(const std::string& name) -> JA_Music_t*;                     // Obtiene la música por nombre
    auto getTexture(const std::string& name) -> std::shared_ptr<Texture>;      // Obtiene la textura por nombre
    auto getTextFile(const std::string& name) -> std::shared_ptr<Text::File>;  // Obtiene el fichero de texto por nombre
    auto getText(const std::string& name) -> std::shared_ptr<Text>;            // Obtiene el objeto de texto por nombre
    auto getAnimation(const std::string& name) -> AnimationsFileBuffer&;       // Obtiene la animación por nombre
    auto getDemoData(int index) -> DemoData&;                                  // Obtiene los datos de demo por índice

    // --- Métodos de recarga de recursos ---
    void reload();  // Recarga todos los recursos

    // --- Cargador incremental ---
    // beginLoad prepara el estado; loadStep carga recursos hasta agotar el presupuesto;
    // devuelve true cuando ya no queda nada. renderProgress se llama una vez por frame
    // desde la escena Preload.
    void beginLoad();
    auto loadStep(int budget_ms) -> bool;
    [[nodiscard]] auto isLoadDone() const -> bool;
    void renderProgress();

    // --- Método para obtener el modo de carga actual ---
    [[nodiscard]] auto getLoadingMode() const -> LoadingMode { return loading_mode_; }

   private:
    // --- Estructuras para recursos individuales ---
    struct ResourceSound {
        std::string name;   // Nombre del sonido
        JA_Sound_t* sound;  // Objeto con el sonido

        ResourceSound(std::string name, JA_Sound_t* sound = nullptr)
            : name(std::move(name)),
              sound(sound) {}
    };

    struct ResourceMusic {
        std::string name;   // Nombre de la música
        JA_Music_t* music;  // Objeto con la música

        ResourceMusic(std::string name, JA_Music_t* music = nullptr)
            : name(std::move(name)),
              music(music) {}
    };

    struct ResourceTexture {
        std::string name;                  // Nombre de la textura
        std::shared_ptr<Texture> texture;  // Objeto con la textura

        ResourceTexture(std::string name, std::shared_ptr<Texture> texture = nullptr)
            : name(std::move(name)),
              texture(std::move(texture)) {}
    };

    struct ResourceTextFile {
        std::string name;                       // Nombre del fichero
        std::shared_ptr<Text::File> text_file;  // Objeto con los descriptores de la fuente de texto

        ResourceTextFile(std::string name, std::shared_ptr<Text::File> text_file = nullptr)
            : name(std::move(name)),
              text_file(std::move(text_file)) {}
    };

    struct ResourceText {
        std::string name;            // Nombre del objeto
        std::shared_ptr<Text> text;  // Objeto de texto

        ResourceText(std::string name, std::shared_ptr<Text> text = nullptr)
            : name(std::move(name)),
              text(std::move(text)) {}
    };

    struct ResourceAnimation {
        std::string name;                // Nombre de la animación
        AnimationsFileBuffer animation;  // Objeto con las animaciones

        ResourceAnimation(std::string name, AnimationsFileBuffer animation = {})
            : name(std::move(name)),
              animation(std::move(animation)) {}
    };

    // --- Estructura para el progreso de carga ---
    struct ResourceCount {
        size_t total{0};   // Número total de recursos
        size_t loaded{0};  // Número de recursos cargados

        ResourceCount() = default;
        ResourceCount(size_t total)
            : total(total) {}

        void add(size_t amount) { loaded += amount; }
        void increase() { loaded++; }
        [[nodiscard]] auto getPercentage() const -> float {
            return total > 0 ? static_cast<float>(loaded) / static_cast<float>(total) : 0.0F;
        }
    };

    // --- Constantes para la barra de progreso de la pantalla de carga ---
    // BAR_WIDTH_RATIO: fracción del ancho de la pantalla ocupada por la barra (centrada)
    //   1.0 = ancho completo, 0.5 = la mitad del ancho
    // BAR_Y_RATIO:     posición vertical del CENTRO de la barra en fracción de la altura
    //   0.0 = centro en el borde superior, 1.0 = centro en el borde inferior
    // BAR_HEIGHT:      grosor de la barra en píxeles
    static constexpr float BAR_WIDTH_RATIO = 0.5F;
    static constexpr float BAR_Y_RATIO = 0.85F;
    static constexpr float BAR_HEIGHT = 5.0F;

    // --- Modo de carga ---
    LoadingMode loading_mode_;

    // --- Vectores de recursos ---
    std::vector<ResourceSound> sounds_;          // Vector con los sonidos
    std::vector<ResourceMusic> musics_;          // Vector con las músicas
    std::vector<ResourceTexture> textures_;      // Vector con las texturas
    std::vector<ResourceTextFile> text_files_;   // Vector con los ficheros de texto
    std::vector<ResourceText> texts_;            // Vector con los objetos de texto
    std::vector<ResourceAnimation> animations_;  // Vector con las animaciones
    std::vector<DemoData> demos_;                // Vector con los ficheros de datos para el modo demostración

    // --- Progreso de carga ---
    ResourceCount loading_count_;         // Contador de recursos cargados
    std::shared_ptr<Text> loading_text_;  // Texto para escribir en pantalla
    std::string loading_resource_name_;   // Nombre del recurso que se está cargando
    SDL_FRect loading_wired_rect_;
    SDL_FRect loading_full_rect_;

    // --- Estado del cargador incremental ---
    enum class LoadStage {
        SOUNDS,
        MUSICS,
        TEXTURES,
        TEXT_FILES,
        ANIMATIONS,
        DEMO_DATA,
        CREATE_TEXT,
        CREATE_TEXT_TEXTURES,
        CREATE_PLAYER_TEXTURES,
        DONE
    };
    LoadStage stage_{LoadStage::DONE};
    size_t stage_index_{0};
    bool saved_vsync_{false};  // Vsync previo a beginLoad, restaurado por finishBoot/load

    // --- Métodos internos de carga y gestión ---
    void loadDemoDataQuiet();       // Carga los datos de demo sin mostrar progreso (para modo lazy)
    void loadEssentialResources();  // Carga recursos esenciales en modo lazy
    void loadEssentialTextures();   // Carga solo las texturas esenciales (fuentes)
    void loadTextFilesQuiet();      // Carga ficheros de texto sin mostrar progreso (para modo lazy)
    void createPlayerTextures();    // Crea las texturas de jugadores con todas sus variantes de paleta
    void createTextTextures();      // Crea las texturas a partir de los datos cargados
    void createText();              // Crea los objetos de texto
    void clear();                   // Vacía todos los vectores de recursos
    void load();                    // Carga todos los recursos
    void clearSounds();             // Vacía el vector de sonidos
    void clearMusics();             // Vacía el vector de músicas

    // --- Métodos para carga perezosa ---
    void initResourceLists();                                                              // Inicializa las listas de recursos sin cargar el contenido
    static auto loadSoundLazy(const std::string& name) -> JA_Sound_t*;                     // Carga un sonido específico bajo demanda
    static auto loadMusicLazy(const std::string& name) -> JA_Music_t*;                     // Carga una música específica bajo demanda
    static auto loadTextureLazy(const std::string& name) -> std::shared_ptr<Texture>;      // Carga una textura específica bajo demanda
    static auto loadTextFileLazy(const std::string& name) -> std::shared_ptr<Text::File>;  // Carga un fichero de texto específico bajo demanda
    auto loadTextLazy(const std::string& name) -> std::shared_ptr<Text>;                   // Carga un objeto de texto específico bajo demanda
    static auto loadAnimationLazy(const std::string& name) -> AnimationsFileBuffer;        // Carga una animación específica bajo demanda

    // --- Métodos internos para gestionar el progreso ---
    void calculateTotalResources();                // Calcula el número de recursos para cargar
    void updateLoadingProgress(std::string name);  // Actualiza el progreso de carga
    void initProgressBar();                        // Inicializa los rectangulos que definen la barra de progreso
    void updateProgressBar();                      // Actualiza la barra de estado

    // --- Helpers del cargador incremental (cargan un único recurso) ---
    void loadOneSound(size_t idx);
    void loadOneMusic(size_t idx);
    void loadOneTexture(size_t idx);
    void loadOneTextFile(size_t idx);
    void loadOneAnimation(size_t idx);
    void loadOneDemoData(size_t idx);

    // --- Constructores y destructor privados (singleton) ---
    explicit Resource(LoadingMode mode);  // Constructor privado con modo de carga
    ~Resource();                          // Destructor privado

    // --- Instancia singleton ---
    static Resource* instance;  // Instancia única de Resource
};