#pragma once

#include <SDL2/SDL_blendmode.h> // para SDL_BlendMode
#include <SDL2/SDL_pixels.h>    // para SDL_PIXELFORMAT_RGBA8888, SDL_PixelF...
#include <SDL2/SDL_rect.h>      // para SDL_Point, SDL_Rect
#include <SDL2/SDL_render.h>    // para SDL_Renderer, SDL_FLIP_NONE, SDL_TEX...
#include <SDL2/SDL_stdinc.h>    // para Uint8, Uint32, Uint16
#include <string>               // para string, basic_string
#include <vector>               // para vector
#include <memory>

// Definiciones de tipos
struct Surface
{
    std::shared_ptr<Uint8[]> data;
    Uint16 w, h;

    // Constructor
    Surface(Uint16 width, Uint16 height, std::shared_ptr<Uint8[]> pixels)
        : data(pixels), w(width), h(height) {}
};

class Texture
{
private:
    // Objetos y punteros
    SDL_Renderer *renderer_;                     // Renderizador donde dibujar la textura
    SDL_Texture *texture_ = nullptr;             // La textura
    std::shared_ptr<Surface> surface_ = nullptr; // Surface para usar imagenes en formato gif con paleta

    // Variables
    std::string path_;                          // Ruta de la imagen de la textura
    int width_ = 0;                             // Ancho de la imagen
    int height_ = 0;                            // Alto de la imagen
    std::vector<std::vector<Uint32>> palettes_; // Vector con las diferentes paletas
    int current_palette_ = 0;                   // Indice de la paleta en uso

    // Crea una surface desde un fichero .gif
    std::shared_ptr<Surface> loadSurface(const std::string &file_name);

    // Vuelca la surface en la textura
    void flipSurface();

    // Carga una paleta desde un fichero
    std::vector<Uint32> loadPaletteFromFile(const std::string &file_name);

    // Libera la memoria de la textura
    void unloadTexture();

    // Desencadenar la superficie actual
    void unloadSurface();

public:
    // Constructor
    explicit Texture(SDL_Renderer *renderer, const std::string &path = std::string());

    // Destructor
    ~Texture();

    // Carga una imagen desde un fichero
    bool loadFromFile(const std::string &path);

    // Crea una textura en blanco
    bool createBlank(int width, int height, SDL_PixelFormatEnum format = SDL_PIXELFORMAT_RGBA8888, SDL_TextureAccess = SDL_TEXTUREACCESS_STREAMING);

    // Establece el color para la modulacion
    void setColor(Uint8 red, Uint8 green, Uint8 blue);

    // Establece el blending
    void setBlendMode(SDL_BlendMode blending);

    // Establece el alpha para la modulación
    void setAlpha(Uint8 alpha);

    // Renderiza la textura en un punto específico
    void render(int x, int y, SDL_Rect *clip = nullptr, float zoomW = 1, float zoomH = 1, double angle = 0.0, SDL_Point *center = nullptr, SDL_RendererFlip flip = SDL_FLIP_NONE);

    // Establece la textura como objetivo de renderizado
    void setAsRenderTarget(SDL_Renderer *renderer);

    // Obtiene el ancho de la imagen
    int getWidth();

    // Obtiene el alto de la imagen
    int getHeight();

    // Recarga la textura
    bool reLoad();

    // Obtiene la textura
    SDL_Texture *getSDLTexture();

    // Añade una paleta a la lista
    void addPaletteFromFile(const std::string &path);

    // Establece un color de la paleta
    void setPaletteColor(int palette, int index, Uint32 color);

    // Cambia la paleta de la textura
    void setPalette(int palette);

    // Obtiene el renderizador
    SDL_Renderer *getRenderer();
};