#pragma once

#include <SDL3/SDL.h>

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

#include "core/rendering/sprite/sprite.hpp"  // Para SSprite
class Surface;                               // Forward declaration

// Clase texto. Pinta texto en pantalla a partir de un bitmap con soporte UTF-8
class Text {
   public:
    // Tipos anidados públicos
    struct Offset {
        int x{0}, y{0}, w{0};
    };

    struct File {
        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
        int columns{16};                              // Número de columnas en el bitmap
        int cell_spacing{0};                          // Píxeles de separación entre columnas (y borde izquierdo/superior)
        int row_spacing{0};                           // Píxeles de separación entre filas (si difiere de cell_spacing)
        std::unordered_map<uint32_t, Offset> offset;  // Posición y ancho de cada glifo (clave: codepoint Unicode)
    };

    // Constructor
    Text(const std::shared_ptr<Surface>& surface, const std::string& text_file);
    Text(const std::shared_ptr<Surface>& surface, const std::shared_ptr<File>& text_file);

    // Destructor
    ~Text() = default;

    // Constantes de flags para writeDX
    static constexpr int COLOR_FLAG = 1;
    static constexpr int SHADOW_FLAG = 2;
    static constexpr int CENTER_FLAG = 4;
    static constexpr int STROKE_FLAG = 8;

    void write(int x, int y, const std::string& text, int kerning = 1, int lenght = -1);                                                                                                      // Escribe el texto en pantalla
    void writeColored(int x, int y, const std::string& text, Uint8 color, int kerning = 1, int lenght = -1);                                                                                  // Escribe el texto con colores
    void writeShadowed(int x, int y, const std::string& text, Uint8 color, Uint8 shadow_distance = 1, int kerning = 1, int lenght = -1);                                                      // Escribe el texto con sombra
    void writeCentered(int x, int y, const std::string& text, int kerning = 1, int lenght = -1);                                                                                              // Escribe el texto centrado en un punto x
    void writeDX(Uint8 flags, int x, int y, const std::string& text, int kerning = 1, Uint8 text_color = Uint8(), Uint8 shadow_distance = 1, Uint8 shadow_color = Uint8(), int lenght = -1);  // Escribe texto con extras

    auto writeToSurface(const std::string& text, int zoom = 1, int kerning = 1) -> std::shared_ptr<Surface>;                                                                                                         // Escribe el texto en una textura
    auto writeDXToSurface(Uint8 flags, const std::string& text, int kerning = 1, Uint8 text_color = Uint8(), Uint8 shadow_distance = 1, Uint8 shadow_color = Uint8(), int lenght = -1) -> std::shared_ptr<Surface>;  // Escribe el texto con extras en una textura

    void writeColoredMono(int x, int y, const std::string& text, Uint8 color, int cell_w);  // Escribe texto monoespaciado con color
    [[nodiscard]] auto length(const std::string& text, int kerning = 1) const -> int;       // Obtiene la longitud en pixels de una cadena
    [[nodiscard]] static auto lengthMono(const std::string& text, int cell_w) -> int;       // Obtiene la longitud en pixels de una cadena monoespaciada
    [[nodiscard]] auto getCharacterSize() const -> int;                                     // Devuelve el tamaño del caracter
    [[nodiscard]] auto glyphWidth(uint32_t codepoint, int kerning = 0) const -> int;        // Devuelve el ancho en pixels de un glifo
    [[nodiscard]] auto getGlyphClip(uint32_t codepoint) const -> SDL_FRect;                 // Devuelve el clip rect del glifo
    [[nodiscard]] auto getSprite() const -> Sprite* { return sprite_.get(); }               // Acceso al sprite interno

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

    static auto loadTextFile(const std::string& file_path) -> std::shared_ptr<File>;  // Carga un fichero de definición de fuente .fnt
    static auto codepointToUtf8(uint32_t cp) -> std::string;                          // Convierte un codepoint Unicode a string UTF-8
    static auto nextCodepoint(const std::string& s, size_t& pos) -> uint32_t;         // Extrae el siguiente codepoint UTF-8

   private:
    // Objetos y punteros
    std::unique_ptr<Sprite> sprite_ = nullptr;  // Objeto con los graficos para el texto

    // Variables
    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
    std::unordered_map<uint32_t, Offset> offset_;  // Posición y ancho de cada glifo (clave: codepoint Unicode)
};
