#include "game/scenes/slides_scene.hpp"

#include <algorithm>
#include <cstdlib>
#include <cstring>

#include "core/audio/audio.hpp"
#include "core/jail/jdraw8.hpp"
#include "core/jail/jinput.hpp"
#include "game/info.hpp"
#include "game/scenes/scene_utils.hpp"
#include "utils/easing.hpp"

namespace {

    constexpr int SCROLL_MS = 1600;  // 80 iters × 20 ms del vell doSlides
    constexpr int HOLD_MS = 4600;    // 230 iters × 20 ms (80 + 150) del vell
    constexpr int SLIDE_Y = 65;
    constexpr int SLIDE_H = 65;
    constexpr int BG_COLOR_INDEX = 255;

    // Desplaçament inicial del slide segons direcció del wipe.
    // Slide 1 i 3: "scroll in from right" (pos_x va de 320 → 0).
    // Slide 2: "wipe reverse" (pos_x va de -320 → 0), el mateix efecte
    // estrany del doSlides vell on la imatge es desplaça des de l'esquerra
    // però revela primer el lateral dret del src.
    constexpr int SLIDE_START_X[3] = {320, -320, 320};

}  // namespace

namespace scenes {

    SlidesScene::~SlidesScene() {
        delete[] pal_aux_;
        // pal_active_ NO s'allibera: propietat de main_palette via SetScreenPalette.
    }

    void SlidesScene::onEnter() {
        num_piramide_at_start_ = info::ctx.num_piramide;

        const char* arxiu = nullptr;
        if (num_piramide_at_start_ == 7) {
            // loop=1 per replicar el vell `play_music("final.ogg", 1)`.
            playMusic("music/final.ogg", 1);
            arxiu = (info::ctx.diners < 200) ? "gfx/intro2.gif" : "gfx/intro3.gif";
        } else {
            arxiu = "gfx/intro.gif";
        }

        gfx_ = SurfaceHandle(arxiu);
        pal_aux_ = Jd8::loadPalette(arxiu);

        // Còpia editable de la paleta. `pal_active_` comparteix memòria amb
        // main_palette després del SetScreenPalette — modificar-la modifica
        // main_palette directament. `pal_aux_` es manté intacte per a poder
        // restaurar després de cada fade-out intermedi.
        pal_active_ = new Color[256];
        std::memcpy(pal_active_, pal_aux_, 768);
        Jd8::setScreenPalette(pal_active_);

        Jd8::clearScreen(BG_COLOR_INDEX);

        phase_ = Phase::SLIDE1_ENTER;
        phase_acc_ms_ = 0;
        next_state_ = 0;
    }

    void SlidesScene::drawSlide(int slide_idx, int pos_x) {
        const int src_y = slide_idx * SLIDE_H;

        // Clipping manual: translada un rect de 320×65 des de (pos_x, SLIDE_Y)
        // a l'àrea visible (0..319, SLIDE_Y..SLIDE_Y+64).
        int dst_x = pos_x;
        int src_x = 0;
        int w = 320;

        if (dst_x < 0) {
            src_x = -dst_x;
            w = 320 + dst_x;
            dst_x = 0;
        } else if (dst_x > 0) {
            w = 320 - dst_x;
        }

        if (w > 0) {
            Jd8::blit(dst_x, SLIDE_Y, gfx_, src_x, src_y, w, SLIDE_H);
        }
    }

    void SlidesScene::restorePalette() {
        std::memcpy(pal_active_, pal_aux_, 768);
    }

    void SlidesScene::beginFinalFade() {
        if (num_piramide_at_start_ != 7) {
            Audio::get()->fadeOutMusic(250);
        }
        fade_.startFadeOut();
        phase_ = Phase::FADE_FINAL;
    }

    void SlidesScene::tick(int delta_ms) {
        // Skip: qualsevol tecla salta directament al fade final. Per fidelitat
        // al vell doSlides, el skip NO atura la música explícitament — només
        // el final natural crida Ja::fadeOutMusic (beginFinalFade() distingeix).
        if (!skip_triggered_ && Ji::anyKey()) {
            skip_triggered_ = true;
            if (num_piramide_at_start_ != 7) {
                Audio::get()->fadeOutMusic(250);
            }
            fade_.startFadeOut();
            phase_ = Phase::FADE_FINAL;
        }

        switch (phase_) {
            case Phase::SLIDE1_ENTER:
            case Phase::SLIDE2_ENTER:
            case Phase::SLIDE3_ENTER: {
                phase_acc_ms_ += delta_ms;
                int slide_idx = 2;
                if (phase_ == Phase::SLIDE1_ENTER) {
                    slide_idx = 0;
                } else if (phase_ == Phase::SLIDE2_ENTER) {
                    slide_idx = 1;
                }
                const float t = std::min(1.0F, static_cast<float>(phase_acc_ms_) / static_cast<float>(SCROLL_MS));
                const float eased = Easing::outCubic(t);
                const int pos_x = Easing::lerpInt(SLIDE_START_X[slide_idx], 0, eased);
                drawSlide(slide_idx, pos_x);

                if (phase_acc_ms_ >= SCROLL_MS) {
                    // Garanteix posició final exacta (pos_x=0).
                    drawSlide(slide_idx, 0);
                    if (phase_ == Phase::SLIDE1_ENTER) {
                        phase_ = Phase::SLIDE1_HOLD;
                    } else if (phase_ == Phase::SLIDE2_ENTER) {
                        phase_ = Phase::SLIDE2_HOLD;
                    } else {
                        phase_ = Phase::SLIDE3_HOLD;
                    }
                    phase_acc_ms_ = 0;
                }
                break;
            }

            case Phase::SLIDE1_HOLD:
            case Phase::SLIDE2_HOLD:
                phase_acc_ms_ += delta_ms;
                if (phase_acc_ms_ >= HOLD_MS) {
                    fade_.startFadeOut();
                    if (phase_ == Phase::SLIDE1_HOLD) {
                        phase_ = Phase::FADE_OUT1;
                    } else {
                        phase_ = Phase::FADE_OUT2;
                    }
                    phase_acc_ms_ = 0;
                }
                break;

            case Phase::SLIDE3_HOLD:
                phase_acc_ms_ += delta_ms;
                if (phase_acc_ms_ >= HOLD_MS) {
                    beginFinalFade();
                }
                break;

            case Phase::FADE_OUT1:
            case Phase::FADE_OUT2:
                fade_.tick(delta_ms);
                if (fade_.done()) {
                    restorePalette();
                    Jd8::clearScreen(BG_COLOR_INDEX);
                    if (phase_ == Phase::FADE_OUT1) {
                        phase_ = Phase::SLIDE2_ENTER;
                    } else {
                        phase_ = Phase::SLIDE3_ENTER;
                    }
                    phase_acc_ms_ = 0;
                }
                break;

            case Phase::FADE_FINAL:
                fade_.tick(delta_ms);
                if (fade_.done()) {
                    if (num_piramide_at_start_ == 7) {
                        info::ctx.num_piramide = 8;
                        next_state_ = 1;
                    } else {
                        next_state_ = 0;
                    }
                    phase_ = Phase::DONE;
                }
                break;

            case Phase::DONE:
                break;
        }
    }

}  // namespace scenes
