// sdl_manager.cpp - Implementació del gestor SDL3
// © 2025 Port a C++20 amb SDL3

#include "sdl_manager.hpp"

#include <algorithm>
#include <format>
#include <iostream>

#include "core/defaults.hpp"
#include "core/input/mouse.hpp"
#include "core/rendering/coordinate_transform.hpp"
#include "core/rendering/line_renderer.hpp"
#include "game/options.hpp"
#include "project.h"

SDLManager::SDLManager()
    : finestra_(nullptr),
      renderer_(nullptr),
      fps_accumulator_(0.0F),
      fps_frame_count_(0),
      fps_display_(0),
      current_width_(Defaults::Window::WIDTH),
      current_height_(Defaults::Window::HEIGHT),
      is_fullscreen_(false),
      max_width_(1920),
      max_height_(1080),
      zoom_factor_(Defaults::Window::BASE_ZOOM),
      windowed_width_(Defaults::Window::WIDTH),
      windowed_height_(Defaults::Window::HEIGHT),
      max_zoom_(1.0F) {
    // Inicialitzar SDL3
    if (!SDL_Init(SDL_INIT_VIDEO)) {
        std::cerr << "Error inicialitzant SDL3: " << SDL_GetError() << std::endl;
        return;
    }

    // Calcular mida màxima des del display
    calculateMaxWindowSize();

    // Construir títol dinàmic
    std::string window_title = std::format("{} v{} ({})", Project::LONG_NAME, Project::VERSION, Project::COPYRIGHT);

    // Crear finestra CENTRADA (SDL ho fa automàticament amb CENTERED)
    finestra_ =
        SDL_CreateWindow(window_title.c_str(), current_width_, current_height_,
            SDL_WINDOW_RESIZABLE  // Permetre resize manual també
        );

    if (finestra_ == nullptr) {
        std::cerr << "Error creant finestra: " << SDL_GetError() << std::endl;
        SDL_Quit();
        return;
    }

    // IMPORTANT: Centrar explícitament la finestra
    SDL_SetWindowPosition(finestra_, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED);

    // Crear renderer amb acceleració
    renderer_ = SDL_CreateRenderer(finestra_, nullptr);

    if (renderer_ == nullptr) {
        std::cerr << "Error creant renderer: " << SDL_GetError() << std::endl;
        SDL_DestroyWindow(finestra_);
        SDL_Quit();
        return;
    }

    // Aplicar configuració de V-Sync
    SDL_SetRenderVSync(renderer_, Options::rendering.vsync);

    // CRÍTIC: Configurar viewport scaling
    updateLogicalPresentation();

    std::cout << "SDL3 inicialitzat: " << current_width_ << "x" << current_height_
              << " (logic: " << Defaults::Game::WIDTH << "x"
              << Defaults::Game::HEIGHT << ")" << std::endl;
}

// Constructor amb configuració
SDLManager::SDLManager(int width, int height, bool fullscreen)
    : finestra_(nullptr),
      renderer_(nullptr),
      fps_accumulator_(0.0F),
      fps_frame_count_(0),
      fps_display_(0),
      current_width_(width),
      current_height_(height),
      is_fullscreen_(fullscreen),
      max_width_(1920),
      max_height_(1080),
      zoom_factor_(static_cast<float>(width) / Defaults::Window::WIDTH),
      windowed_width_(width),
      windowed_height_(height),
      max_zoom_(1.0F) {
    // Inicialitzar SDL3
    if (!SDL_Init(SDL_INIT_VIDEO)) {
        std::cerr << "Error inicialitzant SDL3: " << SDL_GetError() << std::endl;
        return;
    }

    // Calcular mida màxima des del display
    calculateMaxWindowSize();

    // Construir títol dinàmic
    std::string window_title = std::format("{} v{} ({})", Project::LONG_NAME, Project::VERSION, Project::COPYRIGHT);

    // Configurar flags de la finestra
    SDL_WindowFlags flags = SDL_WINDOW_RESIZABLE;
    if (is_fullscreen_) {
        flags = static_cast<SDL_WindowFlags>(flags | SDL_WINDOW_FULLSCREEN);
    }

    // Crear finestra
    finestra_ = SDL_CreateWindow(window_title.c_str(), current_width_, current_height_, flags);

    if (finestra_ == nullptr) {
        std::cerr << "Error creant finestra: " << SDL_GetError() << std::endl;
        SDL_Quit();
        return;
    }

    // Centrar explícitament la finestra (si no és fullscreen)
    if (!is_fullscreen_) {
        SDL_SetWindowPosition(finestra_, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED);
    }

    // Crear renderer amb acceleració
    renderer_ = SDL_CreateRenderer(finestra_, nullptr);

    if (renderer_ == nullptr) {
        std::cerr << "Error creant renderer: " << SDL_GetError() << std::endl;
        SDL_DestroyWindow(finestra_);
        SDL_Quit();
        return;
    }

    // Aplicar configuració de V-Sync
    SDL_SetRenderVSync(renderer_, Options::rendering.vsync);

    // Configurar viewport scaling
    updateLogicalPresentation();

    // Inicialitzar sistema de cursor
    // En fullscreen: forzar ocultació permanent
    if (is_fullscreen_) {
        Mouse::setForceHidden(true);
    }

    std::cout << "SDL3 inicialitzat: " << current_width_ << "x" << current_height_
              << " (logic: " << Defaults::Game::WIDTH << "x"
              << Defaults::Game::HEIGHT << ")";
    if (is_fullscreen_) {
        std::cout << " [FULLSCREEN]";
    }
    std::cout << std::endl;
}

SDLManager::~SDLManager() {
    if (renderer_ != nullptr) {
        SDL_DestroyRenderer(renderer_);
        renderer_ = nullptr;
    }

    if (finestra_ != nullptr) {
        SDL_DestroyWindow(finestra_);
        finestra_ = nullptr;
    }

    SDL_Quit();
    std::cout << "SDL3 netejat correctament" << std::endl;
}

void SDLManager::calculateMaxWindowSize() {
    SDL_DisplayID display = SDL_GetPrimaryDisplay();
    const SDL_DisplayMode* mode = SDL_GetCurrentDisplayMode(display);

    if (mode != nullptr) {
        // Deixar marge de 100px per a decoracions de l'OS
        max_width_ = mode->w - 100;
        max_height_ = mode->h - 100;
        std::cout << "Display detectat: " << mode->w << "x" << mode->h
                  << " (max finestra: " << max_width_ << "x" << max_height_ << ")"
                  << std::endl;
    } else {
        // Fallback conservador
        max_width_ = 1920;
        max_height_ = 1080;
        std::cerr << "No s'ha pogut detectar el display, usant fallback: "
                  << max_width_ << "x" << max_height_ << std::endl;
    }

    // Calculate max zoom immediately after determining max size
    calculateMaxZoom();
}

void SDLManager::calculateMaxZoom() {
    // Maximum zoom limited by BOTH width and height (preserves 4:3)
    float max_zoom_width = static_cast<float>(max_width_) / Defaults::Window::WIDTH;
    float max_zoom_height = static_cast<float>(max_height_) / Defaults::Window::HEIGHT;

    // Take smaller constraint
    float max_zoom_unrounded = std::min(max_zoom_width, max_zoom_height);

    // Round DOWN to nearest 0.1 increment (user preference)
    max_zoom_ = std::floor(max_zoom_unrounded / Defaults::Window::ZOOM_INCREMENT) * Defaults::Window::ZOOM_INCREMENT;

    // Safety clamp
    max_zoom_ = std::max(max_zoom_, Defaults::Window::MIN_ZOOM);

    std::cout << "Max zoom: " << max_zoom_ << "x (display: "
              << max_width_ << "x" << max_height_ << ")" << std::endl;
}

void SDLManager::applyZoom(float new_zoom) {
    // Clamp to valid range
    new_zoom = std::max(Defaults::Window::MIN_ZOOM,
        std::min(new_zoom, max_zoom_));

    // Round to nearest 0.1 increment
    new_zoom = std::round(new_zoom / Defaults::Window::ZOOM_INCREMENT) * Defaults::Window::ZOOM_INCREMENT;

    // No change?
    if (std::abs(new_zoom - zoom_factor_) < 0.01F) {
        return;
    }

    zoom_factor_ = new_zoom;

    // Calculate physical dimensions (4:3 maintained automatically)
    int new_width = static_cast<int>(std::round(
        Defaults::Window::WIDTH * zoom_factor_));
    int new_height = static_cast<int>(std::round(
        Defaults::Window::HEIGHT * zoom_factor_));

    // Apply to window (centers via applyWindowSize)
    applyWindowSize(new_width, new_height);

    // Update viewport for new zoom
    updateViewport();

    // Update windowed size cache
    windowed_width_ = new_width;
    windowed_height_ = new_height;

    // Persist
    Options::window.width = new_width;
    Options::window.height = new_height;
    Options::window.zoom_factor = zoom_factor_;

    std::cout << "Zoom: " << zoom_factor_ << "x ("
              << new_width << "x" << new_height << ")" << std::endl;
}

void SDLManager::updateLogicalPresentation() {
    // CANVIAT: Ja no usem SDL_SetRenderLogicalPresentation
    // Ara renderitzem directament a resolució física per evitar pixelació irregular
    // El viewport amb letterbox es configura a updateViewport()
    updateViewport();
}

void SDLManager::updateViewport() {
    // Calcular dimensions físiques basades en el zoom
    float scale = zoom_factor_;
    int scaled_width = static_cast<int>(std::round(Defaults::Game::WIDTH * scale));
    int scaled_height = static_cast<int>(std::round(Defaults::Game::HEIGHT * scale));

    // Càlcul de letterbox (centrar l'àrea escalada)
    int offset_x = (current_width_ - scaled_width) / 2;
    int offset_y = (current_height_ - scaled_height) / 2;

    // Evitar offsets negatius
    offset_x = std::max(offset_x, 0);
    offset_y = std::max(offset_y, 0);

    // Configurar viewport per al renderitzat
    SDL_Rect viewport = {offset_x, offset_y, scaled_width, scaled_height};
    SDL_SetRenderViewport(renderer_, &viewport);

    std::cout << "Viewport: " << scaled_width << "x" << scaled_height
              << " @ (" << offset_x << "," << offset_y << ") [scale=" << scale << "]"
              << std::endl;
}

void SDLManager::updateRenderingContext() const {
    // Actualitzar el factor d'escala global per a totes les funcions de renderitzat
    Rendering::g_current_scale_factor = zoom_factor_;
}

void SDLManager::increaseWindowSize() {
    if (is_fullscreen_) {
        return;
    }

    float new_zoom = zoom_factor_ + Defaults::Window::ZOOM_INCREMENT;
    applyZoom(new_zoom);

    std::cout << "F2: Zoom aumentat a " << zoom_factor_ << "x" << std::endl;
}

void SDLManager::decreaseWindowSize() {
    if (is_fullscreen_) {
        return;
    }

    float new_zoom = zoom_factor_ - Defaults::Window::ZOOM_INCREMENT;
    applyZoom(new_zoom);

    std::cout << "F1: Zoom reduït a " << zoom_factor_ << "x" << std::endl;
}

void SDLManager::applyWindowSize(int new_width, int new_height) {
    // Obtenir posició actual ABANS del resize
    int old_x;
    int old_y;
    SDL_GetWindowPosition(finestra_, &old_x, &old_y);

    int old_width = current_width_;
    int old_height = current_height_;

    // Actualitzar mida
    SDL_SetWindowSize(finestra_, new_width, new_height);
    current_width_ = new_width;
    current_height_ = new_height;

    // CENTRADO INTEL·LIGENT (algoritme de pollo)
    // Calcular nova posició per mantenir la finestra centrada sobre si mateixa
    int delta_width = old_width - new_width;
    int delta_height = old_height - new_height;

    int new_x = old_x + (delta_width / 2);
    int new_y = old_y + (delta_height / 2);

    // Evitar que la finestra surti de la pantalla
    constexpr int TITLEBAR_HEIGHT = 35;  // Alçada aproximada de la barra de títol
    new_x = std::max(new_x, 0);
    new_y = std::max(new_y, TITLEBAR_HEIGHT);

    SDL_SetWindowPosition(finestra_, new_x, new_y);

    // Actualitzar viewport després del resize
    updateViewport();
}

void SDLManager::toggleFullscreen() {
    if (!is_fullscreen_) {
        // ENTERING FULLSCREEN
        windowed_width_ = current_width_;
        windowed_height_ = current_height_;

        is_fullscreen_ = true;
        SDL_SetWindowFullscreen(finestra_, true);

        std::cout << "F3: Fullscreen activat (guardada: "
                  << windowed_width_ << "x" << windowed_height_ << ")" << std::endl;
    } else {
        // EXITING FULLSCREEN
        is_fullscreen_ = false;
        SDL_SetWindowFullscreen(finestra_, false);

        // CRITICAL: Explicitly restore windowed size
        applyWindowSize(windowed_width_, windowed_height_);

        std::cout << "F3: Fullscreen desactivat (restaurada: "
                  << windowed_width_ << "x" << windowed_height_ << ")" << std::endl;
    }

    Options::window.fullscreen = is_fullscreen_;

    // Notificar al mòdul Mouse: Fullscreen requereix ocultació permanent del cursor.
    // Quan es surt de fullscreen, restaurar el comportament normal d'auto-ocultació.
    Mouse::setForceHidden(is_fullscreen_);
}

bool SDLManager::handleWindowEvent(const SDL_Event& event) {
    if (event.type == SDL_EVENT_WINDOW_RESIZED) {
        SDL_GetWindowSize(finestra_, &current_width_, &current_height_);

        // Calculate zoom from actual size (may not align to 0.1 increments)
        float new_zoom = static_cast<float>(current_width_) / Defaults::Window::WIDTH;
        zoom_factor_ = std::max(Defaults::Window::MIN_ZOOM,
            std::min(new_zoom, max_zoom_));

        // Update windowed cache (if not in fullscreen)
        if (!is_fullscreen_) {
            windowed_width_ = current_width_;
            windowed_height_ = current_height_;
        }

        // Actualitzar viewport després del resize manual
        updateViewport();

        std::cout << "Finestra redimensionada: " << current_width_
                  << "x" << current_height_ << " (zoom ≈" << zoom_factor_ << "x)"
                  << std::endl;
        return true;
    }
    return false;
}

void SDLManager::neteja(uint8_t r, uint8_t g, uint8_t b) {
    if (renderer_ == nullptr) {
        return;
    }

    // [MODIFICAT] Usar color oscil·lat del fons en lloc dels paràmetres
    (void)r;
    (void)g;
    (void)b;  // Suprimir warnings
    SDL_Color bg = color_oscillator_.getCurrentBackgroundColor();
    SDL_SetRenderDrawColor(renderer_, bg.r, bg.g, bg.b, 255);
    SDL_RenderClear(renderer_);
}

void SDLManager::presenta() {
    if (renderer_ == nullptr) {
        return;
    }

    SDL_RenderPresent(renderer_);
}

// [NUEVO] Actualitzar colors amb oscil·lació
void SDLManager::updateColors(float delta_time) {
    color_oscillator_.update(delta_time);

    // Actualitzar color global de línies
    Rendering::setLineColor(color_oscillator_.getCurrentLineColor());
}

// [NUEVO] Actualitzar comptador de FPS
void SDLManager::updateFPS(float delta_time) {
    // Acumular temps i frames
    fps_accumulator_ += delta_time;
    fps_frame_count_++;

    // Actualitzar display cada 0.5 segons
    if (fps_accumulator_ >= 0.5F) {
        fps_display_ = static_cast<int>(fps_frame_count_ / fps_accumulator_);
        fps_frame_count_ = 0;
        fps_accumulator_ = 0.0F;

        // Actualitzar títol de la finestra
        std::string vsync_state = (Options::rendering.vsync == 1) ? "ON" : "OFF";
        std::string title = std::format("{} v{} ({}) - {} FPS - VSync: {}",
            Project::LONG_NAME,
            Project::VERSION,
            Project::COPYRIGHT,
            fps_display_,
            vsync_state);

        if (finestra_ != nullptr) {
            SDL_SetWindowTitle(finestra_, title.c_str());
        }
    }
}

// [NUEVO] Actualitzar títol de la finestra
void SDLManager::setWindowTitle(const std::string& title) {
    if (finestra_ != nullptr) {
        SDL_SetWindowTitle(finestra_, title.c_str());
    }
}

// [NUEVO] Toggle V-Sync (F4)
void SDLManager::toggleVSync() {
    // Toggle: 1 → 0 → 1
    Options::rendering.vsync = (Options::rendering.vsync == 1) ? 0 : 1;

    // Aplicar a SDL
    if (renderer_ != nullptr) {
        SDL_SetRenderVSync(renderer_, Options::rendering.vsync);
    }

    // Reset FPS counter para evitar valores mixtos entre regímenes
    fps_accumulator_ = 0.0F;
    fps_frame_count_ = 0;

    // Guardar configuració
    Options::saveToFile();
}
