#include "global_inputs.h"

#include <string>  // Para operator+, allocator, char_traits, to_string, string
#include <vector>  // Para vector

#include "asset.h"            // Para Asset
#include "audio.h"            // Para Audio
#include "input.h"            // Para Input, Input::DO_NOT_ALLOW_REPEAT, InputAction, InputDevice
#include "lang.h"             // Para getText, Code, getNextLangCode, loadFromFile
#include "options.h"          // Para SettingsOptions, settings, VideoOptions, WindowOptions, video, window, AudioOptions, audio
#include "screen.h"           // Para Screen
#include "section.hpp"        // Para Name, name, Options, options, AttractMode, attract_mode
#include "ui/notifier.h"      // Para Notifier
#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.vsync)});
}

// 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
auto getLangFile(Lang::Code code) -> std::string {
    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
auto getLangName(Lang::Code code) -> std::string {
    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(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.zoom)});
    }
}

// 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.zoom)});
    }
}

// Comprueba el boton de servicio
auto checkServiceButton() -> bool {
    // Teclado
    if (Input::get()->checkAction(Input::Action::SERVICE, Input::DO_NOT_ALLOW_REPEAT, Input::CHECK_KEYBOARD)) {
        toggleServiceMenu();
        return true;
    }

    // Mandos
    {
        auto gamepads = Input::get()->getGamepads();
        for (auto gamepad : gamepads) {
            if (Input::get()->checkAction(Input::Action::SERVICE, Input::DO_NOT_ALLOW_REPEAT, Input::DO_NOT_CHECK_KEYBOARD, gamepad)) {
                toggleServiceMenu();
                return true;
            }
        }
    }
    return false;
}

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

    // Teclado
    {
        // Arriba
        if (Input::get()->checkAction(Input::Action::UP, Input::DO_NOT_ALLOW_REPEAT, Input::CHECK_KEYBOARD)) {
            ServiceMenu::get()->setSelectorUp();
            return true;
        }

        // Abajo
        if (Input::get()->checkAction(Input::Action::DOWN, Input::DO_NOT_ALLOW_REPEAT, Input::CHECK_KEYBOARD)) {
            ServiceMenu::get()->setSelectorDown();
            return true;
        }

        // Derecha
        if (Input::get()->checkAction(Input::Action::RIGHT, Input::DO_NOT_ALLOW_REPEAT, Input::CHECK_KEYBOARD)) {
            ServiceMenu::get()->adjustOption(true);
            return true;
        }

        // Izquierda
        if (Input::get()->checkAction(Input::Action::LEFT, Input::DO_NOT_ALLOW_REPEAT, Input::CHECK_KEYBOARD)) {
            ServiceMenu::get()->adjustOption(false);
            return true;
        }

        // Aceptar
        if (Input::get()->checkAction(Input::Action::SM_SELECT, Input::DO_NOT_ALLOW_REPEAT, Input::CHECK_KEYBOARD)) {
            ServiceMenu::get()->selectOption();
            return true;
        }

        // Atras
        if (Input::get()->checkAction(Input::Action::SM_BACK, Input::DO_NOT_ALLOW_REPEAT, Input::CHECK_KEYBOARD)) {
            ServiceMenu::get()->moveBack();
            return true;
        }
    }

    // Mandos
    {
         auto gamepads = Input::get()->getGamepads();
        for (auto gamepad : gamepads) {
            // Arriba
            if (Input::get()->checkAction(Input::Action::UP, Input::DO_NOT_ALLOW_REPEAT, Input::DO_NOT_CHECK_KEYBOARD, gamepad)) {
                ServiceMenu::get()->setSelectorUp();
                return true;
            }

            // Abajo
            if (Input::get()->checkAction(Input::Action::DOWN, Input::DO_NOT_ALLOW_REPEAT, Input::DO_NOT_CHECK_KEYBOARD, gamepad)) {
                ServiceMenu::get()->setSelectorDown();
                return true;
            }

            // Derecha
            if (Input::get()->checkAction(Input::Action::RIGHT, Input::DO_NOT_ALLOW_REPEAT, Input::DO_NOT_CHECK_KEYBOARD, gamepad)) {
                ServiceMenu::get()->adjustOption(true);
                return true;
            }

            // Izquierda
            if (Input::get()->checkAction(Input::Action::LEFT, Input::DO_NOT_ALLOW_REPEAT, Input::DO_NOT_CHECK_KEYBOARD, gamepad)) {
                ServiceMenu::get()->adjustOption(false);
                return true;
            }

            // Aceptar
            if (Input::get()->checkAction(Input::Action::SM_SELECT, Input::DO_NOT_ALLOW_REPEAT, Input::DO_NOT_CHECK_KEYBOARD, gamepad)) {
                ServiceMenu::get()->selectOption();
                return true;
            }

            // Atras
            if (Input::get()->checkAction(Input::Action::SM_BACK, Input::DO_NOT_ALLOW_REPEAT, Input::DO_NOT_CHECK_KEYBOARD, gamepad)) {
                ServiceMenu::get()->moveBack();
                return true;
            }
        }
    }
    return false;
}

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

        // Comprueba el teclado para decrementar el tamaño de la ventana
        if (Input::get()->checkAction(Input::Action::WINDOW_DEC_SIZE, Input::DO_NOT_ALLOW_REPEAT, Input::CHECK_KEYBOARD)) {
            if (Screen::get()->decWindowSize()) {
                Notifier::get()->show({Lang::getText("[NOTIFICATIONS] 09") + " x" + std::to_string(Options::window.zoom)});
            }
            return true;
        }

        // Comprueba el teclado para incrementar el tamaño de la ventana
        if (Input::get()->checkAction(Input::Action::WINDOW_INC_SIZE, Input::DO_NOT_ALLOW_REPEAT, Input::CHECK_KEYBOARD)) {
            if (Screen::get()->incWindowSize()) {
                Notifier::get()->show({Lang::getText("[NOTIFICATIONS] 09") + " x" + std::to_string(Options::window.zoom)});
            }
            return true;
        }

        // Salir
        if (Input::get()->checkAction(Input::Action::EXIT, Input::DO_NOT_ALLOW_REPEAT, Input::CHECK_KEYBOARD)) {
            quit();
            return true;
        }

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

        // Reset
        if (Input::get()->checkAction(Input::Action::RESET, Input::DO_NOT_ALLOW_REPEAT, Input::CHECK_KEYBOARD)) {
            reset();
            return true;
        }

        // Audio
        if (Input::get()->checkAction(Input::Action::TOGGLE_AUDIO, Input::DO_NOT_ALLOW_REPEAT, Input::CHECK_KEYBOARD)) {
            toggleAudio();
            return true;
        }

        // Autofire
        if (Input::get()->checkAction(Input::Action::TOGGLE_AUTO_FIRE, Input::DO_NOT_ALLOW_REPEAT, Input::CHECK_KEYBOARD)) {
            toggleFireMode();
            return true;
        }

        // Idioma
        if (Input::get()->checkAction(Input::Action::CHANGE_LANG, Input::DO_NOT_ALLOW_REPEAT, Input::CHECK_KEYBOARD)) {
            changeLang();
            return true;
        }

        // Shaders
        if (Input::get()->checkAction(Input::Action::TOGGLE_VIDEO_SHADERS, Input::DO_NOT_ALLOW_REPEAT, Input::CHECK_KEYBOARD)) {
            toggleShaders();
            return true;
        }

        // Integer Scale
        if (Input::get()->checkAction(Input::Action::TOGGLE_VIDEO_INTEGER_SCALE, Input::DO_NOT_ALLOW_REPEAT, Input::CHECK_KEYBOARD)) {
            toggleIntegerScale();
            return true;
        }

        // VSync
        if (Input::get()->checkAction(Input::Action::TOGGLE_VIDEO_VSYNC, Input::DO_NOT_ALLOW_REPEAT, Input::CHECK_KEYBOARD)) {
            toggleVSync();
            return true;
        }

#ifdef _DEBUG
        // Debug info
        if (Input::get()->checkAction(Input::Action::SHOW_INFO, Input::DO_NOT_ALLOW_REPEAT, Input::CHECK_KEYBOARD)) {
            Screen::get()->toggleDebugInfo();
            return true;
        }
#endif
    }
    return false;
}

// Comprueba los inputs que se pueden introducir en cualquier sección del juego
auto check() -> bool {
    if (checkServiceButton()) {
        return true;
    }
    if (checkServiceInputs()) {
        return true;
    }
    if (checkInputs()) {
        return true;
    }
    return false;
}
}  // namespace GlobalInputs