#define STB_IMAGE_IMPLEMENTATION
#include "texture.hpp"

#include <SDL3/SDL_error.h>    // Para SDL_GetError
#include <SDL3/SDL_log.h>      // Para SDL_Log
#include <SDL3/SDL_pixels.h>   // Para SDL_PixelFormat
#include <SDL3/SDL_surface.h>  // Para SDL_CreateSurfaceFrom, SDL_DestroySurface
#include <stdio.h>             // Para NULL
#include <stdlib.h>            // Para exit

#include <iostream>  // Para basic_ostream, char_traits, operator<<
#include <string>    // Para operator<<, string

#include "stb_image.h"  // Para stbi_failure_reason, stbi_image_free
#include "resource_manager.hpp"  // Sistema de empaquetado de recursos centralizado

Texture::Texture(SDL_Renderer *renderer)
    : renderer_(renderer),
      texture_(nullptr),
      width_(0),
      height_(0) {}

Texture::Texture(SDL_Renderer *renderer, const std::string &file_path)
    : renderer_(renderer),
      texture_(nullptr),
      width_(0),
      height_(0) {
    loadFromFile(file_path);
}

Texture::~Texture() {
    free();
}

// Carga la imagen desde una ruta especificada
bool Texture::loadFromFile(const std::string &file_path) {
    const std::string filename = file_path.substr(file_path.find_last_of("\\/") + 1);
    int req_format = STBI_rgb_alpha;
    int width, height, orig_format;
    unsigned char *data = nullptr;

    // 1. Intentar cargar desde ResourceManager (pack o disco)
    unsigned char* resourceData = nullptr;
    size_t resourceSize = 0;

    if (ResourceManager::loadResource(file_path, resourceData, resourceSize)) {
        // Descodificar imagen desde memoria usando stb_image
        data = stbi_load_from_memory(resourceData, static_cast<int>(resourceSize),
                                     &width, &height, &orig_format, req_format);
        delete[] resourceData;  // Liberar buffer temporal

        if (data != nullptr) {
            if (ResourceManager::isPackLoaded()) {
                std::cout << "Imagen cargada desde pack: " << filename.c_str() << std::endl;
            } else {
                std::cout << "Imagen cargada desde disco: " << filename.c_str() << std::endl;
            }
        }
    }

    // 2. Si todo falla, error
    if (data == nullptr) {
        SDL_Log("Error al cargar la imagen: %s", stbi_failure_reason());
        exit(1);
    }

    int pitch;
    SDL_PixelFormat pixel_format;
    if (req_format == STBI_rgb) {
        pitch = 3 * width;  // 3 bytes por pixel * pixels por línea
        pixel_format = SDL_PIXELFORMAT_RGB24;
    } else {  // STBI_rgb_alpha (RGBA)
        pitch = 4 * width;
        pixel_format = SDL_PIXELFORMAT_RGBA32;
    }

    // Libera la memoria previa
    free();

    // La textura final
    SDL_Texture *new_texture = nullptr;

    // Crea la superficie de la imagen desde los datos cargados
    SDL_Surface *loaded_surface = SDL_CreateSurfaceFrom(width, height, pixel_format, (void *)data, pitch);
    if (loaded_surface == nullptr) {
        std::cout << "No se pudo cargar la imagen " << file_path << std::endl;
    } else {
        // Crea la textura desde los píxeles de la superficie
        new_texture = SDL_CreateTextureFromSurface(renderer_, loaded_surface);
        if (new_texture == nullptr) {
            std::cout << "No se pudo crear la textura desde " << file_path << "! Error de SDL: " << SDL_GetError() << std::endl;
        } else {
            // Obtiene las dimensiones de la imagen
            width_ = loaded_surface->w;
            height_ = loaded_surface->h;

            // Configurar filtro nearest neighbor para píxel perfect
            SDL_SetTextureScaleMode(new_texture, SDL_SCALEMODE_NEAREST);

            // Habilitar alpha blending para transparencias
            SDL_SetTextureBlendMode(new_texture, SDL_BLENDMODE_BLEND);
        }

        // Destruye la superficie cargada
        SDL_DestroySurface(loaded_surface);
    }

    // Devuelve el resultado del proceso
    stbi_image_free(data);
    texture_ = new_texture;
    return texture_ != nullptr;
}

// Libera la textura si existe
void Texture::free() {
    if (texture_ != nullptr) {
        SDL_DestroyTexture(texture_);
        texture_ = nullptr;
        width_ = 0;
        height_ = 0;
    }
}

// Renderiza la textura en pantalla
void Texture::render(SDL_FRect *src, SDL_FRect *dst) {
    SDL_RenderTexture(renderer_, texture_, src, dst);
}

// Obtiene el ancho de la imagen
int Texture::getWidth() {
    return width_;
}

// Obtiene la altura de la imagen
int Texture::getHeight() {
    return height_;
}

// Modula el color de la textura
void Texture::setColor(int r, int g, int b) {
    SDL_SetTextureColorMod(texture_, r, g, b);
}

// Modula el alpha de la textura
void Texture::setAlpha(int alpha) {
    if (texture_ != nullptr) {
        SDL_SetTextureAlphaMod(texture_, static_cast<Uint8>(alpha));
    }
}

// Configurar modo de escalado
void Texture::setScaleMode(SDL_ScaleMode mode) {
    if (texture_ != nullptr) {
        SDL_SetTextureScaleMode(texture_, mode);
    }
}
