// global_events.cpp - Implementació dels events globals
// © 2026 JailDesigner

#include "global_events.hpp"

#include <iostream>

#include "core/input/define_inputs.hpp"
#include "core/input/input.hpp"
#include "core/input/mouse.hpp"
#include "core/locale/locale.hpp"
#include "core/rendering/sdl_manager.hpp"
#include "core/system/notifier.hpp"
#include "core/system/service_menu.hpp"
#include "game/config_yaml.hpp"
#include "scene_context.hpp"

// Using declarations per simplificar el codi
using SceneManager::SceneContext;
using SceneType = SceneContext::SceneType;

namespace GlobalEvents {

    namespace {

        // Reenvia events al menu de servei si esta obert. Accepta:
        //   - KEY_DOWN (excepte F1-F12 i ESC, que sempre passen com a globals)
        //   - GAMEPAD_BUTTON_DOWN (per navegacio amb dpad + FIRE/ACCELERATE)
        //   - GAMEPAD_AXIS_MOTION (per navegacio amb stick)
        // Retorna true si l'event s'ha entregat al menu.
        auto forwardToServiceMenu(const SDL_Event& event) -> bool {
            auto* menu = System::ServiceMenu::get();
            if (menu == nullptr || !menu->isOpen()) {
                return false;
            }
            if (event.type == SDL_EVENT_KEY_DOWN) {
                const SDL_Scancode SC = event.key.scancode;
                const bool PASSTHROUGH = (SC == SDL_SCANCODE_ESCAPE) ||
                    (SC >= SDL_SCANCODE_F1 && SC <= SDL_SCANCODE_F12);
                if (PASSTHROUGH) {
                    return false;
                }
                menu->handleEvent(event);
                return true;
            }
            if (event.type == SDL_EVENT_GAMEPAD_BUTTON_DOWN ||
                event.type == SDL_EVENT_GAMEPAD_AXIS_MOTION) {
                menu->handleEvent(event);
                return true;
            }
            return false;
        }

        // Engoleix els events que DefineInputs vol consumir mentre l'overlay
        // es actiu. Els events que el modul torna a passar (QUIT, ESC) cauen
        // cap al pipeline normal i poden tancar la finestra o obrir el prompt
        // d'eixida sense haver de completar la sequencia.
        auto consumeIfDefineActive(const SDL_Event& event) -> bool {
            auto* di = System::DefineInputs::get();
            if (di == nullptr || !di->isActive()) {
                return false;
            }
            return di->handleEvent(event);
        }

        // Botó MENU al mando d'algun jugador → alterna el menú de servei
        // (mateix comportament que F12 al teclat). Retorna true si l'event és
        // un GAMEPAD_BUTTON_DOWN consumit.
        auto handleGamepadMenuButton(const SDL_Event& event) -> bool {
            if (event.type != SDL_EVENT_GAMEPAD_BUTTON_DOWN) {
                return false;
            }
            auto* input = Input::get();
            if (input == nullptr) {
                return false;
            }
            auto match_player = [&](int player_index) {
                auto pad = input->getPlayerGamepad(player_index);
                if (!pad || pad->instance_id != event.gbutton.which) {
                    return false;
                }
                auto it = pad->bindings.find(InputAction::MENU);
                if (it == pad->bindings.end()) {
                    return false;
                }
                return it->second.button == static_cast<int>(event.gbutton.button);
            };
            if (!match_player(0) && !match_player(1)) {
                return false;
            }
            if (auto* menu = System::ServiceMenu::get(); menu != nullptr) {
                menu->toggle();
            }
            return true;
        }

    }  // namespace

    auto handle(const SDL_Event& event, SDLManager& sdl, SceneContext& context) -> bool {
        // 1. Permitir que Input procese el evento (para hotplug de gamepads)
        auto event_msg = Input::get()->handleEvent(event);
        if (!event_msg.empty()) {
            std::cout << "[Input] " << event_msg << '\n';
        }

        // 1b. Si l'overlay de redefinicio esta actiu, engoleix tots els events
        // (cap arriba al joc, al menu de servei ni als hotkeys F1-F12).
        if (consumeIfDefineActive(event)) {
            return true;
        }

        // 2. Procesar SDL_EVENT_QUIT directamente (no es input de juego)
        if (event.type == SDL_EVENT_QUIT) {
            context.setNextScene(SceneType::EXIT);
            SceneManager::actual = SceneType::EXIT;
            return true;
        }

        // 3. Gestió del ratolí (auto-ocultar)
        Mouse::handleEvent(event);

        // 3b. Botó MENU al mando (equivalent a F12)
        if (handleGamepadMenuButton(event)) {
            return true;
        }

        // 4. Service Menu (F12): consumeix tot KEY_DOWN excepte tecles de
        // funció (F1-F12) i ESC, que continuen sent globals (zoom, fullscreen,
        // vsync, AA, postfx, locale, exit prompt). Aixi el menu captura
        // ENTER/BACKSPACE/UP/DOWN/LEFT/RIGHT i lletres mentre esta obert.
        if (forwardToServiceMenu(event)) {
            return true;
        }

        // 5. Procesar acciones globales directamente desde eventos SDL
        // (NO usar Input::checkAction() para evitar desfase de timing)
        if (event.type == SDL_EVENT_KEY_DOWN) {
            switch (event.key.scancode) {
                case SDL_SCANCODE_F1:
                    sdl.decreaseWindowSize();
                    return true;

                case SDL_SCANCODE_F2:
                    sdl.increaseWindowSize();
                    return true;

                case SDL_SCANCODE_F3:
                    sdl.toggleFullscreen();
                    return true;

                case SDL_SCANCODE_F4:
                    sdl.toggleVSync();
                    return true;

                case SDL_SCANCODE_F5:
                    sdl.toggleAntialias();
                    return true;

                case SDL_SCANCODE_F6:
                    sdl.togglePostFx();
                    return true;

                case SDL_SCANCODE_F7: {
                    // Toggle d'idioma en runtime entre català i anglès. Els
                    // strings ja capturats (toast actiu, banner stage start)
                    // sobreviuen fins al seu cicle; la resta (HUD, pantalles,
                    // pròxims toasts) es refresquen al següent frame perquè
                    // criden Locale::text() cada draw.
                    const std::string NEW_LANG = (ConfigYaml::engine_config.locale == "ca") ? "en" : "ca";
                    if (Locale::get().switchTo(NEW_LANG)) {
                        ConfigYaml::engine_config.locale = NEW_LANG;
                        ConfigYaml::saveToFile();
                        if (auto* notifier = System::Notifier::get(); notifier != nullptr) {
                            notifier->notifyInfo(localeSubstitute(
                                Locale::get().text("notification.locale_switched"),
                                "{lang}",
                                Locale::get().text("language." + NEW_LANG)));
                        }
                    }
                    return true;
                }

                case SDL_SCANCODE_F12: {
                    // Toggle del menu de servei. Sempre passa com a global
                    // (alterna obert/tancat des de qualsevol escena).
                    if (auto* menu = System::ServiceMenu::get(); menu != nullptr) {
                        menu->toggle();
                    }
                    return true;
                }

                case SDL_SCANCODE_ESCAPE: {
                    // Doble pulsació per confirmar sortida: la primera ESC
                    // dispara un toast d'avís; només si aquest toast concret
                    // (isExitPromptActive) segueix visible, la segona ESC
                    // tanca el joc. Si la notificació activa és una altra
                    // (zoom, fullscreen, vsync...), ESC obre el prompt de
                    // sortida en lloc de tancar.
                    auto* notifier = System::Notifier::get();
                    if (notifier != nullptr && !notifier->isExitPromptActive()) {
                        notifier->notifyExit(Locale::get().text("notification.press_again_exit"));
                        return true;
                    }
                    // Notifier inexistent (degradació elegant) o segona ESC
                    // sobre el prompt de sortida: tanquem el joc.
                    context.setNextScene(SceneType::EXIT);
                    SceneManager::actual = SceneType::EXIT;
                    return true;
                }

                default:
                    // Tecla no global
                    break;
            }
        }

        return false;  // Event no processat
    }

}  // namespace GlobalEvents
