#include "game/gameplay/scoreboard.hpp"

#include <SDL3/SDL.h>

#include <utility>

#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
#include "core/resources/resource.hpp"                 // Para Resource
#include "game/options.hpp"                            // Para Options, options, Cheat, OptionsGame
#include "utils/defines.hpp"                           // Para BLOCK
#include "utils/utils.hpp"                             // Para stringToColor

// Constructor
Scoreboard::Scoreboard(std::shared_ptr<ScoreboardData> data)
    : item_surface_(Resource::get()->getSurface("items.gif")),
      data_(std::move(std::move(data))) {
    const float SURFACE_WIDTH = Options::game.width;
    constexpr float SURFACE_HEIGHT = 6.0F * BLOCK;

    // Reserva memoria para los objetos
    auto player_texture = Resource::get()->getSurface(Options::cheats.alternate_skin == Options::Cheat::State::ENABLED ? "player2.gif" : "player.gif");
    auto player_animations = Resource::get()->getAnimations(Options::cheats.alternate_skin == Options::Cheat::State::ENABLED ? "player2.ani" : "player.ani");
    player_sprite_ = std::make_shared<SurfaceAnimatedSprite>(player_texture, player_animations);
    player_sprite_->setCurrentAnimation("walk_menu");

    surface_ = std::make_shared<Surface>(SURFACE_WIDTH, SURFACE_HEIGHT);
    surface_dest_ = {.x = 0, .y = Options::game.height - SURFACE_HEIGHT, .w = SURFACE_WIDTH, .h = SURFACE_HEIGHT};

    // Inicializa las variables
    counter_ = 0;
    change_color_speed_ = 4;
    is_paused_ = false;
    paused_time_ = 0;
    paused_time_elapsed_ = 0;
    items_color_ = stringToColor("white");

    // Inicializa el vector de colores
    const std::vector<std::string> COLORS = {"blue", "magenta", "green", "cyan", "yellow", "white", "bright_blue", "bright_magenta", "bright_green", "bright_cyan", "bright_yellow", "bright_white"};
    for (const auto& color : COLORS) {
        color_.push_back(stringToColor(color));
    }
}

// Pinta el objeto en pantalla
void Scoreboard::render() {
    surface_->render(nullptr, &surface_dest_);
}

// Actualiza las variables del objeto
void Scoreboard::update() {
    counter_++;
    player_sprite_->update();

    // Actualiza el color de la cantidad de items recogidos
    updateItemsColor();

    // Dibuja la textura
    fillTexture();

    if (!is_paused_) {
        // Si está en pausa no se actualiza el reloj
        clock_ = getTime();
    }
}

// Obtiene el tiempo transcurrido de partida
auto Scoreboard::getTime() -> Scoreboard::ClockData {
    const Uint32 TIME_ELAPSED = SDL_GetTicks() - data_->ini_clock - paused_time_elapsed_;

    ClockData time;
    time.hours = TIME_ELAPSED / 3600000;
    time.minutes = TIME_ELAPSED / 60000;
    time.seconds = TIME_ELAPSED / 1000;
    time.separator = (TIME_ELAPSED % 1000 <= 500) ? ":" : " ";

    return time;
}

// Pone el marcador en modo pausa
void Scoreboard::setPaused(bool value) {
    if (is_paused_ == value) {
        // Evita ejecutar lógica si el estado no cambia
        return;
    }

    is_paused_ = value;

    if (is_paused_) {
        // Guarda el tiempo actual al pausar
        paused_time_ = SDL_GetTicks();
    } else {
        // Calcula el tiempo pausado acumulado al reanudar
        paused_time_elapsed_ += SDL_GetTicks() - paused_time_;
    }
}

// Actualiza el color de la cantidad de items recogidos
void Scoreboard::updateItemsColor() {
    if (!data_->jail_is_open) {
        return;
    }

    if (counter_ % 20 < 10) {
        items_color_ = stringToColor("white");
    } else {
        items_color_ = stringToColor("magenta");
    }
}

// Devuelve la cantidad de minutos de juego transcurridos
auto Scoreboard::getMinutes() -> int {
    return getTime().minutes;
}

// Dibuja los elementos del marcador en la textura
void Scoreboard::fillTexture() {
    // Empieza a dibujar en la textura
    auto previuos_renderer = Screen::get()->getRendererSurface();
    Screen::get()->setRendererSurface(surface_);

    // Limpia la textura
    surface_->clear(stringToColor("black"));

    // Anclas
    constexpr int LINE1 = BLOCK;
    constexpr int LINE2 = 3 * BLOCK;

    // Dibuja las vidas
    const int DESP = (counter_ / 40) % 8;
    const int FRAME = DESP % 4;
    player_sprite_->setCurrentAnimationFrame(FRAME);
    player_sprite_->setPosY(LINE2);
    for (int i = 0; i < data_->lives; ++i) {
        player_sprite_->setPosX(8 + (16 * i) + DESP);
        const int INDEX = i % color_.size();
        player_sprite_->render(1, color_.at(INDEX));
    }

    // Muestra si suena la música
    if (data_->music) {
        const Uint8 C = data_->color;
        SDL_FRect clip = {0, 8, 8, 8};
        item_surface_->renderWithColorReplace(20 * BLOCK, LINE2, 1, C, &clip);
    }

    // Escribe los textos
    auto text = Resource::get()->getText("smb2");
    const std::string TIME_TEXT = std::to_string((clock_.minutes % 100) / 10) + std::to_string(clock_.minutes % 10) + clock_.separator + std::to_string((clock_.seconds % 60) / 10) + std::to_string(clock_.seconds % 10);
    const std::string ITEMS_TEXT = std::to_string(data_->items / 100) + std::to_string((data_->items % 100) / 10) + std::to_string(data_->items % 10);
    text->writeColored(BLOCK, LINE1, "Items collected ", data_->color);
    text->writeColored(17 * BLOCK, LINE1, ITEMS_TEXT, items_color_);
    text->writeColored(20 * BLOCK, LINE1, " Time ", data_->color);
    text->writeColored(26 * BLOCK, LINE1, TIME_TEXT, stringToColor("white"));

    const std::string ROOMS_TEXT = std::to_string(data_->rooms / 100) + std::to_string((data_->rooms % 100) / 10) + std::to_string(data_->rooms % 10);
    text->writeColored(22 * BLOCK, LINE2, "Rooms", stringToColor("white"));
    text->writeColored(28 * BLOCK, LINE2, ROOMS_TEXT, stringToColor("white"));

    // Deja el renderizador como estaba
    Screen::get()->setRendererSurface(previuos_renderer);
}