#include "tiled_bg.h"
#include <SDL3/SDL_pixels.h> // Para SDL_PIXELFORMAT_RGBA8888
#include <stdlib.h>          // Para rand
#include <cmath>             // Para sin
#include <memory>            // Para unique_ptr, make_unique
#include "resource.h"        // Para Resource
#include "screen.h"          // Para Screen
#include "sprite.h"          // Para Sprite

// Constructor
TiledBG::TiledBG(SDL_FRect pos, TiledBGMode mode)
    : renderer_(Screen::get()->getRenderer()),
      pos_(pos),
      mode_(mode == TiledBGMode::RANDOM ? static_cast<TiledBGMode>(rand() % 2) : mode)
{
    // Crea la textura para el mosaico de fondo
    canvas_ = SDL_CreateTexture(renderer_, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, pos_.w * 2, pos_.h * 2);

    // Rellena la textura con el contenido
    fillTexture();

    // Inicializa variables
    switch (mode_)
    {
    case TiledBGMode::STATIC:
        window_ = {0, 0, pos_.w, pos_.h};
        speed_ = 0.0f;
        break;
    case TiledBGMode::DIAGONAL:
        window_ = {0, 0, pos_.w, pos_.h};
        break;
    case TiledBGMode::CIRCLE:
        window_ = {128, 128, pos_.w, pos_.h};
        break;
    default:
        window_ = {0, 0, pos_.w, pos_.h};
        break;
    }

    // Inicializa los valores del vector con los valores del seno
    for (int i = 0; i < 360; ++i)
    {
        sin_[i] = std::sin(i * 3.14159 / 180.0); // Convierte grados a radianes y calcula el seno
    }
}

// Destructor
TiledBG::~TiledBG()
{
    SDL_DestroyTexture(canvas_);
}

// Rellena la textura con el contenido
void TiledBG::fillTexture()
{
    // Crea los objetos para pintar en la textura de fondo
    auto tile = std::make_unique<Sprite>(Resource::get()->getTexture("title_bg_tile.png"), (SDL_FRect){0, 0, TILE_WIDTH_, TILE_HEIGHT_});

    // Prepara para dibujar sobre la textura
    auto temp = SDL_GetRenderTarget(renderer_);
    SDL_SetRenderTarget(renderer_, canvas_);

    // Rellena la textura con el tile
    const auto i_max = pos_.w * 2 / TILE_WIDTH_;
    const auto j_max = pos_.h * 2 / TILE_HEIGHT_;
    tile->setSpriteClip(0, 0, TILE_WIDTH_, TILE_HEIGHT_);
    for (int i = 0; i < i_max; ++i)
    {
        for (int j = 0; j < j_max; ++j)
        {
            tile->setX(i * TILE_WIDTH_);
            tile->setY(j * TILE_HEIGHT_);
            tile->render();
        }
    }

    // Vuelve a colocar el renderizador como estaba
    SDL_SetRenderTarget(renderer_, temp);
}

// Pinta la clase en pantalla
void TiledBG::render()
{
    SDL_RenderTexture(renderer_, canvas_, &window_, &pos_);
}

// Actualiza la lógica de la clase
void TiledBG::update()
{
    updateDesp();
    updateStop();

    switch (mode_)
    {
    case TiledBGMode::DIAGONAL:
    {
        // El tileado de fondo se desplaza en diagonal
        window_.x = static_cast<int>(desp_) % TILE_WIDTH_;
        window_.y = static_cast<int>(desp_) % TILE_HEIGHT_;

        break;
    }
    case TiledBGMode::CIRCLE:
    {
        // El tileado de fondo se desplaza en circulo
        const int INDEX = static_cast<int>(desp_) % 360;

        window_.x = 128 + (static_cast<int>(sin_[(INDEX + 270) % 360] * 128));
        window_.y = 128 + (static_cast<int>(sin_[(360 - INDEX) % 360] * 96));
        break;
    }
    default:
        break;
    }
}

// Detiene el desplazamiento de forma ordenada
void TiledBG::updateStop()
{
    if (stopping_)
    {
        const int UMBRAL = 20 * speed_; // Ajusta este valor según la precisión deseada

        // Desacelerar si estamos cerca de completar el ciclo (ventana a punto de regresar a 0)
        if (window_.x >= TILE_WIDTH_ - UMBRAL)
        {
            speed_ /= 1.05f; // Reduce gradualmente la velocidad

            // Asegura que no baje demasiado
            if (speed_ < 0.1f)
            {
                speed_ = 0.1f;
            }
        }

        // Si estamos en 0, detener
        if (window_.x == 0)
        {
            speed_ = 0.0f;
            stopping_ = false; // Desactivamos el estado de "stopping"
        }
    }
}