// service_menu.hpp - Menu de servei (singleton)
// © 2026 JailDesigner
//
// Overlay de configuracio global accessible amb F12 des de qualsevol escena
// (LOGO, TITLE, GAME). Captura tots els KEY_DOWN excepte F1-F12 i ESC, que
// continuen arribant a GlobalEvents. Mentre esta obert, GameScene::update()
// fa early return per pausar el joc; LOGO i TITLE continuen renderitzant-se
// sota el menu.
//
// Arquitectura inspirada en aee_arcade service_menu.{hpp,cpp}: pila de
// pagines amb cursor, animacio open/close amb easing easeOutQuad i clipping
// del contingut mentre la caixa creix/decreix.
//
// API singleton equivalent a Notifier: init() al startup amb un renderer,
// get() retorna el punter, destroy() al teardown.

#pragma once

#include <SDL3/SDL.h>

#include <cstddef>
#include <cstdint>
#include <functional>
#include <memory>
#include <string>
#include <vector>

#include "core/graphics/vector_text.hpp"
#include "core/rendering/render_context.hpp"

class SDLManager;

namespace System {

    class DebugOverlay;

    class ServiceMenu {
       public:
        // Tipus d'item de menu. En aquesta iteracio nomes s'usen SUBMENU i
        // LABEL; la resta queden reservats per a iteracions futures (toggles
        // de vsync/zoom, picker d'idioma, restart, exit...).
        enum class Kind : std::uint8_t {
            LABEL,      // No interactiu, nomes es dibuixa
            TOGGLE,     // bool flip — reservat
            CYCLE,      // index amb modul — reservat
            INT_RANGE,  // step ± — reservat
            SUBMENU,    // pushPage en activar — usat
            ACTION      // call al lambda en activar — reservat
        };

        struct Item {
            Kind kind = Kind::LABEL;
            std::string label_key;   // Clau de locale (s'ignora si label_text no esta buit)
            std::string label_text;  // Text literal (no locale). Util per a labels que no necessiten traduccio (resolucions, etc.)
            bool selectable = true;
            // SUBMENU / ACTION: callback en ENTER / RIGHT.
            std::function<void()> on_activate;
            // TOGGLE / CYCLE / INT_RANGE: text del valor actual (renderitzat a la dreta).
            std::function<std::string()> get_value_text;
            // TOGGLE / CYCLE / INT_RANGE: callback amb +1 (RIGHT/ENTER) o -1 (LEFT).
            std::function<void(int)> on_change;
        };

        struct Page {
            std::string title_key;
            // Subtitol opcional, renderitzat sota el titol amb tipografia mes
            // petita i color apagat. Es una funcio perque pot ser dinamic
            // (versio+hash, etc.). Si esta buit, no es renderitza.
            std::function<std::string()> subtitle_provider;
            std::vector<Item> items;
            std::size_t cursor = 0;
        };

        // Inicialitza el singleton amb el renderer global, l'SDLManager (video
        // toggles: fullscreen, vsync, AA, postfx, zoom) i el DebugOverlay
        // (toggle del HUD de debug a OPCIONS). Tots propietat del Director.
        static void init(Rendering::Renderer* renderer, SDLManager* sdl, DebugOverlay* debug_overlay);
        static void destroy();
        [[nodiscard]] static auto get() -> ServiceMenu*;

        // F12: alterna obrir/tancar amb animacio.
        void toggle();
        [[nodiscard]] auto isOpen() const -> bool;

        void update(float delta_time);
        void draw() const;

        // Processa events de navegacio. Retorna true si l'event s'ha consumit.
        // Accepta:
        //   - SDL_EVENT_KEY_DOWN: UP/DOWN/ENTER/RIGHT/LEFT/BACKSPACE.
        //   - SDL_EVENT_GAMEPAD_BUTTON_DOWN: DPAD per nav, FIRE = ENTER,
        //     ACCELERATE = BACK. La resta de botons s'ignoren.
        //   - SDL_EVENT_GAMEPAD_AXIS_MOTION: stick X/Y amb edge-detect.
        auto handleEvent(const SDL_Event& event) -> bool;

       private:
        ServiceMenu(Rendering::Renderer* renderer, SDLManager* sdl, DebugOverlay* debug_overlay);

        // Sub-handlers de handleEvent. Privats, no son part de l'API publica.
        auto handleKeyDown(const SDL_Event& event) -> bool;
        auto handleGamepadButton(const SDL_Event& event) -> bool;
        auto handleGamepadAxis(const SDL_Event& event) -> bool;
        // Helpers per a cada eix; permeten que handleGamepadAxis es quedi
        // com a dispatcher i no bote el llindar de complexitat.
        void processStickX(Sint16 val);
        void processStickY(Sint16 val);
        void processTriggerEdge(SDL_JoystickID which, Sint16 val, int virtual_button, bool& held);

        void buildRootPage();
        [[nodiscard]] auto buildVideoPage() -> Page;
        [[nodiscard]] auto buildResolutionPage() const -> Page;
        [[nodiscard]] static auto buildAudioPage() -> Page;
        [[nodiscard]] auto buildOptionsPage() const -> Page;
        [[nodiscard]] auto buildSystemPage() -> Page;
        [[nodiscard]] auto buildControlsPage() -> Page;
        // Llista de mandos detectats per a un jugador. Cada item assigna el
        // pad triat (amb swap automatic si l'altre jugador ja el tenia) i
        // tanca la picker amb popPage. L'ultim item es "SENSE MANDO" per a
        // desasignar.
        [[nodiscard]] auto buildPadPickerPage(int player_index) -> Page;
        // Pagina de confirmacio "ESTAS SEGUR? NO/SI". on_yes s'executa si
        // l'usuari selecciona SI; el cursor per defecte apunta a NO.
        void pushConfirmPage(const std::string& title_key, std::function<void()> on_yes);
        void pushPage(Page page);
        void popPage();
        void moveCursor(int direction);
        void activateCurrent();
        // RIGHT (direction=+1) / LEFT (direction=-1). Per a TOGGLE/CYCLE/INT_RANGE
        // crida on_change. Per a SUBMENU/ACTION nomes +1 (entra/activa).
        void changeValue(int direction);

        // Alçada objectiu de la caixa per a la pagina superior (sense animacio).
        [[nodiscard]] auto computeTargetHeight() const -> float;

        // Ample objectiu de la caixa per a la pagina superior (sense animacio).
        // Pren com a base BOX_WIDTH_MIN i s'eixampla si algun text no hi cap.
        [[nodiscard]] auto computeTargetWidth() const -> float;

        // Y (top) de l'item index dins una caixa col·locada a box_y. Si la
        // pagina te subtitol, els items es desplacen cap avall.
        [[nodiscard]] static auto computeItemTopY(float box_y, std::size_t index, bool has_subtitle) -> float;

        Rendering::Renderer* renderer_;
        SDLManager* sdl_;
        DebugOverlay* debug_overlay_;
        Graphics::VectorText text_;

        std::vector<Page> stack_;
        bool open_ = false;
        bool closing_ = false;
        float open_anim_ = 0.0F;   // 0..1 raw (sense easing)
        float animated_h_ = 0.0F;  // Alçada animada amb smoothing exponencial
        float animated_w_ = 0.0F;  // Ample animat (eixampla segons contingut)

        // Estat del highlight (rectangle del cursor). Es lerpa cap a l'item
        // actiu amb ease-out exponencial; quan el cursor "salta" (open o
        // push/pop de pagina), s'enganxa directament al nou objectiu.
        float highlight_y_ = 0.0F;
        float highlight_h_ = 0.0F;
        bool highlight_snap_ = true;

        // Edge-detect de stick analogic per a navegacio. Una sola activacio
        // per direccio: cal tornar a centre (sota el llindar) per disparar
        // una altra. Compartit entre tots els pads — qualsevol jugador pot
        // navegar el menu.
        bool stick_left_held_ = false;
        bool stick_right_held_ = false;
        bool stick_up_held_ = false;
        bool stick_down_held_ = false;
        // Edge-detect dels triggers L2/R2 com a botons virtuals. SDL3 no
        // emet button events per als triggers; els llegim com a axis i
        // sintetitzem una pulsacio quan creuen el llindar.
        bool trigger_l2_held_ = false;
        bool trigger_r2_held_ = false;

        static std::unique_ptr<ServiceMenu> instance;
    };

}  // namespace System
