// Port a l'API de callbacks de SDL3: el runtime posseïx el main loop i ens
// crida a SDL_AppInit/SDL_AppIterate/SDL_AppEvent/SDL_AppQuit. Imprescindible
// per al port a emscripten on no podem tindre un bucle while propi al hilo
// principal. Funciona igual en build natiu (Linux/macOS/Windows) perquè
// SDL3 embolcalla el seu propi main loop darrere d'aquestes callbacks.

#define SDL_MAIN_USE_CALLBACKS
#include <SDL3/SDL.h>
#include <SDL3/SDL_main.h>

#include <ctime>
#include <string>

#include "core/audio/audio.hpp"
#include "core/input/key_config.hpp"
#include "core/jail/jdraw8.hpp"
#include "core/jail/jfile.hpp"
#include "core/jail/jgame.hpp"
#include "core/locale/locale.hpp"
#include "core/rendering/menu.hpp"
#include "core/rendering/overlay.hpp"
#include "core/rendering/screen.hpp"
#include "core/resources/resource_cache.hpp"
#include "core/resources/resource_helper.hpp"
#include "core/resources/resource_list.hpp"
#include "core/system/director.hpp"
#include "game/options.hpp"

auto SDL_AppInit(void** /*appstate*/, int /*argc*/, char* /*argv*/[]) -> SDL_AppResult {
    srand(unsigned(time(nullptr)));

    // Crea la carpeta de configuració i carrega les opcions
    Jf::setConfigFolder("jailgames/aee");

    // Ruta absoluta a data/ basada en la ubicació de l'executable.
    // SDL_GetBasePath() detecta automàticament si estem dins d'un .app bundle
    // (retorna Contents/Resources/) o en un executable normal (carpeta del binari).
    const char* base_path = SDL_GetBasePath();
    std::string resource_pack_path;
    if (base_path != nullptr) {
        const std::string DATA_PATH = std::string(base_path) + "data/";
        Jf::setResourceFolder(DATA_PATH.c_str());
        resource_pack_path = std::string(base_path) + "resources.pack";
    } else {
        resource_pack_path = "resources.pack";
    }

    // Sistema de recursos: prova el pack i cau a fitxers solts dins data/.
    // Release natiu exigix el pack (sense fallback); Debug i WASM mantenen
    // el fallback actiu per a desenvolupament i per al build amb MEMFS.
#if defined(NDEBUG) && !defined(__EMSCRIPTEN__)
    const bool ENABLE_FALLBACK = false;
#else
    const bool ENABLE_FALLBACK = true;
#endif
    if (!ResourceHelper::initializeResourceSystem(resource_pack_path, ENABLE_FALLBACK)) {
        return SDL_APP_FAILURE;
    }

    Options::setConfigFile(std::string(Jf::getConfigFolder()) + "config.yaml");
    Options::loadFromFile();

    // KeyConfig: defaults des de data/input/keys.yaml + overrides de l'usuari
    KeyConfig::init("input/keys.yaml",
        std::string(Jf::getConfigFolder()) + "keys.yaml");

#ifndef NDEBUG
    // debug.yaml: estat inicial de gameplay per a tests ràpids,
    // només en builds de debug.
    Options::setDebugFile(std::string(Jf::getConfigFolder()) + "debug.yaml");
    Options::loadDebugFromFile();
#endif

#ifdef __EMSCRIPTEN__
    // MEMFS no persistix entre recàrregues: força valors sensats per a web.
    Options::window.fullscreen = false;
    Options::window.zoom = 3;
    Options::video.aspect_ratio_4_3 = true;
    Options::video.scaling_mode = Options::ScalingMode::INTEGER;
    Options::video.texture_filter = Options::TextureFilter::LINEAR;
#endif

    // Carrega textos (idioma per defecte: valencià)
    Locale::load("locale/ca.yaml");

    // Carrega presets de shaders
    Options::setPostFXFile(std::string(Jf::getConfigFolder()) + "postfx.yaml");
    Options::loadPostFXFromFile();
    Options::setCrtPiFile(std::string(Jf::getConfigFolder()) + "crtpi.yaml");
    Options::loadCrtPiFromFile();

    Jg::init();
    Screen::init();
    Jd8::init();
    Audio::init();  // crida internament Ja::init i aplica Options::audio
    Overlay::init();
    Menu::init();

    // Manifest d'assets (data/config/assets.yaml) + Cache. La precarga
    // real es fa al BootLoaderScene, que el Director arrenca automàticament
    // mentre `Resource::Cache::isLoadDone()` siga fals.
    Resource::List::init("config/assets.yaml");
    Resource::Cache::init();
    Resource::Cache::get()->beginLoad();

    Director::init();
    Director::get()->setup();

    return SDL_APP_CONTINUE;
}

auto SDL_AppIterate(void* /*appstate*/) -> SDL_AppResult {
    // Una iteració del bucle del Director. Abans els events es drenaven
    // amb SDL_PollEvent dins d'aquesta funció; ara SDL ens els lliura
    // d'un en un via SDL_AppEvent, així que iterate() no els toca.
    if (!Director::get()->iterate()) {
        return SDL_APP_SUCCESS;
    }
    return SDL_APP_CONTINUE;
}

auto SDL_AppEvent(void* /*appstate*/, SDL_Event* event) -> SDL_AppResult {
    if (event == nullptr) {
        return SDL_APP_CONTINUE;
    }
    Director::get()->handleEvent(*event);
    if (Director::get()->isQuitRequested()) {
        return SDL_APP_SUCCESS;
    }
    return SDL_APP_CONTINUE;
}

void SDL_AppQuit(void* /*appstate*/, SDL_AppResult /*result*/) {
    // Neteja en ordre invers al de SDL_AppInit.
    Director::get()->teardown();

    Options::saveToFile();
    KeyConfig::saveOverrides();
#ifndef NDEBUG
    Options::saveDebugToFile();
#endif

    Director::destroy();
    KeyConfig::destroy();
    Menu::destroy();
    Overlay::destroy();
    Resource::Cache::destroy();
    Resource::List::destroy();
    Audio::destroy();  // el destructor del singleton crida Ja::quit
    Jd8::quit();
    Screen::destroy();
    Jg::finalize();
    ResourceHelper::shutdownResourceSystem();
}
