#include "textrenderer.hpp"
#include <SDL3/SDL.h>
#include <SDL3_ttf/SDL_ttf.h>
#include "resource_manager.hpp"

TextRenderer::TextRenderer() : renderer_(nullptr), font_(nullptr), font_size_(0), use_antialiasing_(true), font_data_buffer_(nullptr) {
}

TextRenderer::~TextRenderer() {
    cleanup();
}

bool TextRenderer::init(SDL_Renderer* renderer, const char* font_path, int font_size, bool use_antialiasing) {
    renderer_ = renderer;
    font_size_ = font_size;
    use_antialiasing_ = use_antialiasing;
    font_path_ = font_path;  // Guardar ruta para reinitialize()

    // Inicializar SDL_ttf si no está inicializado
    if (!TTF_WasInit()) {
        if (!TTF_Init()) {
            SDL_Log("Error al inicializar SDL_ttf: %s", SDL_GetError());
            return false;
        }
    }

    // Intentar cargar la fuente desde ResourceManager (pack o disco)
    unsigned char* fontData = nullptr;
    size_t fontDataSize = 0;

    if (ResourceManager::loadResource(font_path, fontData, fontDataSize)) {
        // Crear SDL_IOStream desde memoria
        SDL_IOStream* fontIO = SDL_IOFromConstMem(fontData, static_cast<size_t>(fontDataSize));
        if (fontIO != nullptr) {
            // Cargar fuente desde IOStream
            font_ = TTF_OpenFontIO(fontIO, true, font_size);  // true = cerrar stream automáticamente

            if (font_ == nullptr) {
                SDL_Log("Error al cargar fuente desde memoria '%s': %s", font_path, SDL_GetError());
                delete[] fontData;  // Liberar solo si falla la carga
                return false;
            }

            // CRÍTICO: NO eliminar fontData aquí - SDL_ttf necesita estos datos en memoria
            // mientras la fuente esté abierta. Se liberará en cleanup()
            font_data_buffer_ = fontData;
            SDL_Log("Fuente cargada desde ResourceManager: %s (%lu bytes)", font_path, (unsigned long)fontDataSize);
            return true;
        } else {
            delete[] fontData;
        }
    }

    // Fallback final: intentar cargar directamente desde disco (por si falla ResourceManager)
    font_ = TTF_OpenFont(font_path, font_size);
    if (font_ == nullptr) {
        SDL_Log("Error al cargar fuente '%s': %s", font_path, SDL_GetError());
        return false;
    }

    return true;
}

bool TextRenderer::reinitialize(int new_font_size) {
    // Verificar que tenemos todo lo necesario
    if (renderer_ == nullptr || font_path_.empty()) {
        SDL_Log("Error: TextRenderer no inicializado correctamente para reinitialize()");
        return false;
    }

    // Si el tamaño es el mismo, no hacer nada
    if (new_font_size == font_size_) {
        return true;
    }

    // Cerrar fuente actual y liberar buffer previo
    if (font_ != nullptr) {
        TTF_CloseFont(font_);
        font_ = nullptr;
    }
    if (font_data_buffer_ != nullptr) {
        delete[] font_data_buffer_;
        font_data_buffer_ = nullptr;
    }

    // Intentar cargar la fuente desde ResourceManager con el nuevo tamaño
    unsigned char* fontData = nullptr;
    size_t fontDataSize = 0;

    if (ResourceManager::loadResource(font_path_, fontData, fontDataSize)) {
        SDL_IOStream* fontIO = SDL_IOFromConstMem(fontData, static_cast<size_t>(fontDataSize));
        if (fontIO != nullptr) {
            font_ = TTF_OpenFontIO(fontIO, true, new_font_size);

            if (font_ == nullptr) {
                SDL_Log("Error al recargar fuente '%s' con tamaño %d: %s",
                        font_path_.c_str(), new_font_size, SDL_GetError());
                delete[] fontData;  // Liberar solo si falla
                return false;
            }

            // Mantener buffer en memoria (NO eliminar)
            font_data_buffer_ = fontData;
            font_size_ = new_font_size;
            SDL_Log("Fuente recargada desde ResourceManager: %s (tamaño %d)", font_path_.c_str(), new_font_size);
            return true;
        } else {
            delete[] fontData;
        }
    }

    // Fallback: cargar directamente desde disco
    font_ = TTF_OpenFont(font_path_.c_str(), new_font_size);
    if (font_ == nullptr) {
        SDL_Log("Error al recargar fuente '%s' con tamaño %d: %s",
                font_path_.c_str(), new_font_size, SDL_GetError());
        return false;
    }

    font_size_ = new_font_size;
    return true;
}

void TextRenderer::cleanup() {
    if (font_ != nullptr) {
        TTF_CloseFont(font_);
        font_ = nullptr;
    }
    if (font_data_buffer_ != nullptr) {
        delete[] font_data_buffer_;
        font_data_buffer_ = nullptr;
    }
    renderer_ = nullptr;
}

void TextRenderer::print(int x, int y, const char* text, uint8_t r, uint8_t g, uint8_t b) {
    if (!isInitialized() || text == nullptr || text[0] == '\0') {
        return;
    }

    // Crear superficie con el texto renderizado
    SDL_Color color = {r, g, b, 255};
    SDL_Surface* text_surface = nullptr;

    if (use_antialiasing_) {
        // Con antialiasing (suave, mejor calidad)
        text_surface = TTF_RenderText_Blended(font_, text, strlen(text), color);
    } else {
        // Sin antialiasing (píxeles nítidos, estilo retro)
        text_surface = TTF_RenderText_Solid(font_, text, strlen(text), color);
    }

    if (text_surface == nullptr) {
        SDL_Log("Error al renderizar texto: %s", SDL_GetError());
        return;
    }

    // Crear textura desde la superficie
    SDL_Texture* text_texture = SDL_CreateTextureFromSurface(renderer_, text_surface);

    if (text_texture == nullptr) {
        SDL_Log("Error al crear textura: %s", SDL_GetError());
        SDL_DestroySurface(text_surface);
        return;
    }

    // Preparar rectángulo de destino
    SDL_FRect dest_rect;
    dest_rect.x = static_cast<float>(x);
    dest_rect.y = static_cast<float>(y);
    dest_rect.w = static_cast<float>(text_surface->w);
    dest_rect.h = static_cast<float>(text_surface->h);

    // Renderizar la textura
    SDL_RenderTexture(renderer_, text_texture, nullptr, &dest_rect);

    // Limpiar recursos
    SDL_DestroyTexture(text_texture);
    SDL_DestroySurface(text_surface);
}

void TextRenderer::print(int x, int y, const std::string& text, uint8_t r, uint8_t g, uint8_t b) {
    print(x, y, text.c_str(), r, g, b);
}

void TextRenderer::printPhysical(int logical_x, int logical_y, const char* text, uint8_t r, uint8_t g, uint8_t b, float scale_x, float scale_y) {
    if (!isInitialized() || text == nullptr || text[0] == '\0') {
        return;
    }

    // Convertir coordenadas lógicas a físicas
    int physical_x = static_cast<int>(logical_x * scale_x);
    int physical_y = static_cast<int>(logical_y * scale_y);

    // Crear superficie con el texto renderizado
    SDL_Color color = {r, g, b, 255};
    SDL_Surface* text_surface = nullptr;

    if (use_antialiasing_) {
        text_surface = TTF_RenderText_Blended(font_, text, strlen(text), color);
    } else {
        text_surface = TTF_RenderText_Solid(font_, text, strlen(text), color);
    }

    if (text_surface == nullptr) {
        SDL_Log("Error al renderizar texto: %s", SDL_GetError());
        return;
    }

    // Crear textura desde la superficie
    SDL_Texture* text_texture = SDL_CreateTextureFromSurface(renderer_, text_surface);

    if (text_texture == nullptr) {
        SDL_Log("Error al crear textura: %s", SDL_GetError());
        SDL_DestroySurface(text_surface);
        return;
    }

    // Renderizar en coordenadas físicas (bypass presentación lógica)
    // Usar SDL_RenderTexture con coordenadas absolutas de ventana
    SDL_FRect dest_rect;
    dest_rect.x = static_cast<float>(physical_x);
    dest_rect.y = static_cast<float>(physical_y);
    dest_rect.w = static_cast<float>(text_surface->w);
    dest_rect.h = static_cast<float>(text_surface->h);

    // Deshabilitar temporalmente presentación lógica para renderizar en píxeles físicos
    int logical_w = 0, logical_h = 0;
    SDL_RendererLogicalPresentation presentation_mode;
    SDL_GetRenderLogicalPresentation(renderer_, &logical_w, &logical_h, &presentation_mode);

    // Renderizar sin presentación lógica (coordenadas absolutas)
    SDL_SetRenderLogicalPresentation(renderer_, 0, 0, SDL_LOGICAL_PRESENTATION_DISABLED);
    SDL_RenderTexture(renderer_, text_texture, nullptr, &dest_rect);

    // Restaurar presentación lógica
    SDL_SetRenderLogicalPresentation(renderer_, logical_w, logical_h, presentation_mode);

    // Limpiar recursos
    SDL_DestroyTexture(text_texture);
    SDL_DestroySurface(text_surface);
}

void TextRenderer::printPhysical(int logical_x, int logical_y, const std::string& text, uint8_t r, uint8_t g, uint8_t b, float scale_x, float scale_y) {
    printPhysical(logical_x, logical_y, text.c_str(), r, g, b, scale_x, scale_y);
}

void TextRenderer::printAbsolute(int physical_x, int physical_y, const char* text, SDL_Color color) {
    if (!isInitialized() || text == nullptr || text[0] == '\0') {
        return;
    }

    // Crear superficie con el texto renderizado
    SDL_Surface* text_surface = nullptr;

    if (use_antialiasing_) {
        text_surface = TTF_RenderText_Blended(font_, text, strlen(text), color);
    } else {
        text_surface = TTF_RenderText_Solid(font_, text, strlen(text), color);
    }

    if (text_surface == nullptr) {
        SDL_Log("Error al renderizar texto: %s", SDL_GetError());
        return;
    }

    // Crear textura desde la superficie
    SDL_Texture* text_texture = SDL_CreateTextureFromSurface(renderer_, text_surface);

    if (text_texture == nullptr) {
        SDL_Log("Error al crear textura: %s", SDL_GetError());
        SDL_DestroySurface(text_surface);
        return;
    }

    // Configurar alpha blending si el color tiene transparencia
    if (color.a < 255) {
        SDL_SetTextureBlendMode(text_texture, SDL_BLENDMODE_BLEND);
        SDL_SetTextureAlphaModFloat(text_texture, color.a / 255.0f);
    }

    // Obtener viewport ANTES de deshabilitar presentación lógica
    // En modo letterbox (F3), SDL crea un viewport con offset para centrar la imagen
    SDL_Rect viewport;
    SDL_GetRenderViewport(renderer_, &viewport);

    // Preparar rectángulo de destino en coordenadas físicas absolutas
    // Aplicar offset del viewport para que el texto se pinte dentro del área visible
    SDL_FRect dest_rect;
    dest_rect.x = static_cast<float>(physical_x + viewport.x);
    dest_rect.y = static_cast<float>(physical_y + viewport.y);
    dest_rect.w = static_cast<float>(text_surface->w);
    dest_rect.h = static_cast<float>(text_surface->h);

    // Deshabilitar temporalmente presentación lógica para renderizar en píxeles físicos
    int logical_w = 0, logical_h = 0;
    SDL_RendererLogicalPresentation presentation_mode;
    SDL_GetRenderLogicalPresentation(renderer_, &logical_w, &logical_h, &presentation_mode);

    // Renderizar sin presentación lógica (coordenadas absolutas con offset de viewport)
    SDL_SetRenderLogicalPresentation(renderer_, 0, 0, SDL_LOGICAL_PRESENTATION_DISABLED);
    SDL_RenderTexture(renderer_, text_texture, nullptr, &dest_rect);

    // Restaurar presentación lógica
    SDL_SetRenderLogicalPresentation(renderer_, logical_w, logical_h, presentation_mode);

    // Limpiar recursos
    SDL_DestroyTexture(text_texture);
    SDL_DestroySurface(text_surface);
}

void TextRenderer::printAbsolute(int physical_x, int physical_y, const std::string& text, SDL_Color color) {
    printAbsolute(physical_x, physical_y, text.c_str(), color);
}

void TextRenderer::printAbsoluteShadowed(int physical_x, int physical_y, const char* text) {
    // Sombra: negro semitransparente desplazado 1px
    printAbsolute(physical_x + 1, physical_y + 1, text, {0, 0, 0, 180});
    // Texto: blanco opaco
    printAbsolute(physical_x, physical_y, text, {255, 255, 255, 255});
}

void TextRenderer::printAbsoluteShadowed(int physical_x, int physical_y, const std::string& text) {
    printAbsoluteShadowed(physical_x, physical_y, text.c_str());
}

int TextRenderer::getTextWidth(const char* text) {
    if (!isInitialized() || text == nullptr) {
        return 0;
    }

    int width = 0;
    int height = 0;
    if (!TTF_GetStringSize(font_, text, strlen(text), &width, &height)) {
        return 0;
    }
    return width;
}

int TextRenderer::getTextWidthPhysical(const char* text) {
    // Retorna el ancho REAL en píxeles físicos (sin escalado lógico)
    // Idéntico a getTextWidth() pero semánticamente diferente:
    // - Este método se usa cuando se necesita el ancho REAL de la fuente
    // - Útil para calcular dimensiones de UI en coordenadas físicas absolutas
    if (!isInitialized() || text == nullptr) {
        return 0;
    }

    int width = 0;
    int height = 0;
    if (!TTF_GetStringSize(font_, text, strlen(text), &width, &height)) {
        return 0;
    }
    return width;  // Ancho real de la textura generada por TTF
}

int TextRenderer::getTextHeight() {
    if (!isInitialized()) {
        return 0;
    }

    return TTF_GetFontHeight(font_);
}

int TextRenderer::getGlyphHeight() {
    if (!isInitialized()) {
        return 0;
    }

    return TTF_GetFontAscent(font_) - TTF_GetFontDescent(font_);
}
