#include "game/scenes/intro_new_logo_scene.hpp"

#include <cstring>

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

namespace {

    // Coordenades mesurades del wordmark "Jailgames" dins gfx/logo_new.gif.
    // Si canvies el logo, aquí i al GIF són els únics llocs a tocar.
    constexpr int LOGO_SRC_X = 60;
    constexpr int LOGO_SRC_Y = 158;
    constexpr int LOGO_DST_Y = 78;
    constexpr int LOGO_HEIGHT = 28;
    constexpr int LETTER_WIDTHS[9] = {16, 39, 50, 69, 92, 115, 146, 169, 188};
    // El logo vell es pintava a dst_x == LOGO_SRC_X = 60, cosa que el deixava
    // 6 px fora de centre (320 − 188) / 2 = 66. Ho corregim amb un shift
    // comú aplicat tant al blit del logo com als CURSOR_X de sota.
    constexpr int LOGO_DST_X = (320 - LETTER_WIDTHS[8]) / 2;  // 66
    constexpr int CENTER_SHIFT = LOGO_DST_X - LOGO_SRC_X;     // +6
    constexpr int CURSOR_X[9] = {77 + CENTER_SHIFT, 100 + CENTER_SHIFT, 111 + CENTER_SHIFT, 130 + CENTER_SHIFT, 153 + CENTER_SHIFT, 176 + CENTER_SHIFT, 207 + CENTER_SHIFT, 230 + CENTER_SHIFT, 249 + CENTER_SHIFT};
    constexpr int CURSOR_W = 12;
    constexpr int CURSOR_H = 3;
    constexpr int CURSOR_Y = LOGO_DST_Y + LOGO_HEIGHT - CURSOR_H;  // y = 103
    constexpr Uint8 CURSOR_COLOR = 17;                             // mateix index verd que les lletres

    // Timings (ms) — idèntics als de doIntroNewLogo vell.
    constexpr int INITIAL_MS = 1000;
    constexpr int REVEAL_FRAME_MS = 150;
    constexpr int FULL_LOGO_MS = 200;
    constexpr int PALETTE_CYCLE_STEP_MS = 20;
    constexpr int FINAL_WAIT_MS = 20;
    constexpr int PALETTE_CYCLE_STEPS = 256;

}  // namespace

namespace Scenes {

    IntroNewLogoScene::IntroNewLogoScene() = default;

    // No alliberem `pal_`: Jd8::setScreenPalette n'ha pres ownership i el
    // proper SetScreenPalette / FadeToPal el lliurarà. Alliberar-lo ací
    // provocaria double free.
    IntroNewLogoScene::~IntroNewLogoScene() = default;

    void IntroNewLogoScene::onEnter() {
        playMusic("music/menu.ogg");

        gfx_ = SurfaceHandle("gfx/logo_new.gif");
        pal_ = Jd8::loadPalette("gfx/logo_new.gif");
        Jd8::setScreenPalette(pal_);

        // Surface auxiliar omplida amb el color del cursor — permet pintar
        // el "subratllat" amb un blit normal.
        cursor_surf_.adopt(Jd8::newSurface());
        std::memset(cursor_surf_.get(), CURSOR_COLOR, 64000);

        Jd8::clearScreen(0);

        phase_ = Phase::INITIAL;
        phase_acc_ms_ = 0;
        reveal_letter_ = 0;
        reveal_cursor_visible_ = true;
        palette_step_ = 0;
    }

    void IntroNewLogoScene::render() {
        switch (phase_) {
            case Phase::INITIAL:
                Jd8::clearScreen(0);
                break;

            case Phase::REVEALING: {
                Jd8::clearScreen(0);
                Jd8::blit(LOGO_DST_X, LOGO_DST_Y, gfx_, LOGO_SRC_X, LOGO_SRC_Y, LETTER_WIDTHS[reveal_letter_], LOGO_HEIGHT);
                if (reveal_cursor_visible_) {
                    Jd8::blit(CURSOR_X[reveal_letter_], CURSOR_Y, cursor_surf_, 0, 0, CURSOR_W, CURSOR_H);
                }
                break;
            }

            case Phase::FULL_LOGO_FLASH:
                Jd8::clearScreen(0);
                Jd8::blit(LOGO_DST_X, LOGO_DST_Y, gfx_, LOGO_SRC_X, LOGO_SRC_Y, LETTER_WIDTHS[8], LOGO_HEIGHT);
                Jd8::blit(CURSOR_X[8], CURSOR_Y, cursor_surf_, 0, 0, CURSOR_W, CURSOR_H);
                break;

            case Phase::PALETTE_CYCLE:
            case Phase::FINAL_WAIT:
                // Logo complet sense cursor — els pixels del cursor
                // ciclarien de color durant el cicle de paleta.
                Jd8::clearScreen(0);
                Jd8::blit(LOGO_DST_X, LOGO_DST_Y, gfx_, LOGO_SRC_X, LOGO_SRC_Y, LETTER_WIDTHS[8], LOGO_HEIGHT);
                break;

            case Phase::SPRITES:
            case Phase::DONE:
                break;
        }
    }

    void IntroNewLogoScene::advancePaletteCycle() {
        // Replica exacta del ciclo de paleta del doIntroNewLogo vell sobre
        // els índexs 16..31 (grup del verd brillant del logo).
        for (int i = 16; i < 32; i++) {
            if (i == 17) {
                if (pal_[i].r < 255) {
                    pal_[i].r++;
                }
                if (pal_[i].g < 255) {
                    pal_[i].g++;
                }
                if (pal_[i].b < 255) {
                    pal_[i].b++;
                }
            }
            if (pal_[i].b < pal_[i].g) {
                pal_[i].b++;
            }
            if (pal_[i].b > pal_[i].g) {
                pal_[i].b--;
            }
            if (pal_[i].r < pal_[i].g) {
                pal_[i].r++;
            }
            if (pal_[i].r > pal_[i].g) {
                pal_[i].r--;
            }
        }
    }

    void IntroNewLogoScene::advanceRevealing(int delta_ms) {
        phase_acc_ms_ += delta_ms;
        render();
        if (phase_acc_ms_ < REVEAL_FRAME_MS) {
            return;
        }
        phase_acc_ms_ = 0;
        reveal_cursor_visible_ = !reveal_cursor_visible_;
        if (!reveal_cursor_visible_) {
            return;
        }
        ++reveal_letter_;
        if (reveal_letter_ >= 9) {
            phase_ = Phase::FULL_LOGO_FLASH;
            reveal_letter_ = 8;
        }
    }

    void IntroNewLogoScene::advancePaletteStep(int delta_ms) {
        phase_acc_ms_ += delta_ms;
        while (phase_acc_ms_ >= PALETTE_CYCLE_STEP_MS && palette_step_ < PALETTE_CYCLE_STEPS) {
            phase_acc_ms_ -= PALETTE_CYCLE_STEP_MS;
            advancePaletteCycle();
            ++palette_step_;
        }
        render();
        if (palette_step_ >= PALETTE_CYCLE_STEPS) {
            phase_ = Phase::FINAL_WAIT;
            phase_acc_ms_ = 0;
        }
    }

    void IntroNewLogoScene::advanceSpritesPhase(int delta_ms) {
        if (!sprites_scene_) {
            sprites_scene_ = std::make_unique<IntroSpritesScene>(std::move(gfx_));
            sprites_scene_->onEnter();
        }
        sprites_scene_->tick(delta_ms);
        if (sprites_scene_->done()) {
            Info::ctx.num_piramide = 0;
            phase_ = Phase::DONE;
        }
    }

    void IntroNewLogoScene::tick(int delta_ms) {
        // Qualsevol tecla durant el revelat o el ciclo de paleta salta
        // TOTA la intro (inclou saltar la fase de sprites). Durant SPRITES
        // deixem que la sub-escena gestione el seu propi skip.
        if (phase_ != Phase::SPRITES && phase_ != Phase::DONE && Ji::anyKey()) {
            Info::ctx.num_piramide = 0;
            phase_ = Phase::DONE;
            return;
        }
        switch (phase_) {
            case Phase::INITIAL:
                phase_acc_ms_ += delta_ms;
                render();
                if (phase_acc_ms_ >= INITIAL_MS) {
                    phase_ = Phase::REVEALING;
                    phase_acc_ms_ = 0;
                }
                break;
            case Phase::REVEALING:
                advanceRevealing(delta_ms);
                break;
            case Phase::FULL_LOGO_FLASH:
                phase_acc_ms_ += delta_ms;
                render();
                if (phase_acc_ms_ >= FULL_LOGO_MS) {
                    phase_ = Phase::PALETTE_CYCLE;
                    phase_acc_ms_ = 0;
                }
                break;
            case Phase::PALETTE_CYCLE:
                advancePaletteStep(delta_ms);
                break;
            case Phase::FINAL_WAIT:
                phase_acc_ms_ += delta_ms;
                render();
                if (phase_acc_ms_ >= FINAL_WAIT_MS) {
                    phase_ = Phase::SPRITES;
                }
                break;
            case Phase::SPRITES:
                advanceSpritesPhase(delta_ms);
                break;
            case Phase::DONE:
                break;
        }
    }

}  // namespace Scenes
