#pragma once

#include <SDL3/SDL_events.h>  // for SDL_Event
#include <SDL3/SDL_render.h>  // for SDL_Renderer (ui_renderer_ software renderer)
#include <SDL3/SDL_stdinc.h>  // for Uint64
#include <SDL3/SDL_surface.h> // for SDL_Surface (ui_surface_)
#include <SDL3/SDL_video.h>   // for SDL_Window

#include <array>   // for array
#include <memory>  // for unique_ptr, shared_ptr
#include <string>  // for string
#include <vector>  // for vector

#include "ui/app_logo.hpp"              // for AppLogo
#include "ball.hpp"                     // for Ball
#include "boids_mgr/boid_manager.hpp"   // for BoidManager
#include "defines.hpp"                  // for GravityDirection, ColorTheme, ShapeType
#include "external/texture.hpp"         // for Texture
#include "gpu/gpu_ball_buffer.hpp"      // for GpuBallBuffer, BallGPUData
#include "gpu/gpu_context.hpp"          // for GpuContext
#include "gpu/gpu_pipeline.hpp"         // for GpuPipeline
#include "gpu/gpu_sprite_batch.hpp"     // for GpuSpriteBatch
#include "gpu/gpu_texture.hpp"          // for GpuTexture
#include "input/input_handler.hpp"      // for InputHandler
#include "scene/scene_manager.hpp"      // for SceneManager
#include "shapes_mgr/shape_manager.hpp" // for ShapeManager
#include "state/state_manager.hpp"      // for StateManager
#include "theme_manager.hpp"            // for ThemeManager
#include "ui/ui_manager.hpp"            // for UIManager

class Engine {
    public:
        // Interfaz pública principal
        bool initialize(int width = 0, int height = 0, int zoom = 0, bool fullscreen = false, AppMode initial_mode = AppMode::SANDBOX);
        void run();
        void shutdown();

        // === Métodos públicos para InputHandler ===

        // Gravedad y física
        void pushBallsAwayFromGravity();
        void handleGravityToggle();
        void handleGravityDirectionChange(GravityDirection direction, const char* notification_text);

        // Display y depuración
        void toggleVSync();
        void toggleDebug();
        void toggleHelp();

        // Figuras 3D
        void toggleShapeMode();
        void activateShape(ShapeType type, const char* notification_text);
        void handleShapeScaleChange(bool increase);
        void resetShapeScale();
        void toggleDepthZoom();

        // Boids (comportamiento de enjambre)
        void toggleBoidsMode(bool force_gravity_on = true);

        // Temas de colores
        void cycleTheme(bool forward);
        void switchThemeByNumpad(int numpad_key);
        void toggleThemePage();
        void pauseDynamicTheme();

        // Sprites/Texturas
        void switchTexture();

        // Escenarios (número de pelotas)
        void changeScenario(int scenario_id, const char* notification_text);

        // Zoom y fullscreen
        void handleZoomIn();
        void handleZoomOut();
        void toggleFullscreen();
        void toggleRealFullscreen();
        void toggleIntegerScaling();

        // Campo de juego (tamaño lógico + físico)
        void fieldSizeUp();
        void fieldSizeDown();
        void setFieldScale(float new_scale);

        // PostFX presets
        void handlePostFXCycle();
        void handlePostFXToggle();
        void setInitialPostFX(int mode);
        void setPostFXParamOverrides(float vignette, float chroma);

        // Cicle PostFX nadiu (OFF → Vinyeta → Scanlines → Cromàtica → Complet)
        void cycleShader();

        // Modo kiosko
        void setKioskMode(bool enabled) { kiosk_mode_ = enabled; }
        bool isKioskMode() const { return kiosk_mode_; }

        // Escenario custom (tecla 9, --custom-balls)
        void setCustomScenario(int balls);
        bool isCustomScenarioEnabled() const { return custom_scenario_enabled_; }
        bool isCustomAutoAvailable()   const { return custom_auto_available_; }
        int  getCustomScenarioBalls()  const { return custom_scenario_balls_; }

        // Control manual del benchmark (--skip-benchmark, --max-balls)
        void setSkipBenchmark();
        void setMaxBallsOverride(int n);

        // Notificaciones (público para InputHandler)
        void showNotificationForAction(const std::string& text);

        // Modos de aplicación (DEMO/LOGO)
        void toggleDemoMode();
        void toggleDemoLiteMode();
        void toggleLogoMode();

        // === Métodos públicos para StateManager (automatización DEMO/LOGO sin notificación) ===
        void enterShapeMode(ShapeType type);           // Activar figura (sin notificación)
        void exitShapeMode(bool force_gravity = true); // Volver a física (sin notificación)
        void switchTextureSilent();                    // Cambiar textura (sin notificación)
        void setTextureByIndex(size_t index);          // Restaurar textura específica

        // === Getters públicos para UIManager (Debug HUD) ===
        bool getVSyncEnabled() const { return vsync_enabled_; }
        bool getFullscreenEnabled() const { return fullscreen_enabled_; }
        bool getRealFullscreenEnabled() const { return real_fullscreen_enabled_; }
        ScalingMode getCurrentScalingMode() const { return current_scaling_mode_; }
        int getCurrentScreenWidth() const { return current_screen_width_; }
        int getCurrentScreenHeight() const { return current_screen_height_; }
        std::string getCurrentTextureName() const {
            if (texture_names_.empty()) return "";
            return texture_names_[current_texture_index_];
        }
        int getBaseScreenWidth() const { return base_screen_width_; }
        int getBaseScreenHeight() const { return base_screen_height_; }
        int getMaxAutoScenario() const { return max_auto_scenario_; }
        size_t getCurrentTextureIndex() const { return current_texture_index_; }
        bool  isPostFXEnabled()    const { return postfx_enabled_; }
        int   getPostFXMode()      const { return postfx_effect_mode_; }
        float getPostFXVignette()  const { return postfx_uniforms_.vignette_strength; }
        float getPostFXChroma()    const { return postfx_uniforms_.chroma_strength; }
        float getPostFXScanline()  const { return postfx_uniforms_.scanline_strength; }

    private:
        // === Componentes del sistema (Composición) ===
        std::unique_ptr<InputHandler> input_handler_;  // Manejo de entradas SDL
        std::unique_ptr<SceneManager> scene_manager_;  // Gestión de bolas y física
        std::unique_ptr<ShapeManager> shape_manager_;  // Gestión de figuras 3D
        std::unique_ptr<BoidManager> boid_manager_;    // Gestión de comportamiento boids
        std::unique_ptr<StateManager> state_manager_;  // Gestión de estados (DEMO/LOGO)
        std::unique_ptr<UIManager> ui_manager_;        // Gestión de UI (HUD, FPS, notificaciones)
        std::unique_ptr<AppLogo> app_logo_;            // Gestión de logo periódico en pantalla

        // === SDL window ===
        SDL_Window* window_ = nullptr;

        // === SDL_GPU rendering pipeline ===
        std::unique_ptr<GpuContext>      gpu_ctx_;          // Device + swapchain
        std::unique_ptr<GpuPipeline>     gpu_pipeline_;     // Sprite + ball + postfx pipelines
        std::unique_ptr<GpuSpriteBatch>  sprite_batch_;     // Per-frame vertex/index batch (bg + shape + UI)
        std::unique_ptr<GpuBallBuffer>   gpu_ball_buffer_;  // Instanced ball instance data (PHYSICS/BOIDS)
        std::vector<BallGPUData>         ball_gpu_data_;    // CPU-side staging vector (reused each frame)
        std::unique_ptr<GpuTexture>      offscreen_tex_;    // Offscreen render target (Pass 1)
        std::unique_ptr<GpuTexture>      white_tex_;        // 1×1 white (background gradient)
        std::unique_ptr<GpuTexture>      ui_tex_;           // UI text overlay texture

        // GPU sprite textures (one per ball skin, parallel to textures_/texture_names_)
        std::unique_ptr<GpuTexture>              gpu_texture_;   // Active GPU sprite texture
        std::vector<std::unique_ptr<GpuTexture>> gpu_textures_;  // All GPU sprite textures

        // === SDL_Renderer (software, for UI text via SDL3_ttf) ===
        // Renders to ui_surface_, then uploaded as gpu texture overlay.
        SDL_Renderer* ui_renderer_ = nullptr;
        SDL_Surface*  ui_surface_  = nullptr;

        // Legacy Texture objects — kept for ball physics sizing and AppLogo
        std::shared_ptr<Texture> texture_ = nullptr;      // Textura activa actual
        std::vector<std::shared_ptr<Texture>> textures_;  // Todas las texturas disponibles
        std::vector<std::string> texture_names_;          // Nombres de texturas (sin extensión)
        size_t current_texture_index_ = 0;                // Índice de textura activa
        int current_ball_size_ = 10;                      // Tamaño actual de pelotas (dinámico)

        // Estado del simulador
        bool should_exit_ = false;

        // Sistema de timing
        Uint64 last_frame_time_ = 0;
        float delta_time_ = 0.0f;

        // PostFX uniforms (passed to GPU each frame)
        PostFXUniforms postfx_uniforms_ = {0.0f, 0.0f, 0.0f, 0.0f};
        int postfx_effect_mode_ = 3;
        bool postfx_enabled_ = false;
        float postfx_override_vignette_ = -1.f;  // -1 = sin override
        float postfx_override_chroma_   = -1.f;

        // Sistema de escala de ventana
        float current_window_scale_ = 1.0f;

        // Escala del campo de juego lógico (F7/F8)
        float current_field_scale_ = 1.0f;

        // V-Sync y fullscreen
        bool vsync_enabled_ = true;
        bool fullscreen_enabled_ = false;
        bool real_fullscreen_enabled_ = false;
        bool kiosk_mode_ = false;
        ScalingMode current_scaling_mode_ = ScalingMode::INTEGER;

        // Resolución base (configurada por CLI o default)
        int base_screen_width_ = DEFAULT_SCREEN_WIDTH;
        int base_screen_height_ = DEFAULT_SCREEN_HEIGHT;

        // Resolución dinámica actual (cambia en fullscreen real)
        int current_screen_width_ = DEFAULT_SCREEN_WIDTH;
        int current_screen_height_ = DEFAULT_SCREEN_HEIGHT;

        // Resolución física real de ventana/pantalla (para texto absoluto)
        int physical_window_width_ = DEFAULT_SCREEN_WIDTH;
        int physical_window_height_ = DEFAULT_SCREEN_HEIGHT;

        // Sistema de temas (delegado a ThemeManager)
        std::unique_ptr<ThemeManager> theme_manager_;
        int theme_page_ = 0;

        // Modo de simulación actual (PHYSICS/SHAPE/BOIDS)
        SimulationMode current_mode_ = SimulationMode::PHYSICS;

        // Sistema de Modo DEMO (auto-play) y LOGO
        int max_auto_scenario_ = 5;

        // Escenario custom (--custom-balls)
        int  custom_scenario_balls_   = 0;
        bool custom_scenario_enabled_ = false;
        bool custom_auto_available_   = false;
        bool skip_benchmark_          = false;

        // Bucket sort per z-ordering (SHAPE mode)
        static constexpr int DEPTH_SORT_BUCKETS = 256;
        std::array<std::vector<size_t>, DEPTH_SORT_BUCKETS> depth_buckets_;

        // Métodos principales del loop
        void calculateDeltaTime();
        void update();
        void render();

        // Benchmark de rendimiento (determina max_auto_scenario_ al inicio)
        void runPerformanceBenchmark();

        // Métodos auxiliares privados

        // Sistema de cambio de sprites dinámico
        void switchTextureInternal(bool show_notification);

        // Sistema de escala de ventana
        float calculateMaxWindowScale() const;
        void setWindowScale(float new_scale);
        void zoomIn();
        void zoomOut();
        void updatePhysicalWindowSize();

        // Rendering (GPU path replaces addSpriteToBatch)
        void addSpriteToBatch(float x, float y, float w, float h, int r, int g, int b, float scale = 1.0f);

        // Sistema de Figuras 3D
        void toggleShapeModeInternal(bool force_gravity_on_exit = true);
        void activateShapeInternal(ShapeType type);
        void updateShape();
        void generateShape();

        // PostFX helper
        void applyPostFXPreset(int mode);

        // GPU helpers
        bool loadGpuSpriteTexture(size_t index);   // Upload one sprite texture to GPU
        void recreateOffscreenTexture();            // Recreate when resolution changes
        void renderUIToSurface();                   // Render text/UI to ui_surface_
        void uploadUISurface(SDL_GPUCommandBuffer* cmd_buf);  // Upload ui_surface_ → ui_tex_

};
