#pragma once

#include <SDL3/SDL.h>  // Para SDL_FRect, Uint32, SDL_Texture, Uint64

#include <memory>  // Para unique_ptr, shared_ptr
#include <vector>  // Para vector

#include "color.hpp"    // Para Zone, Color
#include "options.hpp"  // Para AudioOptions, MusicOptions, audio
#include "param.hpp"    // Para Param, ParamGame, param
#include "utils.hpp"

// Declaraciones adelantadas
class BalloonManager;
class Fade;
class Player;
class TiledBG;

class Credits {
    public:
        // --- Constructor y destructor ---
        Credits();
        ~Credits();

        // --- Bucle principal ---
        void run();

    private:
        // --- Métodos del bucle principal ---
        void update(float delta_time);       // Actualización principal de la lógica (time-based)
        auto calculateDeltaTime() -> float;  // Calcula el deltatime

        // --- Constantes de clase ---
        static constexpr int PLAY_AREA_HEIGHT = 200;

        // --- Objetos principales ---
        std::unique_ptr<BalloonManager> balloon_manager_;  // Gestión de globos
        std::unique_ptr<TiledBG> tiled_bg_;                // Mosaico animado de fondo
        std::unique_ptr<Fade> fade_in_;                    // Fundido de entrada
        std::unique_ptr<Fade> fade_out_;                   // Fundido de salida
        std::vector<std::shared_ptr<Player>> players_;     // Vector de jugadores

        // --- Gestión de texturas ---
        SDL_Texture* text_texture_;  // Textura con el texto de créditos
        SDL_Texture* canvas_;        // Textura donde se dibuja todo

        // --- Temporización y contadores ---
        Uint64 last_time_ = 0;               // Último tiempo registrado para deltaTime
        float counter_ = 0;                  // Contador principal de lógica
        float counter_pre_fade_ = 0;         // Activación del fundido final
        float counter_prevent_endless_ = 0;  // Prevención de bucle infinito

        // --- Variables de estado ---
        bool fading_ = false;                 // Estado del fade final
        bool want_to_pass_ = false;           // Jugador quiere saltarse créditos
        bool mini_logo_on_position_ = false;  // Minilogo en posición final

        // --- Diseño y posicionamiento ---
        float black_bars_size_ = (param.game.game_area.rect.h - PLAY_AREA_HEIGHT) / 2;  // Tamaño de las barras negras
        int mini_logo_final_pos_ = 0;                                                   // Posición final del minilogo
        Color color_;                                                                   // Color usado para los efectos

        // --- Control de audio ---
        int initial_volume_ = Options::audio.music.volume;  // Volumen inicial
        int steps_ = 0;                                     // Pasos para reducir audio

        // --- Estado de acumuladores para animaciones ---
        struct CreditsState {
                float texture_accumulator = 0.0F;
                float balloon_accumulator = 0.0F;
                float powerball_accumulator = 0.0F;
                float black_rect_accumulator = 0.0F;
                float r = 255.0F;  // UPPER_LIMIT
                float g = 0.0F;    // LOWER_LIMIT
                float b = 0.0F;    // LOWER_LIMIT
                float step_r = -0.5F;
                float step_g = 0.3F;
                float step_b = 0.1F;
        } credits_state_;

        // --- Rectángulos de renderizado ---
        // Texto de créditos
        SDL_FRect credits_rect_src_ = param.game.game_area.rect;
        SDL_FRect credits_rect_dst_ = param.game.game_area.rect;

        // Mini logo
        SDL_FRect mini_logo_rect_src_ = param.game.game_area.rect;
        SDL_FRect mini_logo_rect_dst_ = param.game.game_area.rect;

        // Definición del área de juego
        SDL_FRect play_area_ = {
            param.game.game_area.rect.x,
            param.game.game_area.rect.y + black_bars_size_,
            param.game.game_area.rect.w,
            PLAY_AREA_HEIGHT};

        // Barras negras para efecto letterbox
        SDL_FRect top_black_rect_ = {
            play_area_.x,
            param.game.game_area.rect.y,
            play_area_.w,
            black_bars_size_};
        SDL_FRect bottom_black_rect_ = {
            play_area_.x,
            param.game.game_area.rect.h - black_bars_size_,
            play_area_.w,
            black_bars_size_};
        SDL_FRect left_black_rect_ = {
            play_area_.x,
            param.game.game_area.center_y - 1,
            0,
            2};
        SDL_FRect right_black_rect_ = {
            play_area_.x + play_area_.w,
            param.game.game_area.center_y - 1,
            0,
            2};

        // Borde para la ventana
        SDL_FRect border_rect_ = play_area_;  // Delimitador de ventana

        void render();              // Renderizado de la escena
        static void checkEvents();  // Manejo de eventos
        void checkInput();          // Procesamiento de entrada

        // --- Métodos de renderizado ---
        void fillTextTexture();  // Crear textura de texto de créditos
        void fillCanvas();       // Renderizar todos los sprites y fondos
        void renderPlayers();    // Renderiza los jugadores

        // --- Métodos de lógica del juego ---
        void throwBalloons(float delta_time);   // Lanzar globos al escenario (time-based)
        void initPlayers();                     // Inicializar jugadores
        void updateAllFades(float delta_time);  // Actualizar estados de fade (time-based)
        void cycleColors(float delta_time);     // Cambiar colores de fondo
        void updatePlayers(float delta_time);   // Actualza los jugadores (time-based)

        // --- Métodos de interfaz ---
        void updateBlackRects();                       // Actualizar rectángulos negros (letterbox) (frame-based)
        void updateBlackRects(float delta_time);       // Actualizar rectángulos negros (letterbox) (time-based)
        void updateRedRect();                          // Actualizar rectángulo rojo (borde)
        void updateTextureDstRects();                  // Actualizar destinos de texturas (frame-based)
        void updateTextureDstRects(float delta_time);  // Actualizar destinos de texturas (time-based)

        // --- Métodos de audio ---
        static void setVolume(int amount);  // Establecer volumen
        void resetVolume() const;           // Restablecer volumen
};