#include "help_overlay.hpp"

#include <algorithm>  // for std::min

#include "text/textrenderer.hpp"
#include "theme_manager.hpp"

HelpOverlay::HelpOverlay()
    : renderer_(nullptr),
      theme_mgr_(nullptr),
      text_renderer_(nullptr),
      physical_width_(0),
      physical_height_(0),
      visible_(false),
      box_width_(0),
      box_height_(0),
      box_x_(0),
      box_y_(0),
      column1_width_(0),
      column2_width_(0),
      cached_texture_(nullptr),
      last_category_color_({0, 0, 0, 255}),
      last_content_color_({0, 0, 0, 255}),
      last_bg_color_({0, 0, 0, 255}),
      texture_needs_rebuild_(true) {
    // Llenar lista de controles (organizados por categoría, equilibrado en 2 columnas)
    key_bindings_ = {
        // COLUMNA 1: SIMULACIÓN
        {"SIMULACIÓN", ""},
        {"1-8", "Escenarios (10 a 50,000 pelotas)"},
        {"F", "Toggle Física - Última Figura"},
        {"B", "Modo Boids (enjambre)"},
        {"ESPACIO", "Impulso contra gravedad"},
        {"G", "Toggle Gravedad ON/OFF"},
        {"CURSORES", "Dirección de gravedad"},
        {"", ""},  // Separador

        // COLUMNA 1: FIGURAS 3D
        {"FIGURAS 3D", ""},
        {"Q/W/E/R", "Esfera/Lissajous/Hélice/Toroide"},
        {"T/Y/U/I", "Cubo/Cilindro/Icosaedro/Átomo"},
        {"O", "Forma PNG"},
        {"Num+/-", "Escalar figura"},
        {"Num*", "Reset escala"},
        {"Num/", "Toggle profundidad"},
        {"", ""},  // Separador

        // COLUMNA 1: VISUAL
        {"VISUAL", ""},
        {"C", "Tema siguiente"},
        {"Shift+C", "Tema anterior"},
        {"NumEnter", "Página de temas"},
        {"N", "Cambiar sprite"},
        {"[new_col]", ""},  // Separador -> CAMBIO DE COLUMNA

        // COLUMNA 2: PANTALLA
        {"PANTALLA", ""},
        {"F1/F2", "Zoom out/in (ventana)"},
        {"F3", "Fullscreen letterbox"},
        {"F4", "Fullscreen real"},
        {"F5", "Escalado (F3 activo)"},
        {"V", "Toggle V-Sync"},
        {"", ""},  // Separador

        // COLUMNA 2: MODOS
        {"MODOS", ""},
        {"D", "Modo DEMO"},
        {"Shift+D", "Pausar tema dinámico"},
        {"L", "Modo DEMO LITE"},
        {"K", "Modo LOGO (easter egg)"},
        {"", ""},  // Separador

        // COLUMNA 2: DEBUG/AYUDA
        {"DEBUG/AYUDA", ""},
        {"F12", "Toggle info debug"},
        {"H", "Esta ayuda"},
        {"ESC", "Salir"}};
}

HelpOverlay::~HelpOverlay() {
    // Destruir textura cacheada si existe
    if (cached_texture_) {
        SDL_DestroyTexture(cached_texture_);
        cached_texture_ = nullptr;
    }
    delete text_renderer_;
}

void HelpOverlay::toggle() {
    visible_ = !visible_;
}

void HelpOverlay::initialize(SDL_Renderer* renderer, ThemeManager* theme_mgr, int physical_width, int physical_height, int font_size) {
    renderer_ = renderer;
    theme_mgr_ = theme_mgr;
    physical_width_ = physical_width;
    physical_height_ = physical_height;

    // Crear renderer de texto con tamaño dinámico
    text_renderer_ = new TextRenderer();
    text_renderer_->init(renderer, "data/fonts/FunnelSans-Regular.ttf", font_size, true);

    calculateBoxDimensions();
}

void HelpOverlay::updatePhysicalWindowSize(int physical_width, int physical_height) {
    physical_width_ = physical_width;
    physical_height_ = physical_height;
    calculateBoxDimensions();

    // Marcar textura para regeneración (dimensiones han cambiado)
    texture_needs_rebuild_ = true;
}

void HelpOverlay::reinitializeFontSize(int new_font_size) {
    if (!text_renderer_) return;

    // Reinicializar text renderer con nuevo tamaño
    text_renderer_->reinitialize(new_font_size);

    // NOTA: NO recalcular dimensiones aquí porque physical_width_ y physical_height_
    // pueden tener valores antiguos. updatePhysicalWindowSize() se llamará después
    // con las dimensiones correctas y recalculará todo apropiadamente.

    // Marcar textura para regeneración completa
    texture_needs_rebuild_ = true;
}

void HelpOverlay::updateAll(int font_size, int physical_width, int physical_height) {
    // Actualizar dimensiones físicas PRIMERO
    physical_width_ = physical_width;
    physical_height_ = physical_height;

    // Reinicializar text renderer con nuevo tamaño (si cambió)
    if (text_renderer_) {
        text_renderer_->reinitialize(font_size);
    }

    // Recalcular dimensiones del box con nuevo font y nuevas dimensiones
    calculateBoxDimensions();

    // Marcar textura para regeneración completa
    texture_needs_rebuild_ = true;
}

void HelpOverlay::calculateTextDimensions(int& max_width, int& total_height) {
    if (!text_renderer_) {
        max_width = 0;
        total_height = 0;
        return;
    }

    int line_height = text_renderer_->getTextHeight();
    // Padding dinámico basado en altura física: 25px para >= 600px, escalado proporcionalmente para menores
    int padding = (physical_height_ >= 600) ? 25 : std::max(10, physical_height_ / 24);

    // Calcular ancho máximo por columna
    int max_col1_width = 0;
    int max_col2_width = 0;
    int current_column = 0;

    for (const auto& binding : key_bindings_) {
        // Cambio de columna
        if (strcmp(binding.key, "[new_col]") == 0) {
            current_column = 1;
            continue;
        }

        // Separador vacío (no tiene key ni description)
        if (binding.key[0] == '\0') {
            continue;
        }

        int line_width = 0;

        if (binding.description[0] == '\0') {
            // Es un encabezado (solo tiene key, sin description)
            line_width = text_renderer_->getTextWidthPhysical(binding.key);
        } else {
            // Es una línea normal con key + description
            int key_width = text_renderer_->getTextWidthPhysical(binding.key);
            int desc_width = text_renderer_->getTextWidthPhysical(binding.description);
            line_width = key_width + 10 + desc_width;  // 10px de separación
        }

        // Actualizar máximo de columna correspondiente
        if (current_column == 0) {
            max_col1_width = std::max(max_col1_width, line_width);
        } else {
            max_col2_width = std::max(max_col2_width, line_width);
        }
    }

    // Almacenar anchos de columnas en miembros para uso posterior
    column1_width_ = max_col1_width;
    column2_width_ = max_col2_width;

    // Ancho total: 2 columnas + 3 paddings (izq, medio, der)
    max_width = max_col1_width + max_col2_width + padding * 3;

    // Altura: contar líneas REALES en cada columna
    int col1_lines = 0;
    int col2_lines = 0;
    current_column = 0;

    for (const auto& binding : key_bindings_) {
        // Cambio de columna
        if (strcmp(binding.key, "[new_col]") == 0) {
            current_column = 1;
            continue;
        }

        // Separador vacío no cuenta como línea
        if (binding.key[0] == '\0') {
            continue;
        }

        // Contar línea (ya sea encabezado o contenido)
        if (current_column == 0) {
            col1_lines++;
        } else {
            col2_lines++;
        }
    }

    // Usar la columna más larga para calcular altura
    int max_column_lines = std::max(col1_lines, col2_lines);

    // Altura: título (2 líneas) + contenido + padding superior e inferior
    total_height = line_height * 2 + max_column_lines * line_height + padding * 2;
}

void HelpOverlay::calculateBoxDimensions() {
    // Calcular dimensiones necesarias según el texto
    int text_width, text_height;
    calculateTextDimensions(text_width, text_height);

    // Aplicar límites máximos: 95% ancho, 90% altura
    int max_width = static_cast<int>(physical_width_ * 0.95f);
    int max_height = static_cast<int>(physical_height_ * 0.90f);

    box_width_ = std::min(text_width, max_width);
    box_height_ = std::min(text_height, max_height);

    // Centrar en pantalla
    box_x_ = (physical_width_ - box_width_) / 2;
    box_y_ = (physical_height_ - box_height_) / 2;
}

void HelpOverlay::rebuildCachedTexture() {
    if (!renderer_ || !theme_mgr_ || !text_renderer_) return;

    // Destruir textura anterior si existe
    if (cached_texture_) {
        SDL_DestroyTexture(cached_texture_);
        cached_texture_ = nullptr;
    }

    // Crear nueva textura del tamaño del overlay
    cached_texture_ = SDL_CreateTexture(renderer_,
                                        SDL_PIXELFORMAT_RGBA8888,
                                        SDL_TEXTUREACCESS_TARGET,
                                        box_width_,
                                        box_height_);

    if (!cached_texture_) {
        SDL_Log("Error al crear textura cacheada: %s", SDL_GetError());
        return;
    }

    // Habilitar alpha blending en la textura
    SDL_SetTextureBlendMode(cached_texture_, SDL_BLENDMODE_BLEND);

    // Guardar render target actual
    SDL_Texture* prev_target = SDL_GetRenderTarget(renderer_);

    // Cambiar render target a la textura cacheada
    SDL_SetRenderTarget(renderer_, cached_texture_);

    // Limpiar textura (completamente transparente)
    SDL_SetRenderDrawColor(renderer_, 0, 0, 0, 0);
    SDL_RenderClear(renderer_);

    // Habilitar alpha blending
    SDL_SetRenderDrawBlendMode(renderer_, SDL_BLENDMODE_BLEND);

    // Obtener colores actuales del tema
    int notif_bg_r, notif_bg_g, notif_bg_b;
    theme_mgr_->getCurrentNotificationBackgroundColor(notif_bg_r, notif_bg_g, notif_bg_b);

    // Renderizar fondo del overlay a la textura
    float alpha = 0.85f;
    SDL_Vertex bg_vertices[4];

    float r = notif_bg_r / 255.0f;
    float g = notif_bg_g / 255.0f;
    float b = notif_bg_b / 255.0f;

    // Vértices del fondo (posición relativa 0,0 porque estamos renderizando a textura)
    bg_vertices[0].position = {0, 0};
    bg_vertices[0].tex_coord = {0.0f, 0.0f};
    bg_vertices[0].color = {r, g, b, alpha};

    bg_vertices[1].position = {static_cast<float>(box_width_), 0};
    bg_vertices[1].tex_coord = {1.0f, 0.0f};
    bg_vertices[1].color = {r, g, b, alpha};

    bg_vertices[2].position = {static_cast<float>(box_width_), static_cast<float>(box_height_)};
    bg_vertices[2].tex_coord = {1.0f, 1.0f};
    bg_vertices[2].color = {r, g, b, alpha};

    bg_vertices[3].position = {0, static_cast<float>(box_height_)};
    bg_vertices[3].tex_coord = {0.0f, 1.0f};
    bg_vertices[3].color = {r, g, b, alpha};

    int bg_indices[6] = {0, 1, 2, 2, 3, 0};
    SDL_RenderGeometry(renderer_, nullptr, bg_vertices, 4, bg_indices, 6);

    // Renderizar texto del overlay (ajustando coordenadas para que sean relativas a 0,0)
    // Necesito renderizar el texto igual que en renderHelpText() pero con coordenadas ajustadas

    // Obtener colores para el texto
    int text_r, text_g, text_b;
    theme_mgr_->getCurrentThemeTextColor(text_r, text_g, text_b);
    SDL_Color category_color = {static_cast<Uint8>(text_r), static_cast<Uint8>(text_g), static_cast<Uint8>(text_b), 255};

    Color ball_color = theme_mgr_->getInterpolatedColor(0);
    SDL_Color content_color = {static_cast<Uint8>(ball_color.r), static_cast<Uint8>(ball_color.g), static_cast<Uint8>(ball_color.b), 255};

    // Guardar colores actuales para comparación futura
    last_category_color_ = category_color;
    last_content_color_ = content_color;
    last_bg_color_ = {static_cast<Uint8>(notif_bg_r), static_cast<Uint8>(notif_bg_g), static_cast<Uint8>(notif_bg_b), 255};

    // Configuración de espaciado
    int line_height = text_renderer_->getTextHeight();
    // Padding dinámico basado en altura física: 25px para >= 600px, escalado proporcionalmente para menores
    int padding = (physical_height_ >= 600) ? 25 : std::max(10, physical_height_ / 24);

    int current_x = padding;  // Coordenadas relativas a la textura (0,0)
    int current_y = padding;
    int current_column = 0;

    // Título principal
    const char* title = "CONTROLES - ViBe3 Physics";
    int title_width = text_renderer_->getTextWidthPhysical(title);
    text_renderer_->printAbsolute(box_width_ / 2 - title_width / 2, current_y, title, category_color);
    current_y += line_height * 2;

    int content_start_y = current_y;

    // Renderizar cada línea
    for (const auto& binding : key_bindings_) {
        if (strcmp(binding.key, "[new_col]") == 0 && binding.description[0] == '\0') {
            if (current_column == 0) {
                current_column = 1;
                current_x = padding + column1_width_ + padding;  // Usar ancho real de columna 1
                current_y = content_start_y;
            }
            continue;
        }

        // CHECK PADDING INFERIOR ANTES de escribir la línea (AMBAS COLUMNAS)
        // Verificar si la PRÓXIMA línea cabrá dentro del box con padding inferior
        if (current_y + line_height >= box_height_ - padding) {
            if (current_column == 0) {
                // Columna 0 llena: cambiar a columna 1
                current_column = 1;
                current_x = padding + column1_width_ + padding;
                current_y = content_start_y;
            } else {
                // Columna 1 llena: omitir resto de texto (no cabe)
                // Preferible omitir que sobresalir del overlay
                continue;
            }
        }

        if (binding.description[0] == '\0') {
            text_renderer_->printAbsolute(current_x, current_y, binding.key, category_color);
            current_y += line_height + 2;
            continue;
        }

        text_renderer_->printAbsolute(current_x, current_y, binding.key, content_color);
        int key_width = text_renderer_->getTextWidthPhysical(binding.key);
        text_renderer_->printAbsolute(current_x + key_width + 10, current_y, binding.description, content_color);

        current_y += line_height;
    }

    // Restaurar render target original
    SDL_SetRenderTarget(renderer_, prev_target);

    // Marcar que ya no necesita rebuild
    texture_needs_rebuild_ = false;
}

void HelpOverlay::render(SDL_Renderer* renderer) {
    if (!visible_) return;

    // Obtener colores actuales del tema
    int notif_bg_r, notif_bg_g, notif_bg_b;
    theme_mgr_->getCurrentNotificationBackgroundColor(notif_bg_r, notif_bg_g, notif_bg_b);

    int text_r, text_g, text_b;
    theme_mgr_->getCurrentThemeTextColor(text_r, text_g, text_b);

    Color ball_color = theme_mgr_->getInterpolatedColor(0);

    // Crear colores actuales para comparación
    SDL_Color current_bg = {static_cast<Uint8>(notif_bg_r), static_cast<Uint8>(notif_bg_g), static_cast<Uint8>(notif_bg_b), 255};
    SDL_Color current_category = {static_cast<Uint8>(text_r), static_cast<Uint8>(text_g), static_cast<Uint8>(text_b), 255};
    SDL_Color current_content = {static_cast<Uint8>(ball_color.r), static_cast<Uint8>(ball_color.g), static_cast<Uint8>(ball_color.b), 255};

    // Detectar si los colores han cambiado significativamente (umbral: 5/255)
    constexpr int COLOR_CHANGE_THRESHOLD = 5;
    bool colors_changed =
        (abs(current_bg.r - last_bg_color_.r) > COLOR_CHANGE_THRESHOLD ||
         abs(current_bg.g - last_bg_color_.g) > COLOR_CHANGE_THRESHOLD ||
         abs(current_bg.b - last_bg_color_.b) > COLOR_CHANGE_THRESHOLD ||
         abs(current_category.r - last_category_color_.r) > COLOR_CHANGE_THRESHOLD ||
         abs(current_category.g - last_category_color_.g) > COLOR_CHANGE_THRESHOLD ||
         abs(current_category.b - last_category_color_.b) > COLOR_CHANGE_THRESHOLD ||
         abs(current_content.r - last_content_color_.r) > COLOR_CHANGE_THRESHOLD ||
         abs(current_content.g - last_content_color_.g) > COLOR_CHANGE_THRESHOLD ||
         abs(current_content.b - last_content_color_.b) > COLOR_CHANGE_THRESHOLD);

    // Regenerar textura si es necesario (colores cambiaron O flag de rebuild activo)
    if (texture_needs_rebuild_ || colors_changed || !cached_texture_) {
        rebuildCachedTexture();
    }

    // Si no hay textura cacheada (error), salir
    if (!cached_texture_) return;

    // CRÍTICO: Habilitar alpha blending para que la transparencia funcione
    SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_BLEND);

    // Obtener viewport actual (en modo letterbox F3 tiene offset para centrar imagen)
    SDL_Rect viewport;
    SDL_GetRenderViewport(renderer, &viewport);

    // Calcular posición centrada dentro del VIEWPORT, no de la pantalla física
    // viewport.w y viewport.h son las dimensiones del área visible
    // viewport.x y viewport.y son el offset de las barras negras
    int centered_x = viewport.x + (viewport.w - box_width_) / 2;
    int centered_y = viewport.y + (viewport.h - box_height_) / 2;

    // Renderizar la textura cacheada centrada en el viewport
    SDL_FRect dest_rect;
    dest_rect.x = static_cast<float>(centered_x);
    dest_rect.y = static_cast<float>(centered_y);
    dest_rect.w = static_cast<float>(box_width_);
    dest_rect.h = static_cast<float>(box_height_);

    SDL_RenderTexture(renderer, cached_texture_, nullptr, &dest_rect);
}
