#include "core/rendering/overlay.hpp"

#include <algorithm>
#include <memory>
#include <string>
#include <vector>

#include "core/locale/locale.hpp"
#include "core/rendering/menu.hpp"
#include "core/rendering/text.hpp"
#include "core/system/director.hpp"
#include "game/options.hpp"
#include "utils/easing.hpp"

namespace Overlay {

    static std::unique_ptr<Text> font;

    // --- Aspecte de la notificació ---
    static constexpr Uint32 NOTIF_BG_COLOR = 0xFF2E1A1A;    // Fons blau fosc (ABGR)
    static constexpr Uint32 NOTIF_TEXT_COLOR = 0xFFFFFF00;  // Text cyan (ABGR)
    static constexpr int NOTIF_PADDING_H = 8;               // Padding horitzontal (esquerra/dreta dins la caixa)
    static constexpr int NOTIF_PADDING_V = 4;               // Padding vertical (dalt/baix dins la caixa)
    static constexpr int NOTIF_MARGIN_X = 4;                // Offset des de la vora esquerra de la pantalla
    static constexpr int NOTIF_MARGIN_Y = 4;                // Offset des de la vora superior de la pantalla

    // --- Animació ---
    static constexpr float SLIDE_SPEED = 4.0F;  // Velocitat de l'animació (unitats/segon)

    // --- Pantalla ---
    static constexpr int SCREEN_W = 320;
    static constexpr int SCREEN_H = 200;

    // --- Estat de les notificacions ---

    enum class Status : std::uint8_t { RISING,
        STAY,
        VANISHING,
        FINISHED };

    struct Notification {
        std::vector<std::string> lines;  // una o més línies de text
        Overlay::NotifPosition pos{Overlay::NotifPosition::TOP_LEFT_SLIDE};
        Overlay::NotifStyle style{Overlay::NotifStyle::BOX};
        Uint32 accent_color{NOTIF_BG_COLOR};  // fons (BOX) o ombra (SHADOW)
        Uint32 text_color{NOTIF_TEXT_COLOR};
        Status status{Status::RISING};
        float anim{0.0F};  // 0 = fora de pantalla, 1 = posició final
        float timer{0.0F};
        float duration{2.0F};
        int box_w{0};  // Ample de la caixa (calculat al crear)
        int box_h{0};  // Alçada de la caixa (calculat al crear)
    };

    static std::vector<Notification> notifications;
    static Uint32 last_ticks = 0;

    // --- Render info ---
    static Options::RenderInfoPosition info_visible_pos = Options::RenderInfoPosition::OFF;
    static float info_anim = 0.0F;  // 0 = fora de pantalla, 1 = posició final
    static constexpr float INFO_SLIDE_SPEED = 5.0F;

    // Segments del render info — cadascú amb la seva pròpia visibilitat animada
    static constexpr int INFO_SEGMENT_COUNT = 4;
    static constexpr float SEG_SPEED = 6.0F;  // ~165 ms per aparèixer/desaparèixer
    struct InfoSegment {
        std::string text;
        float anim{0.0F};
        bool visible{false};
        bool mono_digits{false};  // si true, dígits amb amplada fixa (la resta natural)
    };
    static InfoSegment info_segments[INFO_SEGMENT_COUNT];

    // --- Crèdits cinematogràfics ---
    // Usen el sistema de notificacions en posició TOP_CENTER_DROP.
    enum class CreditsPhase : std::uint8_t { IDLE,
        DELAY,
        PLAYING_1,
        GAP,
        PLAYING_2 };
    static CreditsPhase credits_phase = CreditsPhase::IDLE;
    static float credits_timer = 0.0F;  // segons dins la phase actual
    static constexpr float CREDITS_DELAY = 2.0F;
    static constexpr float CREDITS_GAP = 0.4F;
    static constexpr float CREDITS_HOLD = 7.5F;
    static constexpr Uint32 CREDITS_BG = NOTIF_BG_COLOR;    // mateix marró de les notificacions
    static constexpr Uint32 CREDITS_FG = NOTIF_TEXT_COLOR;  // mateix cian

    // --- Doble ESC per a eixir ---
    static bool esc_waiting = false;

    void init() {
        font = std::make_unique<Text>("fonts/8bithud.fnt", "fonts/8bithud.gif");
        last_ticks = SDL_GetTicks();
    }

    void destroy() {
        font.reset();
        notifications.clear();
    }

    // Pinta un rectangle sòlid dins els límits de la pantalla
    static void drawRect(Uint32* pixel_data, int rx, int ry, int rw, int rh, Uint32 color) {
        for (int row = ry; row < ry + rh; row++) {
            if (row < 0 || row >= SCREEN_H) {
                continue;
            }
            for (int col = rx; col < rx + rw; col++) {
                if (col < 0 || col >= SCREEN_W) {
                    continue;
                }
                pixel_data[col + (row * SCREEN_W)] = color;
            }
        }
    }

    void render(Uint32* pixel_data) {
        if (!font || (pixel_data == nullptr)) {
            return;
        }

        // Calcula delta time
        Uint32 now = SDL_GetTicks();
        float dt = static_cast<float>(now - last_ticks) / 1000.0F;
        last_ticks = now;

        // Actualitza i pinta cada notificació
        for (auto& notif : notifications) {
            switch (notif.status) {
                case Status::RISING:
                    notif.anim += SLIDE_SPEED * dt;
                    if (notif.anim >= 1.0F) {
                        notif.anim = 1.0F;
                        notif.status = Status::STAY;
                        notif.timer = 0.0F;
                    }
                    break;

                case Status::STAY:
                    notif.timer += dt;
                    if (notif.timer >= notif.duration) {
                        notif.status = Status::VANISHING;
                    }
                    break;

                case Status::VANISHING:
                    notif.anim -= SLIDE_SPEED * dt;
                    if (notif.anim <= 0.0F) {
                        notif.status = Status::FINISHED;
                    }
                    break;

                case Status::FINISHED:
                    break;
            }

            if (notif.status == Status::FINISHED) {
                continue;
            }

            // Posició segons el tipus
            int box_x = 0;
            int box_y = 0;
            switch (notif.pos) {
                case NotifPosition::TOP_LEFT_SLIDE: {
                    int target_x = NOTIF_MARGIN_X;
                    int target_y = NOTIF_MARGIN_Y;
                    box_x = target_x - static_cast<int>((1.0F - notif.anim) * (notif.box_w + NOTIF_MARGIN_X));
                    box_y = target_y;
                    break;
                }
                case NotifPosition::TOP_CENTER_DROP: {
                    int target_y = NOTIF_MARGIN_Y;
                    box_x = (SCREEN_W - notif.box_w) / 2;
                    // Baixa des de sobre de la pantalla fins a target_y
                    box_y = target_y - static_cast<int>((1.0F - notif.anim) * (notif.box_h + NOTIF_MARGIN_Y));
                    break;
                }
            }

            // Pinta fons (si BOX)
            if (notif.style == NotifStyle::BOX) {
                drawRect(pixel_data, box_x, box_y, notif.box_w, notif.box_h, notif.accent_color);
            }

            // Pinta el text línia a línia (amb ombra o contorn segons style)
            int line_h = font->charHeight();
            int line_y = box_y + NOTIF_PADDING_V;
            for (const auto& line : notif.lines) {
                int line_w = font->width(line.c_str());
                int line_x = box_x + ((notif.box_w - line_w) / 2);  // centrat dins la caixa
                if (notif.style == NotifStyle::SHADOW) {
                    font->draw(pixel_data, line_x + 1, line_y + 1, line.c_str(), notif.accent_color);
                } else if (notif.style == NotifStyle::OUTLINE) {
                    // Contorn 4-direccional (N, S, E, W)
                    font->draw(pixel_data, line_x, line_y - 1, line.c_str(), notif.accent_color);
                    font->draw(pixel_data, line_x, line_y + 1, line.c_str(), notif.accent_color);
                    font->draw(pixel_data, line_x - 1, line_y, line.c_str(), notif.accent_color);
                    font->draw(pixel_data, line_x + 1, line_y, line.c_str(), notif.accent_color);
                }
                font->draw(pixel_data, line_x, line_y, line.c_str(), notif.text_color);
                line_y += line_h + 1;
            }
        }

        // Render info (FPS, driver, shader) — animat amb slide vertical
        // State machine: visible_pos s'actualitza cap a DESIRED quan anim arriba a 0
        {
            const auto DESIRED = Options::render_info.position;
            if (DESIRED == info_visible_pos) {
                // Mateix lloc: entra fins a 1
                if (info_anim < 1.0F) {
                    info_anim += INFO_SLIDE_SPEED * dt;
                    info_anim = std::min(info_anim, 1.0F);
                }
            } else {
                // Canvi: si visible_pos està OFF, commuta directament
                if (info_visible_pos == Options::RenderInfoPosition::OFF) {
                    info_visible_pos = DESIRED;
                    info_anim = 0.0F;
                } else {
                    // Ix del lloc actual
                    info_anim -= INFO_SLIDE_SPEED * dt;
                    if (info_anim <= 0.0F) {
                        info_anim = 0.0F;
                        info_visible_pos = DESIRED;
                    }
                }
            }

            // Actualitza animacions individuals dels segments
            for (auto& seg : info_segments) {
                float target = seg.visible ? 1.0F : 0.0F;
                if (seg.anim < target) {
                    seg.anim += SEG_SPEED * dt;
                    seg.anim = std::min(seg.anim, target);
                } else if (seg.anim > target) {
                    seg.anim -= SEG_SPEED * dt;
                    seg.anim = std::max(seg.anim, target);
                }
            }

            // Render si hi ha alguna cosa visible
            if (info_visible_pos != Options::RenderInfoPosition::OFF && info_anim > 0.0F) {
                const int DIGIT_CELL = font->charBoxWidth() - 1;  // amplada uniforme per dígit

                // Calcula amplada total interpolant cada segment per la seva anim
                float total_w = 0.0F;
                for (const auto& seg : info_segments) {
                    if (seg.anim > 0.0F && !seg.text.empty()) {
                        int w = seg.mono_digits
                            ? font->widthMonoDigits(seg.text.c_str(), DIGIT_CELL)
                            : font->width(seg.text.c_str());
                        total_w += w * Easing::outQuad(seg.anim);
                    }
                }
                if (total_w > 0.0F) {
                    float eased_y = Easing::outQuad(info_anim);
                    int ch = font->charHeight();
                    int final_y;
                    int start_y;
                    if (info_visible_pos == Options::RenderInfoPosition::TOP) {
                        final_y = 1;
                        start_y = -ch - 1;
                    } else {
                        final_y = SCREEN_H - ch - 1;
                        start_y = SCREEN_H;
                    }
                    int info_y = start_y + static_cast<int>((final_y - start_y) * eased_y);

                    // Dibuixa cada segment en la seva posició x acumulada
                    float cur_x = (SCREEN_W - total_w) / 2.0F;
                    for (const auto& seg : info_segments) {
                        if (seg.anim > 0.01F && !seg.text.empty()) {
                            int xi = static_cast<int>(cur_x);
                            int seg_w = seg.mono_digits
                                ? font->widthMonoDigits(seg.text.c_str(), DIGIT_CELL)
                                : font->width(seg.text.c_str());
                            if (seg.mono_digits) {
                                font->drawMonoDigits(pixel_data, xi + 1, info_y + 1, seg.text.c_str(), Options::render_info.shadow_color, DIGIT_CELL);
                                font->drawMonoDigits(pixel_data, xi, info_y, seg.text.c_str(), Options::render_info.text_color, DIGIT_CELL);
                            } else {
                                font->draw(pixel_data, xi + 1, info_y + 1, seg.text.c_str(), Options::render_info.shadow_color);
                                font->draw(pixel_data, xi, info_y, seg.text.c_str(), Options::render_info.text_color);
                            }
                            cur_x += seg_w * Easing::outQuad(seg.anim);
                        }
                    }
                }
            }
        }

        // Elimina les acabades
        std::erase_if(notifications, [](const Notification& n) { return n.status == Status::FINISHED; });

        // Si la notificació d'ESC ha desaparegut, reseteja l'estat
        if (esc_waiting && notifications.empty()) {
            esc_waiting = false;
        }

        // Indicador de pausa persistent (cantó superior dret)
        if ((Director::get() != nullptr) && Director::get()->isPaused()) {
            const char* pause_text = Locale::get("notifications.pause");
            int w = font->width(pause_text);
            int x = SCREEN_W - w - 4;
            int y = 4;
            // Contorn blanc 4-direccional
            font->draw(pixel_data, x, y - 1, pause_text, 0xFFFFFFFF);
            font->draw(pixel_data, x, y + 1, pause_text, 0xFFFFFFFF);
            font->draw(pixel_data, x - 1, y, pause_text, 0xFFFFFFFF);
            font->draw(pixel_data, x + 1, y, pause_text, 0xFFFFFFFF);
            // Text en roig
            font->draw(pixel_data, x, y, pause_text, 0xFF0000FF);
        }

        // Crèdits seqüencials — dispara notificacions TOP_CENTER_DROP una darrere l'altra.
        if (credits_phase != CreditsPhase::IDLE) {
            credits_timer += dt;
            switch (credits_phase) {
                case CreditsPhase::DELAY:
                    if (credits_timer >= CREDITS_DELAY) {
                        showNotification(
                            {std::string(Locale::get("credits.port_role")),
                                std::string(Locale::get("credits.port_name"))},
                            CREDITS_HOLD,
                            NotifPosition::TOP_CENTER_DROP,
                            NotifStyle::OUTLINE,
                            CREDITS_BG,
                            CREDITS_FG);
                        credits_phase = CreditsPhase::PLAYING_1;
                        credits_timer = 0.0F;
                    }
                    break;
                case CreditsPhase::PLAYING_1:
                    if (notifications.empty()) {
                        credits_phase = CreditsPhase::GAP;
                        credits_timer = 0.0F;
                    }
                    break;
                case CreditsPhase::GAP:
                    if (credits_timer >= CREDITS_GAP) {
                        showNotification(
                            {std::string(Locale::get("credits.modern_role")),
                                std::string(Locale::get("credits.modern_name"))},
                            CREDITS_HOLD,
                            NotifPosition::TOP_CENTER_DROP,
                            NotifStyle::OUTLINE,
                            CREDITS_BG,
                            CREDITS_FG);
                        credits_phase = CreditsPhase::PLAYING_2;
                        credits_timer = 0.0F;
                    }
                    break;
                case CreditsPhase::PLAYING_2:
                    if (notifications.empty()) {
                        credits_phase = CreditsPhase::IDLE;
                        credits_timer = 0.0F;
                    }
                    break;
                case CreditsPhase::IDLE:
                    break;
            }
        }

        // Neteja notificacions finalitzades
        std::erase_if(notifications, [](const Notification& n) { return n.status == Status::FINISHED; });

        // Menú flotant per damunt de tot (isVisible inclou l'animació de tancament)
        if (Menu::isVisible()) {
            Menu::render(pixel_data);
        }
    }

    void showNotification(const char* text, float duration_seconds) {
        showNotification({std::string(text)}, duration_seconds, NotifPosition::TOP_LEFT_SLIDE, NotifStyle::BOX, NOTIF_BG_COLOR, NOTIF_TEXT_COLOR);
    }

    void showNotification(const std::vector<std::string>& lines,
        float duration_seconds,
        NotifPosition pos,
        NotifStyle style,
        Uint32 accent_color,
        Uint32 text_color) {
        // Reemplaça la notificació anterior
        notifications.clear();

        Notification notif;
        notif.lines = lines;
        notif.pos = pos;
        notif.style = style;
        notif.accent_color = accent_color;
        notif.text_color = text_color;
        notif.duration = duration_seconds;

        // Calcula l'amplada màxima de les línies
        int max_w = 0;
        for (const auto& line : lines) {
            int w = font->width(line.c_str());
            max_w = std::max(w, max_w);
        }
        notif.box_w = max_w + NOTIF_PADDING_H * 2;
        int line_h = font->charHeight();
        int line_count = static_cast<int>(lines.size());
        notif.box_h = line_count * line_h + (line_count - 1) * 1 + NOTIF_PADDING_V * 2;

        notifications.push_back(notif);
    }

    void toggleRenderInfo() { cycleRenderInfo(+1); }

    void cycleRenderInfo(int dir) {
        // Seqüència: OFF → TOP → BOTTOM → OFF
        int pos = static_cast<int>(Options::render_info.position);
        pos = (pos + (dir >= 0 ? 1 : -1) + 3) % 3;
        Options::render_info.position = static_cast<Options::RenderInfoPosition>(pos);
    }

    void setRenderInfoSegments(const char* s0, const char* s1, const char* s2, const char* s3, unsigned int mono_mask) {
        const char* segs[INFO_SEGMENT_COUNT] = {s0, s1, s2, s3};
        for (int i = 0; i < INFO_SEGMENT_COUNT; i++) {
            info_segments[i].mono_digits = (((mono_mask >> i) & 1U) != 0U);
            if (segs[i] != nullptr && *segs[i] != '\0') {
                info_segments[i].text = segs[i];
                info_segments[i].visible = true;
            } else {
                info_segments[i].visible = false;
            }
        }
    }

    void startCredits() {
        if (credits_phase != CreditsPhase::IDLE) {
            return;
        }
        credits_phase = CreditsPhase::DELAY;
        credits_timer = 0.0F;
    }

    void cancelCredits() {
        credits_phase = CreditsPhase::IDLE;
        credits_timer = 0.0F;
        notifications.clear();
    }

    auto creditsActive() -> bool {
        return credits_phase != CreditsPhase::IDLE;
    }

    auto isEscConsumed() -> bool {
        return esc_waiting;
    }

    auto handleEscape() -> bool {
        if (!esc_waiting) {
            // Primera pulsació: mostra avís i consumeix
            esc_waiting = true;
            showNotification(Locale::get("notifications.exit_double_esc"), 2.0F);
            return true;  // Consumit
        }
        // Segona pulsació: deixa passar
        esc_waiting = false;
        return false;
    }

}  // namespace Overlay
