#include "global_inputs.h"
#include <SDL3/SDL_render.h> // Para SDL_RendererLogicalPresentation, SDL_Se...
#include <string>            // Para operator+, allocator, char_traits, string
#include <vector>            // Para vector
#include "asset.h"           // Para Asset
#include "input.h"           // Para Input, InputAction, InputDeviceToUse
#include "audio.h"           // Para JA_SetMusicVolume, JA_SetSoundVolume
#include "lang.h"            // Para getText, Code, getNextLangCode, loadFro...
#include "notifier.h"        // Para Notifier
#include "options.h"         // Para Options, options, VideoOptions, GameOpt...
#include "param.h"           // Para Param, ParamGame, param
#include "screen.h"          // Para Screen
#include "section.h"         // Para Name, name, Options, options, AttractMode
#include "ui/service_menu.h"    // Para ServiceMenu
#include "utils.h"           // Para boolToOnOff

namespace GlobalInputs
{
    // Termina
    void quit()
    {
        const std::string CODE = "QUIT";
        if (Notifier::get()->checkCode(CODE))
        {
            // Si la notificación de salir está activa, cambia de sección
            Section::name = Section::Name::QUIT;
            Section::options = Section::Options::NONE;
        }
        else
        {
            // Si la notificación de salir no está activa, muestra la notificación
            Notifier::get()->show({Lang::getText("[NOTIFICATIONS] 01"), std::string()}, -1, CODE);
        }
    }

    // Reinicia
    void reset()
    {
        const std::string CODE = "RESET";
        if (Notifier::get()->checkCode(CODE))
        {
            Section::name = Section::Name::RESET;
            Notifier::get()->show({Lang::getText("[NOTIFICATIONS] 15")});
        }
        else
        {
            Notifier::get()->show({Lang::getText("[NOTIFICATIONS] 03"), std::string()}, -1, CODE);
        }
    }

    // Activa o desactiva el audio
    void toggleAudio()
    {
        Options::audio.enabled = !Options::audio.enabled;
        Audio::get()->enable(Options::audio.enabled);
        Notifier::get()->show({"Audio " + boolToOnOff(Options::audio.enabled)});
    }

    // Cambia el modo de escalado entero
    void toggleIntegerScale()
    {
        Screen::get()->toggleIntegerScale();
        Notifier::get()->show({Lang::getText("[NOTIFICATIONS] 12") + " " + boolToOnOff(Options::video.integer_scale)});
    }

    // Activa / desactiva el vsync
    void toggleVSync()
    {
        Screen::get()->toggleVSync();
        Notifier::get()->show({Lang::getText("[NOTIFICATIONS] 14") + " " + boolToOnOff(Options::video.v_sync)});
    }

    // Activa o desactiva los shaders
    void toggleShaders()
    {
        Screen::get()->toggleShaders();
        Notifier::get()->show({Lang::getText("[NOTIFICATIONS] 13") + " " + boolToOnOff(Options::video.shaders)});
    }

    // Obtiene una fichero a partir de un lang::Code
    std::string getLangFile(Lang::Code code)
    {
        switch (code)
        {
        case Lang::Code::VALENCIAN:
            return Asset::get()->get("ba_BA.json");
            break;
        case Lang::Code::SPANISH:
            return Asset::get()->get("es_ES.json");
            break;
        default:
            return Asset::get()->get("en_UK.json");
            break;
        }
    }

    // Obtiene una cadena a partir de un lang::Code
    std::string getLangName(Lang::Code code)
    {
        switch (code)
        {
        case Lang::Code::VALENCIAN:
            return " \"ba_BA\"";
            break;
        case Lang::Code::SPANISH:
            return " \"es_ES\"";
            break;
        default:
            return " \"en_UK\"";
            break;
        }
    }

    // Cambia el idioma
    void changeLang()
    {
        const std::string CODE = "LANG";
        if (Notifier::get()->checkCode(CODE))
        {
            Options::settings.language = Lang::getNextLangCode(Options::settings.language);
            Lang::loadFromFile(getLangFile(static_cast<Lang::Code>(Options::settings.language)));
            Section::name = Section::Name::RESET;
            Section::options = Section::Options::RELOAD;
            Notifier::get()->show({Lang::getText("[NOTIFICATIONS] 05") + getLangName(Options::settings.language)});
        }
        else
        {
            const auto NEXT = Lang::getNextLangCode(Options::settings.language);
            Notifier::get()->show({Lang::getText("[NOTIFICATIONS] 04") + getLangName(NEXT), std::string()}, -1, CODE);
        }
    }

    // Cambia el modo de disparo
    void toggleFireMode()
    {
        Options::settings.autofire = !Options::settings.autofire;
        Notifier::get()->show({Lang::getText("[NOTIFICATIONS] 08") + " " + boolToOnOff(Options::settings.autofire)});
    }

    // Salta una sección del juego
    void skipSection()
    {
        switch (Section::name)
        {
        case Section::Name::INTRO:
            Audio::get()->stopMusic();
            /* Continua en el case de abajo */
        case Section::Name::LOGO:
        case Section::Name::HI_SCORE_TABLE:
        case Section::Name::INSTRUCTIONS:
        {
            Section::name = Section::Name::TITLE;
            Section::options = Section::Options::TITLE_1;
            Section::attract_mode = Section::AttractMode::TITLE_TO_DEMO;
            break;
        }
        default:
            break;
        }
    }

    // Activa el menu de servicio
    void toggleServiceMenu()
    {
        ServiceMenu::get()->toggle();
    }

    // Cambia el modo de pantalla completa
    void toggleFullscreen()
    {
        Screen::get()->toggleFullscreen();
        const std::string MODE = Options::video.fullscreen ? Lang::getText("[NOTIFICATIONS] 11") : Lang::getText("[NOTIFICATIONS] 10");
        Notifier::get()->show({MODE});
    }

    // Reduce el tamaño de la ventana
    void decWindowSize()
    {
        if (Screen::get()->decWindowSize())
        {
            Notifier::get()->show({Lang::getText("[NOTIFICATIONS] 09") + " x" + std::to_string(Options::window.size)});
        }
    }

    // Aumenta el tamaño de la ventana
    void incWindowSize()
    {
        if (Screen::get()->incWindowSize())
        {
            Notifier::get()->show({Lang::getText("[NOTIFICATIONS] 09") + " x" + std::to_string(Options::window.size)});
        }
    }

    // Comprueba el boton de servicio
    void checkServiceButton()
    {
        // Teclado
        if (Input::get()->checkInput(InputAction::SERVICE, INPUT_DO_NOT_ALLOW_REPEAT, InputDeviceToUse::KEYBOARD))
        {
            toggleServiceMenu();
            return;
        }

        // Mandos
        {
            for (int i = 0; i < Input::get()->getNumControllers(); ++i)
            {
                if (Input::get()->checkInput(InputAction::SERVICE, INPUT_DO_NOT_ALLOW_REPEAT, InputDeviceToUse::CONTROLLER, i))
                {
                    toggleServiceMenu();
                    return;
                }
            }
        }
    }

    // Comprueba las entradas del menú de servicio
    void checkServiceInputs()
    {
        if (!ServiceMenu::get()->isEnabled())
            return;

        // Teclado
        {
            // Arriba
            if (Input::get()->checkInput(InputAction::UP, INPUT_DO_NOT_ALLOW_REPEAT, InputDeviceToUse::KEYBOARD))
            {
                ServiceMenu::get()->setSelectorUp();
                return;
            }

            // Abajo
            if (Input::get()->checkInput(InputAction::DOWN, INPUT_DO_NOT_ALLOW_REPEAT, InputDeviceToUse::KEYBOARD))
            {
                ServiceMenu::get()->setSelectorDown();
                return;
            }

            // Derecha
            if (Input::get()->checkInput(InputAction::RIGHT, INPUT_DO_NOT_ALLOW_REPEAT, InputDeviceToUse::KEYBOARD))
            {
                ServiceMenu::get()->adjustOption(true);
                return;
            }

            // Izquierda
            if (Input::get()->checkInput(InputAction::LEFT, INPUT_DO_NOT_ALLOW_REPEAT, InputDeviceToUse::KEYBOARD))
            {
                ServiceMenu::get()->adjustOption(false);
                return;
            }

            // Aceptar
            if (Input::get()->checkInput(InputAction::SM_SELECT, INPUT_DO_NOT_ALLOW_REPEAT, InputDeviceToUse::KEYBOARD))
            {
                ServiceMenu::get()->selectOption();
                return;
            }

            // Atras
            if (Input::get()->checkInput(InputAction::SM_BACK, INPUT_DO_NOT_ALLOW_REPEAT, InputDeviceToUse::KEYBOARD))
            {
                ServiceMenu::get()->moveBack();
                return;
            }
        }

        // Mandos
        {
            for (int i = 0; i < Input::get()->getNumControllers(); ++i)
            {
                // Arriba
                if (Input::get()->checkInput(InputAction::UP, INPUT_DO_NOT_ALLOW_REPEAT, InputDeviceToUse::CONTROLLER, i))
                {
                    ServiceMenu::get()->setSelectorUp();
                    return;
                }

                // Abajo
                if (Input::get()->checkInput(InputAction::DOWN, INPUT_DO_NOT_ALLOW_REPEAT, InputDeviceToUse::CONTROLLER, i))
                {
                    ServiceMenu::get()->setSelectorDown();
                    return;
                }

                // Derecha
                if (Input::get()->checkInput(InputAction::RIGHT, INPUT_DO_NOT_ALLOW_REPEAT, InputDeviceToUse::CONTROLLER, i))
                {
                    ServiceMenu::get()->adjustOption(true);
                    return;
                }

                // Izquierda
                if (Input::get()->checkInput(InputAction::LEFT, INPUT_DO_NOT_ALLOW_REPEAT, InputDeviceToUse::CONTROLLER, i))
                {
                    ServiceMenu::get()->adjustOption(false);
                    return;
                }

                // Aceptar
                if (Input::get()->checkInput(InputAction::SM_SELECT, INPUT_DO_NOT_ALLOW_REPEAT, InputDeviceToUse::CONTROLLER, i))
                {
                    ServiceMenu::get()->selectOption();
                    return;
                }

                // Atras
                if (Input::get()->checkInput(InputAction::SM_BACK, INPUT_DO_NOT_ALLOW_REPEAT, InputDeviceToUse::CONTROLLER, i))
                {
                    ServiceMenu::get()->moveBack();
                    return;
                }
            }
        }
    }

    // Comprueba las entradas fuera del menú de servicio
    void checkInputs()
    {
        // Teclado
        {
            // Comprueba el teclado para cambiar entre pantalla completa y ventana
            if (Input::get()->checkInput(InputAction::WINDOW_FULLSCREEN, INPUT_DO_NOT_ALLOW_REPEAT, InputDeviceToUse::KEYBOARD))
            {
                Screen::get()->toggleFullscreen();
                const std::string MODE = Options::video.fullscreen ? Lang::getText("[NOTIFICATIONS] 11") : Lang::getText("[NOTIFICATIONS] 10");
                Notifier::get()->show({MODE});
                return;
            }

            // Comprueba el teclado para decrementar el tamaño de la ventana
            if (Input::get()->checkInput(InputAction::WINDOW_DEC_SIZE, INPUT_DO_NOT_ALLOW_REPEAT, InputDeviceToUse::KEYBOARD))
            {
                if (Screen::get()->decWindowSize())
                {
                    Notifier::get()->show({Lang::getText("[NOTIFICATIONS] 09") + " x" + std::to_string(Options::window.size)});
                }
                return;
            }

            // Comprueba el teclado para incrementar el tamaño de la ventana
            if (Input::get()->checkInput(InputAction::WINDOW_INC_SIZE, INPUT_DO_NOT_ALLOW_REPEAT, InputDeviceToUse::KEYBOARD))
            {
                if (Screen::get()->incWindowSize())
                {
                    Notifier::get()->show({Lang::getText("[NOTIFICATIONS] 09") + " x" + std::to_string(Options::window.size)});
                }
                return;
            }

            // Salir
            if (Input::get()->checkInput(InputAction::EXIT, INPUT_DO_NOT_ALLOW_REPEAT, InputDeviceToUse::KEYBOARD))
            {
                quit();
                return;
            }

            // Saltar sección
            if (Input::get()->checkAnyButtonPressed() && !ServiceMenu::get()->isEnabled())
            {
                skipSection();
                return;
            }

            // Reset
            if (Input::get()->checkInput(InputAction::RESET, INPUT_DO_NOT_ALLOW_REPEAT, InputDeviceToUse::KEYBOARD))
            {
                reset();
                return;
            }

            // Audio
            if (Input::get()->checkInput(InputAction::TOGGLE_AUDIO, INPUT_DO_NOT_ALLOW_REPEAT, InputDeviceToUse::KEYBOARD))
            {
                toggleAudio();
                return;
            }

            // Autofire
            if (Input::get()->checkInput(InputAction::TOGGLE_AUTO_FIRE, INPUT_DO_NOT_ALLOW_REPEAT, InputDeviceToUse::KEYBOARD))
            {
                toggleFireMode();
                return;
            }

            // Idioma
            if (Input::get()->checkInput(InputAction::CHANGE_LANG, INPUT_DO_NOT_ALLOW_REPEAT, InputDeviceToUse::KEYBOARD))
            {
                changeLang();
                return;
            }

            // Shaders
            if (Input::get()->checkInput(InputAction::TOGGLE_VIDEO_SHADERS, INPUT_DO_NOT_ALLOW_REPEAT, InputDeviceToUse::KEYBOARD))
            {
                toggleShaders();
                return;
            }

            // Integer Scale
            if (Input::get()->checkInput(InputAction::TOGGLE_VIDEO_INTEGER_SCALE, INPUT_DO_NOT_ALLOW_REPEAT, InputDeviceToUse::KEYBOARD))
            {
                toggleIntegerScale();
                return;
            }

            // VSync
            if (Input::get()->checkInput(InputAction::TOGGLE_VIDEO_VSYNC, INPUT_DO_NOT_ALLOW_REPEAT, InputDeviceToUse::KEYBOARD))
            {
                toggleVSync();
                return;
            }

#ifdef DEBUG
            // Debug info
            if (Input::get()->checkInput(InputAction::SHOW_INFO, INPUT_DO_NOT_ALLOW_REPEAT, InputDeviceToUse::KEYBOARD))
            {
                Screen::get()->toggleDebugInfo();
                return;
            }
#endif
        }
    }

    // Comprueba los inputs que se pueden introducir en cualquier sección del juego
    void check()
    {
        checkServiceButton();
        checkServiceInputs();
        checkInputs();
    }
}