#define STB_IMAGE_IMPLEMENTATION
#include "texture.h"

#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_pack.h"  // Sistema de empaquetado de recursos

// Instancia global de ResourcePack (se inicializa al primer uso)
static ResourcePack* g_resourcePack = nullptr;

// Inicializar el sistema de recursos (llamar desde main antes de cargar texturas)
void Texture::initResourceSystem(const std::string& packFilePath) {
    if (g_resourcePack == nullptr) {
        g_resourcePack = new ResourcePack();
        if (!g_resourcePack->loadPack(packFilePath)) {
            // Si falla, borrar instancia (usará fallback a disco)
            delete g_resourcePack;
            g_resourcePack = nullptr;
            std::cout << "resources.pack no encontrado - usando carpeta data/" << std::endl;
        } else {
            std::cout << "resources.pack cargado (" << g_resourcePack->getResourceCount() << " recursos)" << std::endl;
        }
    }
}

// Obtener lista de recursos disponibles en el pack
std::vector<std::string> Texture::getPackResourceList() {
    if (g_resourcePack != nullptr) {
        return g_resourcePack->getResourceList();
    }
    return std::vector<std::string>();  // Vacío si no hay pack
}

// Verificar si el pack está cargado
bool Texture::isPackLoaded() {
    return g_resourcePack != nullptr;
}

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 pack (si está inicializado)
    if (g_resourcePack != nullptr) {
        ResourcePack::ResourceData packData = g_resourcePack->loadResource(file_path);
        if (packData.data != nullptr) {
            // Descodificar imagen desde memoria usando stb_image
            data = stbi_load_from_memory(packData.data, static_cast<int>(packData.size),
                                         &width, &height, &orig_format, req_format);
            delete[] packData.data;  // Liberar buffer temporal del pack

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

    // 2. Fallback: cargar desde disco (modo desarrollo)
    if (data == nullptr) {
        data = stbi_load(file_path.c_str(), &width, &height, &orig_format, req_format);
        if (data == nullptr) {
            SDL_Log("Error al cargar la imagen: %s", stbi_failure_reason());
            exit(1);
        } else {
            std::cout << "Imagen cargada desde disco: " << filename.c_str() << std::endl;
        }
    }

    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);
        }

        // 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);
}
