#include "core/rendering/menu.hpp"

#include <cstdio>
#include <functional>
#include <memory>
#include <string>
#include <vector>

#include "core/rendering/overlay.hpp"
#include "core/rendering/screen.hpp"
#include "core/rendering/text.hpp"
#include "game/options.hpp"

namespace Menu {

    // --- Constants visuals ---
    static constexpr int SCREEN_W = 320;
    static constexpr int SCREEN_H = 200;

    static constexpr int BOX_W = 220;
    static constexpr int BOX_H = 150;
    static constexpr int BOX_X = (SCREEN_W - BOX_W) / 2;  // 50
    static constexpr int BOX_Y = (SCREEN_H - BOX_H) / 2;  // 25

    static constexpr Uint32 BG_COLOR = 0xFF1A0E0E;      // fons marró fosc (ABGR)
    static constexpr Uint8 BG_ALPHA = 220;              // semi-transparent
    static constexpr Uint32 BORDER_COLOR = 0xFFFFFF00;  // cyan
    static constexpr Uint32 TITLE_COLOR = 0xFFFFFFFF;   // blanc
    static constexpr Uint32 LABEL_COLOR = 0xFFCCCCCC;   // gris clar
    static constexpr Uint32 VALUE_COLOR = 0xFFFFFF00;   // cyan
    static constexpr Uint32 CURSOR_COLOR = 0xFF00FFFF;  // groc
    static constexpr Uint32 FOOTER_COLOR = 0xFF888888;  // gris

    static constexpr int TITLE_PAD_Y = 4;
    static constexpr int ITEM_PAD_X = 10;
    static constexpr int ITEM_SPACING = 11;  // 8 px glifo + 3 pad
    static constexpr int FOOTER_PAD_Y = 4;

    // --- Items ---
    enum class ItemKind { Toggle,
        Cycle,
        IntRange };

    struct Item {
        const char* label;
        ItemKind kind;
        std::function<std::string()> getValue;
        std::function<void(int dir)> change;  // dir: -1=left, +1=right
    };

    // --- Estat ---
    static bool open_ = false;
    static int cursor_ = 0;
    static std::vector<Item> items_;
    static std::unique_ptr<Text> font_;

    // --- Helpers ---

    static std::string yesNo(bool b) { return b ? "SI" : "NO"; }
    static std::string onOff(bool b) { return b ? "ON" : "OFF"; }

    // Construeix la llista d'items (Video)
    static void buildItems() {
        items_.clear();

        // ZOOM
        items_.push_back({"ZOOM", ItemKind::IntRange, [] {
                char buf[16];
                std::snprintf(buf, sizeof(buf), "%dX", Screen::get()->getZoom());
                return std::string(buf); }, [](int dir) {
                if (dir < 0) Screen::get()->decZoom();
                else if (dir > 0) Screen::get()->incZoom(); }});

        // PANTALLA (fullscreen)
        items_.push_back({"PANTALLA", ItemKind::Toggle, [] { return std::string(Screen::get()->isFullscreen() ? "COMPLETA" : "FINESTRA"); }, [](int) { Screen::get()->toggleFullscreen(); }});

        // SHADER
        items_.push_back({"SHADER", ItemKind::Toggle, [] { return onOff(Options::video.shader_enabled); }, [](int) { Screen::get()->toggleShaders(); }});

        // ASPECTE 4:3
        items_.push_back({"ASPECTE 4:3", ItemKind::Toggle, [] { return yesNo(Options::video.aspect_ratio_4_3); }, [](int) { Screen::get()->toggleAspectRatio(); }});

        // SUPERSAMPLING
        items_.push_back({"SUPERSAMPLING", ItemKind::Toggle, [] { return onOff(Options::video.supersampling); }, [](int) { Screen::get()->toggleSupersampling(); }});

        // TIPUS SHADER
        items_.push_back({"TIPUS SHADER", ItemKind::Cycle, [] { return std::string(Screen::get()->getActiveShaderName()); }, [](int dir) {
                if (dir < 0) Screen::get()->prevShaderType();
                else Screen::get()->nextShaderType(); }});

        // PRESET
        items_.push_back({"PRESET", ItemKind::Cycle, [] { return std::string(Screen::get()->getCurrentPresetName()); }, [](int dir) {
                if (dir < 0) Screen::get()->prevPreset();
                else Screen::get()->nextPreset(); }});

        // FILTRE 4:3
        items_.push_back({"FILTRE 4:3", ItemKind::Toggle, [] { return std::string(Options::video.stretch_filter_linear ? "LINEAR" : "NEAREST"); }, [](int) { Screen::get()->toggleStretchFilter(); }});

        // RENDER INFO
        items_.push_back({"RENDER INFO", ItemKind::Cycle, [] {
                switch (Options::render_info.position) {
                    case Options::RenderInfoPosition::OFF: return std::string("OFF");
                    case Options::RenderInfoPosition::TOP: return std::string("TOP");
                    case Options::RenderInfoPosition::BOTTOM: return std::string("BOTTOM");
                }
                return std::string("OFF"); }, [](int dir) { Overlay::cycleRenderInfo(dir); }});
    }

    // --- Dibuix ---

    // Alpha blending per pixel sobre el buffer ARGB (ABGR en memòria)
    // src_argb és el color desitjat (canal alpha ignorat, s'usa src_alpha)
    static void blendRect(Uint32* buf, int x, int y, int w, int h, Uint32 src_argb, Uint8 src_alpha) {
        const Uint8 sa = src_alpha;
        const Uint8 sr = src_argb & 0xFF;
        const Uint8 sg = (src_argb >> 8) & 0xFF;
        const Uint8 sb = (src_argb >> 16) & 0xFF;
        const Uint8 inv = 255 - sa;
        for (int row = y; row < y + h; row++) {
            if (row < 0 || row >= SCREEN_H) continue;
            for (int col = x; col < x + w; col++) {
                if (col < 0 || col >= SCREEN_W) continue;
                Uint32* p = &buf[col + row * SCREEN_W];
                Uint32 dst = *p;
                Uint8 dr = dst & 0xFF;
                Uint8 dg = (dst >> 8) & 0xFF;
                Uint8 db = (dst >> 16) & 0xFF;
                Uint8 r = (sr * sa + dr * inv) / 255;
                Uint8 g = (sg * sa + dg * inv) / 255;
                Uint8 b = (sb * sa + db * inv) / 255;
                *p = 0xFF000000u | (static_cast<Uint32>(b) << 16) | (static_cast<Uint32>(g) << 8) | r;
            }
        }
    }

    static void fillRect(Uint32* buf, int x, int y, int w, int h, Uint32 color) {
        for (int row = y; row < y + h; row++) {
            if (row < 0 || row >= SCREEN_H) continue;
            for (int col = x; col < x + w; col++) {
                if (col < 0 || col >= SCREEN_W) continue;
                buf[col + row * SCREEN_W] = color;
            }
        }
    }

    static void drawBorder(Uint32* buf, int x, int y, int w, int h, Uint32 color) {
        fillRect(buf, x, y, w, 1, color);          // top
        fillRect(buf, x, y + h - 1, w, 1, color);  // bottom
        fillRect(buf, x, y, 1, h, color);          // left
        fillRect(buf, x + w - 1, y, 1, h, color);  // right
    }

    // --- API pública ---

    void init() {
        font_ = std::make_unique<Text>("fonts/8bithud.fnt", "fonts/8bithud.gif");
        buildItems();
        cursor_ = 0;
        open_ = false;
    }

    void destroy() {
        font_.reset();
        items_.clear();
    }

    auto isOpen() -> bool {
        return open_;
    }

    void toggle() {
        open_ = !open_;
    }

    void close() {
        open_ = false;
    }

    void handleKey(SDL_Scancode sc) {
        if (!open_ || items_.empty()) return;
        switch (sc) {
            case SDL_SCANCODE_UP:
                cursor_ = (cursor_ - 1 + static_cast<int>(items_.size())) % static_cast<int>(items_.size());
                break;
            case SDL_SCANCODE_DOWN:
                cursor_ = (cursor_ + 1) % static_cast<int>(items_.size());
                break;
            case SDL_SCANCODE_LEFT:
                items_[cursor_].change(-1);
                break;
            case SDL_SCANCODE_RIGHT:
            case SDL_SCANCODE_RETURN:
            case SDL_SCANCODE_KP_ENTER:
                items_[cursor_].change(+1);
                break;
            default:
                break;
        }
    }

    void render(Uint32* pixel_data) {
        if (!open_ || !font_ || !pixel_data) return;

        // Fons semi-transparent
        blendRect(pixel_data, BOX_X, BOX_Y, BOX_W, BOX_H, BG_COLOR, BG_ALPHA);

        // Vora
        drawBorder(pixel_data, BOX_X, BOX_Y, BOX_W, BOX_H, BORDER_COLOR);

        // Títol
        const char* title = "OPCIONS DE VIDEO";
        int title_w = font_->width(title);
        font_->draw(pixel_data, BOX_X + (BOX_W - title_w) / 2, BOX_Y + TITLE_PAD_Y, title, TITLE_COLOR);

        // Línia sota el títol
        int title_line_y = BOX_Y + TITLE_PAD_Y + font_->charHeight() + 2;
        fillRect(pixel_data, BOX_X + 4, title_line_y, BOX_W - 8, 1, BORDER_COLOR);

        // Items
        int items_y = title_line_y + 4;
        for (size_t i = 0; i < items_.size(); i++) {
            int y = items_y + static_cast<int>(i) * ITEM_SPACING;
            bool selected = (static_cast<int>(i) == cursor_);

            // Cursor
            if (selected) {
                font_->draw(pixel_data, BOX_X + 4, y, ">", CURSOR_COLOR);
            }

            // Label
            Uint32 label_color = selected ? CURSOR_COLOR : LABEL_COLOR;
            font_->draw(pixel_data, BOX_X + ITEM_PAD_X, y, items_[i].label, label_color);

            // Valor (dreta)
            std::string value = items_[i].getValue();
            int value_w = font_->width(value.c_str());
            Uint32 value_color = selected ? CURSOR_COLOR : VALUE_COLOR;
            font_->draw(pixel_data, BOX_X + BOX_W - ITEM_PAD_X - value_w, y, value.c_str(), value_color);
        }

        // Peu
        const char* footer = "^v:MOU  <>:CANVIA  ESC:IX";
        int footer_w = font_->width(footer);
        int footer_y = BOX_Y + BOX_H - font_->charHeight() - FOOTER_PAD_Y;
        font_->draw(pixel_data, BOX_X + (BOX_W - footer_w) / 2, footer_y, footer, FOOTER_COLOR);
    }

}  // namespace Menu
