#pragma once

#include <memory>              // for shared_ptr
#include <string>              // for string
#include <vector>              // for vector
#include "s_animated_sprite.h" // for AnimationsFileBuffer
#include "room.h"              // for room_t
#include "text.h"              // for Text, TextFile
#include "surface.h"           // for Surface
struct JA_Music_t;             // lines 11-11
struct JA_Sound_t;             // lines 12-12

// Estructura para almacenar ficheros de sonido y su nombre
struct ResourceSound
{
    std::string name;  // Nombre del sonido
    JA_Sound_t *sound; // Objeto con el sonido

    // Constructor
    ResourceSound(const std::string &name, JA_Sound_t *sound)
        : name(name), sound(sound) {}
};

// Estructura para almacenar ficheros musicales y su nombre
struct ResourceMusic
{
    std::string name;  // Nombre de la musica
    JA_Music_t *music; // Objeto con la música

    // Constructor
    ResourceMusic(const std::string &name, JA_Music_t *music)
        : name(name), music(music) {}
};

// Estructura para almacenar objetos Surface y su nombre
struct ResourceSurface
{
    std::string name;                 // Nombre de la surface
    std::shared_ptr<Surface> surface; // Objeto con la surface

    // Constructor
    ResourceSurface(const std::string &name, std::shared_ptr<Surface> surface)
        : name(name), surface(surface) {}
};

// Estructura para almacenar objetos Palette y su nombre
struct ResourcePalette
{
    std::string name; // Nombre de la surface
    Palette palette;  // Paleta

    // Constructor
    ResourcePalette(const std::string &name, Palette palette)
        : name(name), palette(palette) {}
};

// Estructura para almacenar ficheros TextFile y su nombre
struct ResourceTextFile
{
    std::string name;                    // Nombre del fichero
    std::shared_ptr<TextFile> text_file; // Objeto con los descriptores de la fuente de texto

    // Constructor
    ResourceTextFile(const std::string &name, std::shared_ptr<TextFile> text_file)
        : name(name), text_file(text_file) {}
};

// Estructura para almacenar objetos Text y su nombre
struct ResourceText
{
    std::string name;           // Nombre del objeto
    std::shared_ptr<Text> text; // Objeto

    // Constructor
    ResourceText(const std::string &name, std::shared_ptr<Text> text)
        : name(name), text(text) {}
};

// Estructura para almacenar ficheros animaciones y su nombre
struct ResourceAnimation
{
    std::string name;     // Nombre del fichero
    Animations animation; // Objeto con las animaciones

    // Constructor
    ResourceAnimation(const std::string &name, const Animations &animation)
        : name(name), animation(animation) {}
};

// Estructura para almacenar ficheros con el mapa de tiles de una habitación y su nombre
struct ResourceTileMap
{
    std::string name;         // Nombre del mapa de tiles
    std::vector<int> tileMap; // Vector con los indices del mapa de tiles

    // Constructor
    ResourceTileMap(const std::string &name, const std::vector<int> &tileMap)
        : name(name), tileMap(tileMap) {}
};

// Estructura para almacenar habitaciones y su nombre
struct ResourceRoom
{
    std::string name;               // Nombre de la habitación
    std::shared_ptr<RoomData> room; // Habitación

    // Constructor
    ResourceRoom(const std::string &name, std::shared_ptr<RoomData> room)
        : name(name), room(room) {}
};

// Estructura para llevar la cuenta de los recursos cargados
struct ResourceCount
{
    int total;  // Número total de recursos
    int loaded; // Número de recursos cargados

    // Constructor
    ResourceCount()
        : total(0), loaded(0) {}

    // Constructor
    ResourceCount(int total, int loaded)
        : total(total), loaded(loaded) {}

    // Añade una cantidad a los recursos cargados
    void add(int amount)
    {
        loaded += amount;
    }

    // Obtiene el porcentaje de recursos cargados
    float getPercentage()
    {
        return static_cast<float>(loaded) / static_cast<float>(total);
    }
};

class Resource
{
private:
    // [SINGLETON] Objeto resource privado para Don Melitón
    static Resource *resource_;

    std::vector<ResourceSound> sounds_;         // Vector con los sonidos
    std::vector<ResourceMusic> musics_;         // Vector con las musicas
    std::vector<ResourceSurface> surfaces_;     // Vector con las surfaces
    std::vector<ResourcePalette> palettes_;      // Vector con las paletas
    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<ResourceTileMap> tile_maps_;    // Vector con los mapas de tiles
    std::vector<ResourceRoom> rooms_;           // Vector con las habitaciones

    ResourceCount count_; // Contador de recursos

    // Carga los sonidos
    void loadSounds();

    // Carga las musicas
    void loadMusics();

    // Carga las surfaces
    void loadSurfaces();

    // Carga las paletas
    void loadPalettes();

    // Carga los ficheros de texto
    void loadTextFiles();

    // Carga las animaciones
    void loadAnimations();

    // Carga los mapas de tiles
    void loadTileMaps();

    // Carga las habitaciones
    void loadRooms();

    // Crea los objetos de texto
    void createText();

    // Vacia todos los vectores de recursos
    void clear();

    // Carga todos los recursos
    void load();

    // Vacía el vector de sonidos
    void clearSounds();

    // Vacía el vector de musicas
    void clearMusics();

    // Calcula el numero de recursos para cargar
    void calculateTotal();

    // Muestra el progreso de carga
    void renderProgress();

    // Comprueba los eventos
    void checkEvents();

    // Actualiza el progreso de carga
    void updateLoadingProgress();

    // [SINGLETON] Ahora el constructor y el destructor son privados, para no poder crear objetos resource desde fuera

    // Constructor
    Resource();

    // Destructor
    ~Resource() = default;

public:
    // [SINGLETON] Crearemos el objeto resource con esta función estática
    static void init();

    // [SINGLETON] Destruiremos el objeto resource con esta función estática
    static void destroy();

    // [SINGLETON] Con este método obtenemos el objeto resource y podemos trabajar con él
    static Resource *get();

    // Obtiene el sonido a partir de un nombre
    JA_Sound_t *getSound(const std::string &name);

    // Obtiene la música a partir de un nombre
    JA_Music_t *getMusic(const std::string &name);

    // Obtiene la surface a partir de un nombre
    std::shared_ptr<Surface> getSurface(const std::string &name);

    // Obtiene la paleta a partir de un nombre
    Palette getPalette(const std::string &name);

    // Obtiene el fichero de texto a partir de un nombre
    std::shared_ptr<TextFile> getTextFile(const std::string &name);

    // Obtiene el objeto de texto a partir de un nombre
    std::shared_ptr<Text> getText(const std::string &name);

    // Obtiene la animación a partir de un nombre
    Animations &getAnimations(const std::string &name);

    // Obtiene el mapa de tiles a partir de un nombre
    std::vector<int> &getTileMap(const std::string &name);

    // Obtiene la habitación a partir de un nombre
    std::shared_ptr<RoomData> getRoom(const std::string &name);

    // Obtiene todas las habitaciones
    std::vector<ResourceRoom> &getRooms();

    // Recarga todos los recursos
    void reload();
};