#pragma once

#include <SDL3/SDL.h>  // Para Uint8

#include <array>   // Para array
#include <memory>  // Para shared_ptr, unique_ptr
#include <string>  // Para string

#include "core/rendering/sprite/sprite.hpp"  // Para Sprite
#include "utils/color.hpp"                   // Para Color

class Texture;

// --- Clase Text: pinta texto en pantalla a partir de un bitmap ---
class Text {
   public:
    // --- Constantes para flags de texto ---
    static constexpr int COLOR = 1;
    static constexpr int SHADOW = 2;
    static constexpr int CENTER = 4;
    static constexpr int STROKE = 8;

    // --- Estructuras ---
    struct Offset {
        int x, y, w;
    };

    struct File {
        int box_width;                        // Anchura de la caja de cada caracter en el png
        int box_height;                       // Altura de la caja de cada caracter en el png
        std::array<Offset, 128> offset = {};  // Vector con las posiciones y ancho de cada letra
    };

    struct Style {
        Uint8 flags;
        Color text_color;
        Color shadow_color;
        Uint8 shadow_distance;
        int kerning;

        // Constructor con argumentos por defecto
        Style(Uint8 flags = 0,
            Color text = Color(),
            Color shadow = Color(),
            Uint8 distance = 1,
            int kern = 1)
            : flags(flags),
              text_color(text),
              shadow_color(shadow),
              shadow_distance(distance),
              kerning(kern) {}
    };

    // --- Constructores y destructor ---
    Text(const std::shared_ptr<Texture>& texture, const std::string& text_file);
    Text(const std::shared_ptr<Texture>& texture, const std::shared_ptr<Text::File>& text_file);
    Text(const std::shared_ptr<Texture>& texture, const std::shared_ptr<Texture>& white_texture, const std::string& text_file);
    Text(const std::shared_ptr<Texture>& texture, const std::shared_ptr<Texture>& white_texture, const std::shared_ptr<Text::File>& text_file);
    ~Text() = default;

    // --- Métodos de escritura en pantalla ---
    void write(int x, int y, const std::string& text, int kerning = 1, int length = -1);    // Escribe el texto en pantalla
    void write2X(int x, int y, const std::string& text, int kerning = 1, int length = -1);  // Escribe el texto al doble de tamaño

    // --- Escritura en textura ---
    auto writeToTexture(const std::string& text, int zoom = 1, int kerning = 1, int length = -1) -> std::shared_ptr<Texture>;                                                                                        // Escribe el texto en una textura
    auto writeDXToTexture(Uint8 flags, const std::string& text, int kerning = 1, Color text_color = Color(), Uint8 shadow_distance = 1, Color shadow_color = Color(), int length = -1) -> std::shared_ptr<Texture>;  // Escribe el texto con extras en una textura

    // --- Métodos de escritura avanzada ---
    void writeColored(int x, int y, const std::string& text, Color color, int kerning = 1, int length = -1);                                                                                  // Escribe el texto con colores
    void writeShadowed(int x, int y, const std::string& text, Color color, Uint8 shadow_distance = 1, int kerning = 1, int length = -1);                                                      // Escribe el texto con sombra
    void writeCentered(int x, int y, const std::string& text, int kerning = 1, int length = -1);                                                                                              // Escribe el texto centrado en un punto x
    void writeDX(Uint8 flags, int x, int y, const std::string& text, int kerning = 1, Color text_color = Color(), Uint8 shadow_distance = 1, Color shadow_color = Color(), int length = -1);  // Escribe texto con extras
    void writeStyle(int x, int y, const std::string& text, const Style& style, int length = -1);                                                                                              // Escribe texto a partir de un TextStyle

    // --- Utilidades ---
    [[nodiscard]] auto length(const std::string& text, int kerning = 1) const -> int;  // Obtiene la longitud en pixels de una cadena
    [[nodiscard]] auto getCharacterSize() const -> int;                                // Devuelve el tamaño de caracter actual

    // --- Configuración ---
    void setFixedWidth(bool value);  // Establece si se usa un tamaño fijo de letra

    // --- Métodos estáticos ---
    static auto loadFile(const std::string& file_path) -> std::shared_ptr<Text::File>;  // Llena una estructura Text::File desde un fichero

    // --- Métodos privados ---
    void writeColoredWithSprite(Sprite* sprite, int x, int y, const std::string& text, Color color, int kerning = 1, int length = -1);          // Escribe con un sprite específico
    void writeStrokeWithAlpha(int x, int y, const std::string& text, int kerning, Color stroke_color, Uint8 shadow_distance, int length = -1);  // Escribe stroke con alpha correcto
    void renderShadow(int x, int y, const std::string& text, Color shadow_color, int kerning, int length, Uint8 shadow_distance);               // Renderiza sombra del texto
    void renderSolidStroke(int x, int y, const std::string& text, Color stroke_color, int kerning, int length, Uint8 shadow_distance);          // Renderiza stroke sólido

   private:
    // --- Objetos y punteros ---
    std::unique_ptr<Sprite> sprite_ = nullptr;        // Objeto con los gráficos para el texto
    std::unique_ptr<Sprite> white_sprite_ = nullptr;  // Objeto con los gráficos en blanco para efectos

    // --- Variables de estado ---
    std::array<Offset, 128> offset_ = {};  // Vector con las posiciones y ancho de cada letra
    int box_width_ = 0;                    // Anchura de la caja de cada caracter en el png
    int box_height_ = 0;                   // Altura de la caja de cada caracter en el png
    bool fixed_width_ = false;             // Indica si el texto se ha de escribir con longitud fija
};