// define_inputs.hpp - Overlay modal de redefinici de controls (singleton)
// © 2026 JailDesigner
//
// Sub-mòdul inspirat en aee_arcade/source/core/input/define_buttons. Quan el
// menú de servei dispara una acció "Redefinir tecles/botons P1/P2", aquest
// singleton pren el control: pinta una caixa central, captura events SDL i
// avança per una seqüència fixa d'accions, persistint les noves assignacions
// a config.yaml en acabar.
//
// Cicle de vida:
//   1. begin(mode, player) → construeix la seqüència (4 passos) i activa
//      l'overlay. Per a GAMEPAD, retorna false si el jugador no té pad.
//   2. handleEvent() captura el següent event vàlid; ESC cancel·la sense
//      desar; duplicats dins de la sessió es rebutgen silenciosament.
//   3. Quan la seqüència es completa, persistim a engine_config + saveToFile,
//      reapliquem els bindings i mostrem un missatge "OK" durant 1.5 s
//      abans d'auto-tancar-se.
//
// El routing d'events es fa des de GlobalEvents::handle: mentre isActive()
// retorna true, tots els events SDL es desvien aquí i no arriben al joc ni
// al menú de servei.

#pragma once

#include <SDL3/SDL.h>

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

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

namespace System {

    class DefineInputs {
       public:
        enum class Mode : std::uint8_t { KEYBOARD,
            GAMEPAD };
        enum class Player : std::uint8_t { P1,
            P2 };

        static void init(Rendering::Renderer* renderer);
        static void destroy();
        [[nodiscard]] static auto get() -> DefineInputs*;

        // Comença la sessió. Retorna false per a GAMEPAD si el jugador no té
        // cap pad assignat (el caller hauria de notificar a l'usuari abans).
        auto begin(Mode mode, Player player) -> bool;
        void cancel();

        [[nodiscard]] auto isActive() const -> bool;

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

        // Retorna true si l'event s'ha consumit (és a dir, mentre l'overlay
        // és actiu sempre consumeix tot per evitar passages al joc o menú).
        auto handleEvent(const SDL_Event& event) -> bool;

       private:
        explicit DefineInputs(Rendering::Renderer* renderer);

        enum class Phase : std::uint8_t {
            INACTIVE,
            CAPTURING,
            COMPLETE,  // mostra missatge OK breu abans d'auto-cancel
        };

        struct Step {
            std::string action_label_key;  // p.ex. "define.action.left"
            InputAction action;            // mapeig a la struct PlayerBindings
            int captured{-1};              // scancode o button code; -1 = sense capturar
        };

        void buildSequence();
        [[nodiscard]] auto isInUse(int code) const -> bool;
        void captureAndAdvance(int code);
        void persistAndComplete();

        // Handlers especialitzats segons mode_.
        auto handleKeyboardEvent(const SDL_Event& event) -> bool;
        auto handleGamepadEvent(const SDL_Event& event) -> bool;

        // Edge-detect per als triggers L2/R2 com a botons virtuals.
        void processTrigger(int virtual_button, bool& was_pressed, bool now);

        Rendering::Renderer* renderer_;
        Graphics::VectorText text_;

        Phase phase_{Phase::INACTIVE};
        Mode mode_{Mode::KEYBOARD};
        Player player_{Player::P1};
        std::vector<Step> sequence_;
        std::size_t index_{0};
        float complete_timer_s_{0.0F};

        // Estat d'edge-detect dels triggers durant la sessió GAMEPAD.
        bool l2_was_pressed_{false};
        bool r2_was_pressed_{false};

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

}  // namespace System
