#include "core/system/director.hpp"

#include <SDL3/SDL.h>
#include <sys/stat.h>  // Para mkdir, stat, S_IRWXU
#include <unistd.h>    // Para getuid

#include <cerrno>    // Para errno, EEXIST, EACCES, ENAMETOO...
#include <cstdio>    // Para printf, perror
#include <cstdlib>   // Para exit, EXIT_FAILURE, srand
#include <iostream>  // Para basic_ostream, operator<<, cout
#include <memory>    // Para make_unique, unique_ptr
#include <string>    // Para operator+, allocator, char_traits

#include "core/audio/audio.hpp"            // Para Audio
#include "core/input/input.hpp"            // Para Input, InputAction
#include "core/rendering/screen.hpp"       // Para Screen
#include "core/resources/asset.hpp"        // Para Asset, AssetType
#include "core/resources/resource.hpp"     // Para Resource
#include "core/system/debug.hpp"           // Para Debug
#include "game/gameplay/cheevos.hpp"       // Para Cheevos
#include "game/options.hpp"                // Para Options, options, OptionsVideo
#include "game/scene_manager.hpp"          // Para SceneManager
#include "game/scenes/credits.hpp"         // Para Credits
#include "game/scenes/ending.hpp"          // Para Ending
#include "game/scenes/ending2.hpp"         // Para Ending2
#include "game/scenes/game.hpp"            // Para Game, GameMode
#include "game/scenes/game_over.hpp"       // Para GameOver
#include "game/scenes/loading_screen.hpp"  // Para LoadingScreen
#include "game/scenes/logo.hpp"            // Para Logo
#include "game/scenes/title.hpp"           // Para Title
#include "game/ui/notifier.hpp"            // Para Notifier
#include "utils/defines.hpp"               // Para WINDOW_CAPTION

#ifndef _WIN32
#include <pwd.h>
#endif

// Constructor
Director::Director(std::vector<std::string> const& args) {
    std::cout << "Game start" << '\n';

    // Crea e inicializa las opciones del programa
    Options::init();

    // Comprueba los parametros del programa
    executable_path_ = checkProgramArguments(args);

    // Crea el objeto que controla los ficheros de recursos
    Asset::init(executable_path_);

    // Crea la carpeta del sistema donde guardar datos
    createSystemFolder("jailgames");
    createSystemFolder("jailgames/jaildoctors_dilemma");

    // Si falta algún fichero no inicia el programa
    if (!setFileList()) {
        exit(EXIT_FAILURE);
    }

    // Carga las opciones desde un fichero
    Options::loadFromFile(Asset::get()->get("config.txt"));

    // Inicializa JailAudio
    Audio::init();

    // Crea los objetos
    Screen::init();
    SDL_HideCursor();
    Resource::init();
    Notifier::init("", "8bithud");
    Screen::get()->setNotificationsEnabled(true);
    Input::init(Asset::get()->get("gamecontrollerdb.txt"));
    initInput();
    Debug::init();
    Cheevos::init(Asset::get()->get("cheevos.bin"));
}

Director::~Director() {
    // Guarda las opciones a un fichero
    Options::saveToFile(Asset::get()->get("config.txt"));

    // Destruye los singletones
    Cheevos::destroy();
    Debug::destroy();
    Input::destroy();
    Notifier::destroy();
    Resource::destroy();
    Audio::destroy();
    Screen::destroy();
    Asset::destroy();

    SDL_Quit();

    std::cout << "\nBye!" << '\n';
}

// Comprueba los parametros del programa
auto Director::checkProgramArguments(std::vector<std::string> const& args) -> std::string {
    // Iterar sobre los argumentos del programa (saltando args[0] que es el ejecutable)
    for (std::size_t i = 1; i < args.size(); ++i) {
        const std::string& argument = args[i];

        if (argument == "--console") {
            Options::console = true;
        } else if (argument == "--infiniteLives") {
            Options::cheats.infinite_lives = Options::Cheat::State::ENABLED;
        } else if (argument == "--invincible") {
            Options::cheats.invincible = Options::Cheat::State::ENABLED;
        } else if (argument == "--jailEnabled") {
            Options::cheats.jail_is_open = Options::Cheat::State::ENABLED;
        } else if (argument == "--altSkin") {
            Options::cheats.alternate_skin = Options::Cheat::State::ENABLED;
        }
    }

    return args[0];
}

// Crea la carpeta del sistema donde guardar datos
void Director::createSystemFolder(const std::string& folder) {
#ifdef _WIN32
    system_folder_ = std::string(getenv("APPDATA")) + "/" + folder;
#elif __APPLE__
    struct passwd* pw = getpwuid(getuid());
    const char* homedir = pw->pw_dir;
    system_folder_ = std::string(homedir) + "/Library/Application Support" + "/" + folder;
#elif __linux__
    struct passwd* pw = getpwuid(getuid());
    const char* homedir = pw->pw_dir;
    system_folder_ = std::string(homedir) + "/.config/" + folder;

    {
        // Intenta crear ".config", per si no existeix
        std::string config_base_folder = std::string(homedir) + "/.config";
        int ret = mkdir(config_base_folder.c_str(), S_IRWXU);
        if (ret == -1 && errno != EEXIST) {
            printf("ERROR CREATING CONFIG BASE FOLDER.");
            exit(EXIT_FAILURE);
        }
    }
#endif

    struct stat st = {.st_dev = 0};
    if (stat(system_folder_.c_str(), &st) == -1) {
        errno = 0;
#ifdef _WIN32
        int ret = mkdir(system_folder_.c_str());
#else
        int ret = mkdir(system_folder_.c_str(), S_IRWXU);
#endif

        if (ret == -1) {
            switch (errno) {
                case EACCES:
                    printf("the parent directory does not allow write");
                    exit(EXIT_FAILURE);

                case EEXIST:
                    printf("pathname already exists");
                    exit(EXIT_FAILURE);

                case ENAMETOOLONG:
                    printf("pathname is too long");
                    exit(EXIT_FAILURE);

                default:
                    perror("mkdir");
                    exit(EXIT_FAILURE);
            }
        }
    }
}

// Inicia las variables necesarias para arrancar el programa
void Director::initInput() {
    // Busca si hay un mando conectado
    Input::get()->discoverGameControllers();

    // Teclado - Movimiento
    if (Options::keys == Options::ControlScheme::CURSOR) {
        Input::get()->bindKey(InputAction::JUMP, SDL_SCANCODE_UP);
        Input::get()->bindKey(InputAction::LEFT, SDL_SCANCODE_LEFT);
        Input::get()->bindKey(InputAction::RIGHT, SDL_SCANCODE_RIGHT);
        Input::get()->bindKey(InputAction::UP, SDL_SCANCODE_UP);
        Input::get()->bindKey(InputAction::DOWN, SDL_SCANCODE_DOWN);
    } else if (Options::keys == Options::ControlScheme::OPQA) {
        Input::get()->bindKey(InputAction::JUMP, SDL_SCANCODE_Q);
        Input::get()->bindKey(InputAction::LEFT, SDL_SCANCODE_O);
        Input::get()->bindKey(InputAction::RIGHT, SDL_SCANCODE_P);
        Input::get()->bindKey(InputAction::UP, SDL_SCANCODE_Q);
        Input::get()->bindKey(InputAction::DOWN, SDL_SCANCODE_A);
    } else if (Options::keys == Options::ControlScheme::WASD) {
        Input::get()->bindKey(InputAction::JUMP, SDL_SCANCODE_W);
        Input::get()->bindKey(InputAction::LEFT, SDL_SCANCODE_A);
        Input::get()->bindKey(InputAction::RIGHT, SDL_SCANCODE_D);
        Input::get()->bindKey(InputAction::UP, SDL_SCANCODE_W);
        Input::get()->bindKey(InputAction::DOWN, SDL_SCANCODE_S);
    }

    // Teclado - Otros
    Input::get()->bindKey(InputAction::ACCEPT, SDL_SCANCODE_RETURN);
    Input::get()->bindKey(InputAction::CANCEL, SDL_SCANCODE_ESCAPE);
    Input::get()->bindKey(InputAction::PAUSE, SDL_SCANCODE_H);
    Input::get()->bindKey(InputAction::EXIT, SDL_SCANCODE_ESCAPE);
    Input::get()->bindKey(InputAction::WINDOW_DEC_ZOOM, SDL_SCANCODE_F1);
    Input::get()->bindKey(InputAction::WINDOW_INC_ZOOM, SDL_SCANCODE_F2);
    Input::get()->bindKey(InputAction::TOGGLE_VIDEOMODE, SDL_SCANCODE_F3);
    Input::get()->bindKey(InputAction::TOGGLE_SHADERS, SDL_SCANCODE_F4);
    Input::get()->bindKey(InputAction::NEXT_PALETTE, SDL_SCANCODE_F5);
    Input::get()->bindKey(InputAction::PREVIOUS_PALETTE, SDL_SCANCODE_F6);
    Input::get()->bindKey(InputAction::TOGGLE_INTEGER_SCALE, SDL_SCANCODE_F7);
    Input::get()->bindKey(InputAction::SHOW_DEBUG_INFO, SDL_SCANCODE_F12);
    Input::get()->bindKey(InputAction::TOGGLE_MUSIC, SDL_SCANCODE_M);
    Input::get()->bindKey(InputAction::TOGGLE_BORDER, SDL_SCANCODE_B);

    // MANDO
    const int NUM_GAMEPADS = Input::get()->getNumControllers();
    for (int i = 0; i < NUM_GAMEPADS; ++i) {
        // Movimiento
        Input::get()->bindGameControllerButton(i, InputAction::JUMP, SDL_GAMEPAD_BUTTON_SOUTH);
        Input::get()->bindGameControllerButton(i, InputAction::LEFT, SDL_GAMEPAD_BUTTON_DPAD_LEFT);
        Input::get()->bindGameControllerButton(i, InputAction::RIGHT, SDL_GAMEPAD_BUTTON_DPAD_RIGHT);

        // Otros
        Input::get()->bindGameControllerButton(i, InputAction::ACCEPT, SDL_GAMEPAD_BUTTON_SOUTH);
        Input::get()->bindGameControllerButton(i, InputAction::CANCEL, SDL_GAMEPAD_BUTTON_EAST);
#ifdef GAME_CONSOLE
        Input::get()->bindGameControllerButton(i, InputAction::input_pause, SDL_GAMEPAD_BUTTON_BACK);
        Input::get()->bindGameControllerButton(i, InputAction::input_exit, SDL_GAMEPAD_BUTTON_START);
#else
        Input::get()->bindGameControllerButton(i, InputAction::PAUSE, SDL_GAMEPAD_BUTTON_START);
        Input::get()->bindGameControllerButton(i, InputAction::EXIT, SDL_GAMEPAD_BUTTON_BACK);
#endif
        Input::get()->bindGameControllerButton(i, InputAction::NEXT_PALETTE, SDL_GAMEPAD_BUTTON_LEFT_SHOULDER);
        Input::get()->bindGameControllerButton(i, InputAction::TOGGLE_MUSIC, SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER);
        Input::get()->bindGameControllerButton(i, InputAction::TOGGLE_BORDER, SDL_GAMEPAD_BUTTON_NORTH);
    }
}

// Crea el indice de ficheros
auto Director::setFileList() -> bool {
#ifdef MACOS_BUNDLE
    const std::string prefix = "/../Resources";
#else
    const std::string PREFIX;
#endif

    // Texto
    Asset::get()->add(PREFIX + "/data/font/smb2.gif", AssetType::BITMAP);
    Asset::get()->add(PREFIX + "/data/font/smb2.txt", AssetType::FONT);
    Asset::get()->add(PREFIX + "/data/font/debug.gif", AssetType::BITMAP);
    Asset::get()->add(PREFIX + "/data/font/debug.txt", AssetType::FONT);
    Asset::get()->add(PREFIX + "/data/font/gauntlet.gif", AssetType::BITMAP);
    Asset::get()->add(PREFIX + "/data/font/gauntlet.txt", AssetType::FONT);
    Asset::get()->add(PREFIX + "/data/font/subatomic.gif", AssetType::BITMAP);
    Asset::get()->add(PREFIX + "/data/font/subatomic.txt", AssetType::FONT);
    Asset::get()->add(PREFIX + "/data/font/8bithud.gif", AssetType::BITMAP);
    Asset::get()->add(PREFIX + "/data/font/8bithud.txt", AssetType::FONT);

    // Paletas
    Asset::get()->add(PREFIX + "/data/palette/zx-spectrum.pal", AssetType::PALETTE);
    Asset::get()->add(PREFIX + "/data/palette/zx-spectrum-adjusted.pal", AssetType::PALETTE);
    Asset::get()->add(PREFIX + "/data/palette/zxarne-5-2.pal", AssetType::PALETTE);
    Asset::get()->add(PREFIX + "/data/palette/black-and-white.pal", AssetType::PALETTE);
    Asset::get()->add(PREFIX + "/data/palette/green-phosphor.pal", AssetType::PALETTE);
    Asset::get()->add(PREFIX + "/data/palette/orange-screen.pal", AssetType::PALETTE);
    Asset::get()->add(PREFIX + "/data/palette/ruzx-spectrum.pal", AssetType::PALETTE);
    Asset::get()->add(PREFIX + "/data/palette/ruzx-spectrum-revision-2.pal", AssetType::PALETTE);
    Asset::get()->add(PREFIX + "/data/palette/pico-8.pal", AssetType::PALETTE);
    Asset::get()->add(PREFIX + "/data/palette/sweetie-16.pal", AssetType::PALETTE);
    Asset::get()->add(PREFIX + "/data/palette/island-joy-16.pal", AssetType::PALETTE);
    Asset::get()->add(PREFIX + "/data/palette/lost-century.pal", AssetType::PALETTE);
    Asset::get()->add(PREFIX + "/data/palette/na16.pal", AssetType::PALETTE);
    Asset::get()->add(PREFIX + "/data/palette/steam-lords.pal", AssetType::PALETTE);

    // Shaders
    Asset::get()->add(PREFIX + "/data/shaders/crtpi_vertex.glsl", AssetType::DATA);
    Asset::get()->add(PREFIX + "/data/shaders/crtpi_fragment.glsl", AssetType::DATA);
    Asset::get()->add(PREFIX + "/data/shaders/crtpi_vertex_es.glsl", AssetType::DATA);
    Asset::get()->add(PREFIX + "/data/shaders/crtpi_fragment_es.glsl", AssetType::DATA);

    // Datos
    Asset::get()->add(PREFIX + "/data/input/gamecontrollerdb.txt", AssetType::DATA);

    // Ficheros de sistema
    Asset::get()->add(system_folder_ + "/config.txt", AssetType::DATA, false, true);
    Asset::get()->add(system_folder_ + "/stats_buffer.csv", AssetType::DATA, false, true);
    Asset::get()->add(system_folder_ + "/stats.csv", AssetType::DATA, false, true);
    Asset::get()->add(system_folder_ + "/cheevos.bin", AssetType::DATA, false, true);

    // Tilemaps y Rooms
    for (int i = 1; i <= 60; ++i) {
        std::string index = (i < 10 ? "0" : "") + std::to_string(i);
        Asset::get()->add(PREFIX + "/data/room/" + index + ".tmx", AssetType::TILEMAP);
        Asset::get()->add(PREFIX + "/data/room/" + index + ".room", AssetType::ROOM);
    }

    // Tilesets
    Asset::get()->add(PREFIX + "/data/tilesets/standard.gif", AssetType::BITMAP);

    // Enemigos
    Asset::get()->add(PREFIX + "/data/enemies/abad_bell.ani", AssetType::ANIMATION);
    Asset::get()->add(PREFIX + "/data/enemies/abad_bell.gif", AssetType::BITMAP);
    Asset::get()->add(PREFIX + "/data/enemies/abad.ani", AssetType::ANIMATION);
    Asset::get()->add(PREFIX + "/data/enemies/abad.gif", AssetType::BITMAP);
    Asset::get()->add(PREFIX + "/data/enemies/amstrad_cs.ani", AssetType::ANIMATION);
    Asset::get()->add(PREFIX + "/data/enemies/amstrad_cs.gif", AssetType::BITMAP);
    Asset::get()->add(PREFIX + "/data/enemies/flying_arounder.ani", AssetType::ANIMATION);
    Asset::get()->add(PREFIX + "/data/enemies/flying_arounder.gif", AssetType::BITMAP);
    Asset::get()->add(PREFIX + "/data/enemies/stopped_arounder.ani", AssetType::ANIMATION);
    Asset::get()->add(PREFIX + "/data/enemies/stopped_arounder.gif", AssetType::BITMAP);
    Asset::get()->add(PREFIX + "/data/enemies/walking_arounder.ani", AssetType::ANIMATION);
    Asset::get()->add(PREFIX + "/data/enemies/walking_arounder.gif", AssetType::BITMAP);
    Asset::get()->add(PREFIX + "/data/enemies/arounders_door.ani", AssetType::ANIMATION);
    Asset::get()->add(PREFIX + "/data/enemies/arounders_door.gif", AssetType::BITMAP);
    Asset::get()->add(PREFIX + "/data/enemies/arounders_machine.ani", AssetType::ANIMATION);
    Asset::get()->add(PREFIX + "/data/enemies/arounders_machine.gif", AssetType::BITMAP);
    Asset::get()->add(PREFIX + "/data/enemies/bat.ani", AssetType::ANIMATION);
    Asset::get()->add(PREFIX + "/data/enemies/bat.gif", AssetType::BITMAP);
    Asset::get()->add(PREFIX + "/data/enemies/batman_bell.ani", AssetType::ANIMATION);
    Asset::get()->add(PREFIX + "/data/enemies/batman_bell.gif", AssetType::BITMAP);
    Asset::get()->add(PREFIX + "/data/enemies/batman_fire.ani", AssetType::ANIMATION);
    Asset::get()->add(PREFIX + "/data/enemies/batman_fire.gif", AssetType::BITMAP);
    Asset::get()->add(PREFIX + "/data/enemies/batman.ani", AssetType::ANIMATION);
    Asset::get()->add(PREFIX + "/data/enemies/batman.gif", AssetType::BITMAP);
    Asset::get()->add(PREFIX + "/data/enemies/bell.ani", AssetType::ANIMATION);
    Asset::get()->add(PREFIX + "/data/enemies/bell.gif", AssetType::BITMAP);
    Asset::get()->add(PREFIX + "/data/enemies/bin.ani", AssetType::ANIMATION);
    Asset::get()->add(PREFIX + "/data/enemies/bin.gif", AssetType::BITMAP);
    Asset::get()->add(PREFIX + "/data/enemies/bird.ani", AssetType::ANIMATION);
    Asset::get()->add(PREFIX + "/data/enemies/bird.gif", AssetType::BITMAP);
    Asset::get()->add(PREFIX + "/data/enemies/breakout.ani", AssetType::ANIMATION);
    Asset::get()->add(PREFIX + "/data/enemies/breakout.gif", AssetType::BITMAP);
    Asset::get()->add(PREFIX + "/data/enemies/bry.ani", AssetType::ANIMATION);
    Asset::get()->add(PREFIX + "/data/enemies/bry.gif", AssetType::BITMAP);
    Asset::get()->add(PREFIX + "/data/enemies/chip.ani", AssetType::ANIMATION);
    Asset::get()->add(PREFIX + "/data/enemies/chip.gif", AssetType::BITMAP);
    Asset::get()->add(PREFIX + "/data/enemies/code.ani", AssetType::ANIMATION);
    Asset::get()->add(PREFIX + "/data/enemies/code.gif", AssetType::BITMAP);
    Asset::get()->add(PREFIX + "/data/enemies/congo.ani", AssetType::ANIMATION);
    Asset::get()->add(PREFIX + "/data/enemies/congo.gif", AssetType::BITMAP);
    Asset::get()->add(PREFIX + "/data/enemies/crosshair.ani", AssetType::ANIMATION);
    Asset::get()->add(PREFIX + "/data/enemies/crosshair.gif", AssetType::BITMAP);
    Asset::get()->add(PREFIX + "/data/enemies/demon.ani", AssetType::ANIMATION);
    Asset::get()->add(PREFIX + "/data/enemies/demon.gif", AssetType::BITMAP);
    Asset::get()->add(PREFIX + "/data/enemies/dimallas.ani", AssetType::ANIMATION);
    Asset::get()->add(PREFIX + "/data/enemies/dimallas.gif", AssetType::BITMAP);
    Asset::get()->add(PREFIX + "/data/enemies/floppy.ani", AssetType::ANIMATION);
    Asset::get()->add(PREFIX + "/data/enemies/floppy.gif", AssetType::BITMAP);
    Asset::get()->add(PREFIX + "/data/enemies/dong.ani", AssetType::ANIMATION);
    Asset::get()->add(PREFIX + "/data/enemies/dong.gif", AssetType::BITMAP);
    Asset::get()->add(PREFIX + "/data/enemies/guitar.ani", AssetType::ANIMATION);
    Asset::get()->add(PREFIX + "/data/enemies/guitar.gif", AssetType::BITMAP);
    Asset::get()->add(PREFIX + "/data/enemies/heavy.ani", AssetType::ANIMATION);
    Asset::get()->add(PREFIX + "/data/enemies/heavy.gif", AssetType::BITMAP);
    Asset::get()->add(PREFIX + "/data/enemies/jailer_#1.ani", AssetType::ANIMATION);
    Asset::get()->add(PREFIX + "/data/enemies/jailer_#1.gif", AssetType::BITMAP);
    Asset::get()->add(PREFIX + "/data/enemies/jailer_#2.ani", AssetType::ANIMATION);
    Asset::get()->add(PREFIX + "/data/enemies/jailer_#2.gif", AssetType::BITMAP);
    Asset::get()->add(PREFIX + "/data/enemies/jailer_#3.ani", AssetType::ANIMATION);
    Asset::get()->add(PREFIX + "/data/enemies/jailer_#3.gif", AssetType::BITMAP);
    Asset::get()->add(PREFIX + "/data/enemies/jailbattle_alien.ani", AssetType::ANIMATION);
    Asset::get()->add(PREFIX + "/data/enemies/jailbattle_alien.gif", AssetType::BITMAP);
    Asset::get()->add(PREFIX + "/data/enemies/jailbattle_human.ani", AssetType::ANIMATION);
    Asset::get()->add(PREFIX + "/data/enemies/jailbattle_human.gif", AssetType::BITMAP);
    Asset::get()->add(PREFIX + "/data/enemies/jeannine.ani", AssetType::ANIMATION);
    Asset::get()->add(PREFIX + "/data/enemies/jeannine.gif", AssetType::BITMAP);
    Asset::get()->add(PREFIX + "/data/enemies/lamp.ani", AssetType::ANIMATION);
    Asset::get()->add(PREFIX + "/data/enemies/lamp.gif", AssetType::BITMAP);
    Asset::get()->add(PREFIX + "/data/enemies/lord_abad.ani", AssetType::ANIMATION);
    Asset::get()->add(PREFIX + "/data/enemies/lord_abad.gif", AssetType::BITMAP);
    Asset::get()->add(PREFIX + "/data/enemies/matatunos.ani", AssetType::ANIMATION);
    Asset::get()->add(PREFIX + "/data/enemies/matatunos.gif", AssetType::BITMAP);
    Asset::get()->add(PREFIX + "/data/enemies/mummy.ani", AssetType::ANIMATION);
    Asset::get()->add(PREFIX + "/data/enemies/mummy.gif", AssetType::BITMAP);
    Asset::get()->add(PREFIX + "/data/enemies/paco.ani", AssetType::ANIMATION);
    Asset::get()->add(PREFIX + "/data/enemies/paco.gif", AssetType::BITMAP);
    Asset::get()->add(PREFIX + "/data/enemies/elsa.ani", AssetType::ANIMATION);
    Asset::get()->add(PREFIX + "/data/enemies/elsa.gif", AssetType::BITMAP);
    Asset::get()->add(PREFIX + "/data/enemies/qvoid.ani", AssetType::ANIMATION);
    Asset::get()->add(PREFIX + "/data/enemies/qvoid.gif", AssetType::BITMAP);
    Asset::get()->add(PREFIX + "/data/enemies/robot.ani", AssetType::ANIMATION);
    Asset::get()->add(PREFIX + "/data/enemies/robot.gif", AssetType::BITMAP);
    Asset::get()->add(PREFIX + "/data/enemies/sam.ani", AssetType::ANIMATION);
    Asset::get()->add(PREFIX + "/data/enemies/sam.gif", AssetType::BITMAP);
    Asset::get()->add(PREFIX + "/data/enemies/shock.ani", AssetType::ANIMATION);
    Asset::get()->add(PREFIX + "/data/enemies/shock.gif", AssetType::BITMAP);
    Asset::get()->add(PREFIX + "/data/enemies/sigmasua.ani", AssetType::ANIMATION);
    Asset::get()->add(PREFIX + "/data/enemies/sigmasua.gif", AssetType::BITMAP);
    Asset::get()->add(PREFIX + "/data/enemies/spark.ani", AssetType::ANIMATION);
    Asset::get()->add(PREFIX + "/data/enemies/spark.gif", AssetType::BITMAP);
    Asset::get()->add(PREFIX + "/data/enemies/special/aerojailer.ani", AssetType::ANIMATION);
    Asset::get()->add(PREFIX + "/data/enemies/special/aerojailer.gif", AssetType::BITMAP);
    Asset::get()->add(PREFIX + "/data/enemies/special/arounder.ani", AssetType::ANIMATION);
    Asset::get()->add(PREFIX + "/data/enemies/special/arounder.gif", AssetType::BITMAP);
    Asset::get()->add(PREFIX + "/data/enemies/special/pepe_rosita_job.ani", AssetType::ANIMATION);
    Asset::get()->add(PREFIX + "/data/enemies/special/pepe_rosita_job.gif", AssetType::BITMAP);
    Asset::get()->add(PREFIX + "/data/enemies/special/shooting_star.ani", AssetType::ANIMATION);
    Asset::get()->add(PREFIX + "/data/enemies/special/shooting_star.gif", AssetType::BITMAP);
    Asset::get()->add(PREFIX + "/data/enemies/spider.ani", AssetType::ANIMATION);
    Asset::get()->add(PREFIX + "/data/enemies/spider.gif", AssetType::BITMAP);
    Asset::get()->add(PREFIX + "/data/enemies/tree_thing.ani", AssetType::ANIMATION);
    Asset::get()->add(PREFIX + "/data/enemies/tree_thing.gif", AssetType::BITMAP);
    Asset::get()->add(PREFIX + "/data/enemies/tuno.ani", AssetType::ANIMATION);
    Asset::get()->add(PREFIX + "/data/enemies/tuno.gif", AssetType::BITMAP);
    Asset::get()->add(PREFIX + "/data/enemies/tv_panel.ani", AssetType::ANIMATION);
    Asset::get()->add(PREFIX + "/data/enemies/tv_panel.gif", AssetType::BITMAP);
    Asset::get()->add(PREFIX + "/data/enemies/tv.ani", AssetType::ANIMATION);
    Asset::get()->add(PREFIX + "/data/enemies/tv.gif", AssetType::BITMAP);
    Asset::get()->add(PREFIX + "/data/enemies/upv_student.ani", AssetType::ANIMATION);
    Asset::get()->add(PREFIX + "/data/enemies/upv_student.gif", AssetType::BITMAP);
    Asset::get()->add(PREFIX + "/data/enemies/wave.ani", AssetType::ANIMATION);
    Asset::get()->add(PREFIX + "/data/enemies/wave.gif", AssetType::BITMAP);
    Asset::get()->add(PREFIX + "/data/enemies/z80.ani", AssetType::ANIMATION);
    Asset::get()->add(PREFIX + "/data/enemies/z80.gif", AssetType::BITMAP);

    // Jugador
    Asset::get()->add(PREFIX + "/data/player/player.gif", AssetType::BITMAP);
    Asset::get()->add(PREFIX + "/data/player/player.ani", AssetType::ANIMATION);
    Asset::get()->add(PREFIX + "/data/player/player2.gif", AssetType::BITMAP);
    Asset::get()->add(PREFIX + "/data/player/player2.ani", AssetType::ANIMATION);
    Asset::get()->add(PREFIX + "/data/player/player_game_over.gif", AssetType::BITMAP);
    Asset::get()->add(PREFIX + "/data/player/player_game_over.ani", AssetType::ANIMATION);

    // Items
    Asset::get()->add(PREFIX + "/data/items/items.gif", AssetType::BITMAP);

    // Musicas
    Asset::get()->add(PREFIX + "/data/music/title.ogg", AssetType::MUSIC);
    Asset::get()->add(PREFIX + "/data/music/game.ogg", AssetType::MUSIC);
    Asset::get()->add(PREFIX + "/data/music/loading_sound1.ogg", AssetType::MUSIC);
    Asset::get()->add(PREFIX + "/data/music/loading_sound2.ogg", AssetType::MUSIC);
    Asset::get()->add(PREFIX + "/data/music/loading_sound3.ogg", AssetType::MUSIC);
    Asset::get()->add(PREFIX + "/data/music/ending1.ogg", AssetType::MUSIC);
    Asset::get()->add(PREFIX + "/data/music/ending2.ogg", AssetType::MUSIC);
    Asset::get()->add(PREFIX + "/data/music/game_over.ogg", AssetType::MUSIC);

    // Efectos de sonido
    Asset::get()->add(PREFIX + "/data/sound/item.wav", AssetType::SOUND);
    Asset::get()->add(PREFIX + "/data/sound/death.wav", AssetType::SOUND);
    Asset::get()->add(PREFIX + "/data/sound/notify.wav", AssetType::SOUND);

    // Efectos de sonido para el salto
    for (int i = 1; i <= 24; ++i) {
        std::string jump_index = std::to_string(i);
        Asset::get()->add(PREFIX + "/data/sound/jump" + jump_index + ".wav", AssetType::SOUND);
    }

    // Logo
    Asset::get()->add(PREFIX + "/data/logo/jailgames.gif", AssetType::BITMAP);
    Asset::get()->add(PREFIX + "/data/logo/since_1998.gif", AssetType::BITMAP);

    // Loading
    Asset::get()->add(PREFIX + "/data/loading/loading_screen_bn.gif", AssetType::BITMAP);
    Asset::get()->add(PREFIX + "/data/loading/loading_screen_color.gif", AssetType::BITMAP);

    // Title
    Asset::get()->add(PREFIX + "/data/title/title_logo.gif", AssetType::BITMAP);

    // Ending
    Asset::get()->add(PREFIX + "/data/ending/ending1.gif", AssetType::BITMAP);
    Asset::get()->add(PREFIX + "/data/ending/ending2.gif", AssetType::BITMAP);
    Asset::get()->add(PREFIX + "/data/ending/ending3.gif", AssetType::BITMAP);
    Asset::get()->add(PREFIX + "/data/ending/ending4.gif", AssetType::BITMAP);
    Asset::get()->add(PREFIX + "/data/ending/ending5.gif", AssetType::BITMAP);

    // Credits
    Asset::get()->add(PREFIX + "/data/credits/shine.gif", AssetType::BITMAP);
    Asset::get()->add(PREFIX + "/data/credits/shine.ani", AssetType::ANIMATION);

    return Asset::get()->check();
}

// Ejecuta la seccion de juego con el logo
void Director::runLogo() {
    auto logo = std::make_unique<Logo>();
    logo->run();
}

// Ejecuta la seccion de juego de la pantalla de carga
void Director::runLoadingScreen() {
    auto loading_screen = std::make_unique<LoadingScreen>();
    loading_screen->run();
}

// Ejecuta la seccion de juego con el titulo y los menus
void Director::runTitle() {
    auto title = std::make_unique<Title>();
    title->run();
}

// Ejecuta la seccion de los creditos del juego
void Director::runCredits() {
    auto credits = std::make_unique<Credits>();
    credits->run();
}

// Ejecuta la seccion de la demo, donde se ven pantallas del juego
void Director::runDemo() {
    auto game = std::make_unique<Game>(GameMode::DEMO);
    game->run();
}

// Ejecuta la seccion del final del juego
void Director::runEnding() {
    auto ending = std::make_unique<Ending>();
    ending->run();
}

// Ejecuta la seccion del final del juego
void Director::runEnding2() {
    auto ending2 = std::make_unique<Ending2>();
    ending2->run();
}

// Ejecuta la seccion del final de la partida
void Director::runGameOver() {
    auto game_over = std::make_unique<GameOver>();
    game_over->run();
}

// Ejecuta la seccion de juego donde se juega
void Director::runGame() {
    Audio::get()->stopMusic();
    auto game = std::make_unique<Game>(GameMode::GAME);
    game->run();
}

auto Director::run() -> int {
    // Bucle principal
    while (SceneManager::current != SceneManager::Scene::QUIT) {
        switch (SceneManager::current) {
            case SceneManager::Scene::LOGO:
                runLogo();
                break;

            case SceneManager::Scene::LOADING_SCREEN:
                runLoadingScreen();
                break;

            case SceneManager::Scene::TITLE:
                runTitle();
                break;

            case SceneManager::Scene::CREDITS:
                runCredits();
                break;

            case SceneManager::Scene::DEMO:
                runDemo();
                break;

            case SceneManager::Scene::GAME:
                runGame();
                break;

            case SceneManager::Scene::GAME_OVER:
                runGameOver();
                break;

            case SceneManager::Scene::ENDING:
                runEnding();
                break;

            case SceneManager::Scene::ENDING2:
                runEnding2();
                break;

            default:
                break;
        }
    }

    return 0;
}