#include "game/modulegame.hpp"

#include <algorithm>

#include "core/audio/audio.hpp"
#include "core/jail/jdraw8.hpp"
#include "core/jail/jgame.hpp"
#include "core/jail/jinput.hpp"

ModuleGame::ModuleGame() {
    this->gfx_ = Jd8::loadSurface(Info::ctx.pepe_activat ? "gfx/frames2.gif" : "gfx/frames.gif");
    Jg::setUpdateTicks(10);

    this->sam_ = std::make_unique<Prota>(this->gfx_);
    this->mapa_ = std::make_unique<Mapa>(this->gfx_, this->sam_.get());
    this->marcador_ = std::make_unique<Marcador>(this->gfx_, this->sam_.get());
    if (Info::ctx.num_piramide == 2) {
        this->bola_ = std::make_unique<Bola>(this->gfx_, this->sam_.get());
    }

    this->iniciarMomies();
}

ModuleGame::~ModuleGame() {
    Jd8::freeSurface(this->gfx_);
}

void ModuleGame::onEnter() {
    // Primera Draw per omplir `screen` amb el contingut del gameplay
    // abans que el fade-in arranque. Si no, les primeres iteracions del
    // fade interpolarien cap a una paleta amb pantalla buida.
    this->draw();

    // Audio::playMusic ja és idempotent: si la pista actual coincideix amb la
    // demanada, no fa res. Per això podem cridar-lo cada onEnter sense
    // desencadenar restarts indesitjats.
    const char* music_name = "piramide_1_4_5.ogg";
    if (Info::ctx.num_piramide == 3) {
        music_name = "piramide_3.ogg";
    } else if (Info::ctx.num_piramide == 2) {
        music_name = "piramide_2.ogg";
    } else if (Info::ctx.num_piramide == 6) {
        music_name = "secreta.ogg";
    }
    Audio::get()->playMusic(music_name);

    // Arranca el fade-in tick-based. El `PaletteFade` avança un pas (de
    // 32) per cada tick; durant aquesta fase el gameplay no corre,
    // només Draw+fade. Substituïx la crida bloquejant `JD8_FadeToPal`.
    fade_.startFadeTo(Jd8::loadPalette(Info::ctx.pepe_activat ? "gfx/frames2.gif" : "gfx/frames.gif"));
    phase_ = Phase::FADING_IN;
}

void ModuleGame::tick(int delta_ms) {
    switch (phase_) {
        case Phase::FADING_IN:
            // No redibuixem durant el fade: el `screen` ja va ser omplit
            // per la Draw() d'onEnter. Només el Jd8::flip del caller muta
            // pixel_data segons la paleta que avança pas a pas.
            fade_.tick(delta_ms);
            if (fade_.done()) {
                phase_ = Phase::PLAYING;
            }
            break;

        case Phase::PLAYING:
            this->draw();
            this->update();
            if (this->final_ != 0) {
                this->applyFinalTransitions();
                fade_.startFadeOut();
                phase_ = Phase::FADING_OUT;
            }
            break;

        case Phase::FADING_OUT:
            // No redibuixem: el `screen` té l'últim frame pintat per la
            // fase Playing (just abans que Update() setegés `final_`).
            // El vell `JD8_FadeOut` feia exactament això — flips amb
            // paleta fading però sense tocar el buffer. Redibuixar ací
            // mostraria l'estat post-Update del sprite (p.ex. el prota
            // "tornant" davant la porta després d'haver eixit).
            fade_.tick(delta_ms);
            if (fade_.done()) {
                phase_ = Phase::DONE;
            }
            break;

        case Phase::DONE:
            break;
    }
}

auto ModuleGame::nextState() const -> int {
    if (Jg::quitting()) {
        return -1;
    }
    if (Info::ctx.num_habitacio == 1 ||
        Info::ctx.num_piramide == 100 ||
        Info::ctx.num_piramide == 7) {
        return 1;
    }
    return 0;
}

void ModuleGame::applyFinalTransitions() const {
    if (this->final_ == 1) {
        Info::ctx.num_habitacio++;
        if (Info::ctx.num_habitacio == 6) {
            Info::ctx.num_habitacio = 1;
            Info::ctx.num_piramide++;
        }
        if (Info::ctx.num_piramide == 6 && Info::ctx.num_habitacio == 2) {
            Info::ctx.num_piramide++;
        }
    } else if (this->final_ == 2) {
        Info::ctx.num_piramide = 100;
    }
}

void ModuleGame::draw() {
    // No crida Jd8::flip — el caller (mini-loop del fiber, o Director a
    // Phase B.2) ho fa després de cada tick.
    this->mapa_->draw();
    this->marcador_->draw();
    this->sam_->draw();
    for (auto& m : this->momies_) {
        m->draw();
    }
    if (this->bola_) {
        this->bola_->draw();
    }
}

void ModuleGame::update() {
    if (Jg::shouldUpdate()) {
        Ji::update();

        this->final_ = this->sam_->update();
        const auto ERASED = std::erase_if(this->momies_, [](auto& m) { return m->update(); });
        Info::ctx.momies -= static_cast<int>(ERASED);
        if (this->bola_) {
            this->bola_->update();
        }
        this->mapa_->update();
        if (this->mapa_->novaMomia()) {
            this->momies_.emplace_back(std::make_unique<Momia>(this->gfx_, true, 0, 0, this->sam_.get()));
            Info::ctx.momies++;
        }

        if (Ji::cheatActivated("reviu")) {
            Info::ctx.vida = 5;
        }
        if (Ji::cheatActivated("alone")) {
            this->momies_.clear();
            Info::ctx.momies = 0;
        }
        if (Ji::cheatActivated("obert")) {
            for (int i = 0; i < 16; i++) {
                this->mapa_->tombes[i].costat[0] = true;
                this->mapa_->tombes[i].costat[1] = true;
                this->mapa_->tombes[i].costat[2] = true;
                this->mapa_->tombes[i].costat[3] = true;
                this->mapa_->comprovaCaixa(i);
            }
        }

        if (Ji::keyPressed(SDL_SCANCODE_ESCAPE)) {
            Jg::quitSignal();
        }
    }
}

void ModuleGame::iniciarMomies() {
    if (Info::ctx.num_habitacio == 1) {
        Info::ctx.momies = 1;
    } else {
        Info::ctx.momies++;
    }
    if (Info::ctx.num_piramide == 6) {
        Info::ctx.momies = 8;
    }

    int x = 20;
    int y = 170;
    bool dimonis = Info::ctx.num_piramide == 6;
    for (int i = 0; i < Info::ctx.momies; i++) {
        this->momies_.emplace_back(std::make_unique<Momia>(this->gfx_, dimonis, x, y, this->sam_.get()));
        x += 65;
        if (x == 345) {
            x = 20;
            y -= 35;
        }
    }
}
