#include "game/scenes/credits.hpp"

#include <SDL3/SDL.h>

#include <algorithm>  // Para min

#include "core/input/global_inputs.hpp"                // Para check
#include "core/rendering/screen.hpp"                   // Para Screen
#include "core/rendering/surface.hpp"                  // Para Surface
#include "core/rendering/surface_animated_sprite.hpp"  // Para SAnimatedSprite
#include "core/rendering/text.hpp"                     // Para Text, TEXT_CENTER, TEXT_COLOR
#include "core/resources/resource.hpp"                 // Para Resource
#include "game/options.hpp"                            // Para Options, options, OptionsGame, Sectio...
#include "game/scene_manager.hpp"                      // Para SceneManager
#include "utils/defines.hpp"                           // Para GAME_SPEED, PLAY_AREA_CENTER_X, PLAY_...
#include "core/system/global_events.hpp"               // Para check
#include "utils/utils.hpp"                             // Para PaletteColor

// Constructor
Credits::Credits()
    : shining_sprite_(std::make_shared<SurfaceAnimatedSprite>(Resource::get()->getSurface("shine.gif"), Resource::get()->getAnimations("shine.ani"))) {
    // Inicializa variables
    SceneManager::current = SceneManager::Scene::CREDITS;
    SceneManager::options = SceneManager::Options::NONE;
    shining_sprite_->setPos({194, 174, 8, 8});

    // Cambia el color del borde
    Screen::get()->setBorderColor(static_cast<Uint8>(PaletteColor::BLACK));

    // Crea la textura para el texto que se escribe en pantalla
    text_surface_ = std::make_shared<Surface>(Options::game.width, Options::game.height);

    // Crea la textura para cubrir el rexto
    cover_surface_ = std::make_shared<Surface>(Options::game.width, Options::game.height);

    // Escribe el texto en la textura
    fillTexture();
}

// Comprueba el manejador de eventos
void Credits::checkEvents() {
    SDL_Event event;
    while (SDL_PollEvent(&event)) {
        GlobalEvents::check(event);
    }
}

// Comprueba las entradas
void Credits::checkInput() {
    GlobalInputs::check();
}

// Inicializa los textos
void Credits::iniTexts() {
    std::string keys;

    switch (Options::keys) {
        case Options::ControlScheme::CURSOR:
            keys = "CURSORS";
            break;
        case Options::ControlScheme::OPQA:
            keys = "O,P AND Q";
            break;
        case Options::ControlScheme::WASD:
            keys = "A,D AND W";
            break;
        default:
            break;
    }

    texts_.clear();
    texts_.push_back({"", static_cast<Uint8>(PaletteColor::WHITE)});
    texts_.push_back({"INSTRUCTIONS:", static_cast<Uint8>(PaletteColor::YELLOW)});
    texts_.push_back({"", static_cast<Uint8>(PaletteColor::WHITE)});
    texts_.push_back({"HELP JAILDOC TO GET BACK ALL", static_cast<Uint8>(PaletteColor::WHITE)});
    texts_.push_back({"HIS PROJECTS AND GO TO THE", static_cast<Uint8>(PaletteColor::WHITE)});
    texts_.push_back({"JAIL TO FINISH THEM", static_cast<Uint8>(PaletteColor::WHITE)});
    texts_.push_back({"", static_cast<Uint8>(PaletteColor::WHITE)});
    texts_.push_back({"", static_cast<Uint8>(PaletteColor::WHITE)});

    texts_.push_back({"KEYS:", static_cast<Uint8>(PaletteColor::YELLOW)});
    texts_.push_back({"", static_cast<Uint8>(PaletteColor::WHITE)});
    texts_.push_back({keys + " TO MOVE AND JUMP", static_cast<Uint8>(PaletteColor::WHITE)});
    texts_.push_back({"M TO SWITCH THE MUSIC", static_cast<Uint8>(PaletteColor::WHITE)});
    texts_.push_back({"H TO PAUSE THE GAME", static_cast<Uint8>(PaletteColor::WHITE)});
    texts_.push_back({"F1-F2 TO CHANGE WINDOWS SIZE", static_cast<Uint8>(PaletteColor::WHITE)});
    texts_.push_back({"F3 TO SWITCH TO FULLSCREEN", static_cast<Uint8>(PaletteColor::WHITE)});
    texts_.push_back({"B TO TOOGLE THE BORDER SCREEN", static_cast<Uint8>(PaletteColor::WHITE)});
    texts_.push_back({"", static_cast<Uint8>(PaletteColor::WHITE)});
    texts_.push_back({"", static_cast<Uint8>(PaletteColor::WHITE)});

    texts_.push_back({"A GAME BY JAILDESIGNER", static_cast<Uint8>(PaletteColor::YELLOW)});
    texts_.push_back({"MADE ON SUMMER/FALL 2022", static_cast<Uint8>(PaletteColor::YELLOW)});
    texts_.push_back({"", static_cast<Uint8>(PaletteColor::WHITE)});
    texts_.push_back({"", static_cast<Uint8>(PaletteColor::WHITE)});

    texts_.push_back({"I LOVE JAILGAMES! ", static_cast<Uint8>(PaletteColor::WHITE)});
    texts_.push_back({"", static_cast<Uint8>(PaletteColor::WHITE)});
}

// Escribe el texto en la textura
void Credits::fillTexture() {
    // Inicializa los textos
    iniTexts();

    // Rellena la textura de texto
    auto previuos_renderer = Screen::get()->getRendererSurface();
    Screen::get()->setRendererSurface(text_surface_);
    text_surface_->clear(static_cast<Uint8>(PaletteColor::BLACK));

    auto text = Resource::get()->getText("smb2");

    // Escribe el texto en la textura
    const int SIZE = text->getCharacterSize();
    int pos_y = 0;

    for (const auto& t : texts_) {
        text->writeDX(TEXT_CENTER | TEXT_COLOR, PLAY_AREA_CENTER_X, pos_y * SIZE, t.label, 1, t.color);
        pos_y++;
    }

    // Escribe el corazón
    const int TEXT_LENGHT = text->lenght(texts_[22].label, 1) - text->lenght(" ", 1);  // Se resta el ultimo caracter que es un espacio
    const int POS_X = ((PLAY_AREA_WIDTH - TEXT_LENGHT) / 2) + TEXT_LENGHT;
    text->writeColored(POS_X, 176, "}", static_cast<Uint8>(PaletteColor::BRIGHT_RED));
    Screen::get()->setRendererSurface(previuos_renderer);

    // Recoloca el sprite del brillo
    shining_sprite_->setPosX(POS_X + 2);

    // Rellena la textura que cubre el texto con color transparente
    cover_surface_->clear(static_cast<Uint8>(PaletteColor::TRANSPARENT));

    // Los primeros 8 pixels crea una malla
    auto color = static_cast<Uint8>(PaletteColor::BLACK);
    for (int i = 0; i < 256; i += 2) {
        cover_surface_->putPixel(i, 0, color);
        cover_surface_->putPixel(i, 2, color);
        cover_surface_->putPixel(i, 4, color);
        cover_surface_->putPixel(i, 6, color);

        cover_surface_->putPixel(i + 1, 5, color);
        cover_surface_->putPixel(i + 1, 7, color);
    }

    // El resto se rellena de color sólido
    SDL_FRect rect = {0, 8, 256, 192};
    cover_surface_->fillRect(&rect, color);
}

// Actualiza el contador
void Credits::updateCounter() {
    // Incrementa el contador
    if (counter_enabled_) {
        counter_++;
        if (counter_ == 224 || counter_ == 544 || counter_ == 672) {
            counter_enabled_ = false;
        }
    } else {
        sub_counter_++;
        if (sub_counter_ == 100) {
            counter_enabled_ = true;
            sub_counter_ = 0;
        }
    }

    // Comprueba si ha terminado la sección
    if (counter_ > 1200) {
        SceneManager::current = SceneManager::Scene::DEMO;
    }
}

// Actualiza las variables
void Credits::update() {
    // Comprueba que la diferencia de ticks sea mayor a la velocidad del juego
    if (SDL_GetTicks() - ticks_ > GAME_SPEED) {
        // Actualiza el contador de ticks
        ticks_ = SDL_GetTicks();

        // Comprueba las entradas
        checkInput();

        // Actualiza el contador
        updateCounter();

        Screen::get()->update();

        // Actualiza el sprite con el brillo
        if (counter_ > 770) {
            shining_sprite_->update();
        }
    }
}

// Dibuja en pantalla
void Credits::render() {
    // Prepara para empezar a dibujar en la textura de juego
    Screen::get()->start();

    // Limpia la pantalla
    Screen::get()->clearSurface(static_cast<Uint8>(PaletteColor::BLACK));

    if (counter_ < 1150) {
        // Dibuja la textura con el texto en pantalla
        text_surface_->render(0, 0);

        // Dibuja la textura que cubre el texto
        const int OFFSET = std::min(counter_ / 8, 192 / 2);
        SDL_FRect src_rect = {0.0F, 0.0F, 256.0F, 192.0F - (OFFSET * 2.0F)};
        cover_surface_->render(0, OFFSET * 2, &src_rect);

        // Dibuja el sprite con el brillo
        shining_sprite_->render(1, static_cast<Uint8>(PaletteColor::BRIGHT_WHITE));
    }

    // Vuelca el contenido del renderizador en pantalla
    Screen::get()->render();
}

// Bucle para el logo del juego
void Credits::run() {
    while (SceneManager::current == SceneManager::Scene::CREDITS) {
        update();
        checkEvents();
        render();
    }
}