#pragma once

#include <SDL3/SDL.h>  // Para SDL_Event, SDL_Renderer, SDL_Texture, Uint64, Uint8

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

#include "hit.h"                   // Para Hit
#include "item.h"                  // Para Item, ItemType
#include "manage_hiscore_table.h"  // Para HiScoreEntry
#include "options.h"               // Para SettingsOptions, settings
#include "path_sprite.h"           // Para PathSprite, Path
#include "player.h"                // Para Player
#include "smart_sprite.h"          // Para SmartSprite
#include "utils.h"                 // Para Demo

class Background;
class Balloon;
class BalloonManager;
class Bullet;
class Fade;
class Input;
class Scoreboard;
class Screen;
class Tabe;
class Texture;
enum class BulletType : Uint8;
namespace Difficulty {
enum class Code;
}  // namespace Difficulty

// Modo demo
constexpr bool GAME_MODE_DEMO_OFF = false;
constexpr bool GAME_MODE_DEMO_ON = true;

// Cantidad de elementos a escribir en los ficheros de datos
constexpr int TOTAL_SCORE_DATA = 3;

// Clase Game
class Game {
    public:
        // --- Constructor y destructor ---
        Game(int player_id, int current_stage, bool demo);
        ~Game();

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

    private:
        // --- Tipos internos ---
        enum class State {
            FADE_IN,
            ENTERING_PLAYER,
            SHOWING_GET_READY_MESSAGE,
            PLAYING,
            COMPLETED,
            GAME_OVER,
        };

        // --- Constantes internas ---
        static constexpr int HELP_COUNTER = 1000;
        static constexpr int GAME_COMPLETED_START_FADE = 500;
        static constexpr int GAME_COMPLETED_END = 700;
        static constexpr int GAME_OVER_COUNTER = 350;
        static constexpr int TIME_STOPPED_COUNTER = 360;
        static constexpr int ITEM_POINTS_1_DISK_ODDS = 10;
        static constexpr int ITEM_POINTS_2_GAVINA_ODDS = 6;
        static constexpr int ITEM_POINTS_3_PACMAR_ODDS = 3;
        static constexpr int ITEM_CLOCK_ODDS = 5;
        static constexpr int ITEM_COFFEE_ODDS = 5;
        static constexpr int ITEM_POWER_BALL_ODDS = 0;
        static constexpr int ITEM_COFFEE_MACHINE_ODDS = 4;

        // --- Estructuras ---
        struct Helper {
                bool need_coffee{false};          // Indica si se necesitan cafes
                bool need_coffee_machine{false};  // Indica si se necesita PowerUp
                bool need_power_ball{false};      // Indica si se necesita una PowerBall
                int counter;                      // Contador para no dar ayudas consecutivas
                int item_disk_odds;               // Probabilidad de aparición del objeto
                int item_gavina_odds;             // Probabilidad de aparición del objeto
                int item_pacmar_odds;             // Probabilidad de aparición del objeto
                int item_clock_odds;              // Probabilidad de aparición del objeto
                int item_coffee_odds;             // Probabilidad de aparición del objeto
                int item_coffee_machine_odds;     // Probabilidad de aparición del objeto

                Helper()
                    : counter(HELP_COUNTER),
                      item_disk_odds(ITEM_POINTS_1_DISK_ODDS),
                      item_gavina_odds(ITEM_POINTS_2_GAVINA_ODDS),
                      item_pacmar_odds(ITEM_POINTS_3_PACMAR_ODDS),
                      item_clock_odds(ITEM_CLOCK_ODDS),
                      item_coffee_odds(ITEM_COFFEE_ODDS),
                      item_coffee_machine_odds(ITEM_COFFEE_MACHINE_ODDS) {}
        };

        // --- Objetos y punteros ---
        SDL_Renderer *renderer_;  // El renderizador de la ventana
        Screen *screen_;          // Objeto encargado de dibujar en pantalla
        Input *input_;            // Manejador de entrada
        Scoreboard *scoreboard_;  // Objeto para dibujar el marcador

        std::unique_ptr<Background> background_;  // Objeto para dibujar el fondo del juego

        SDL_Texture *canvas_;  // Textura para dibujar la zona de juego

        std::vector<std::shared_ptr<Player>> players_;             // Vector con los jugadores
        std::vector<std::shared_ptr<Bullet>> bullets_;             // Vector con las balas
        std::vector<std::unique_ptr<Item>> items_;                 // Vector con los items
        std::vector<std::unique_ptr<SmartSprite>> smart_sprites_;  // Vector con los smartsprites
        std::vector<std::unique_ptr<PathSprite>> path_sprites_;    // Vector con los pathsprites

        std::vector<std::shared_ptr<Texture>> item_textures_;                 // Vector con las texturas de los items
        std::vector<std::vector<std::shared_ptr<Texture>>> player_textures_;  // Vector con todas las texturas de los jugadores

        std::vector<std::shared_ptr<Texture>> game_text_textures_;  // Vector con las texturas para los sprites con textos

        std::vector<std::vector<std::string>> item_animations_;    // Vector con las animaciones de los items
        std::vector<std::vector<std::string>> player_animations_;  // Vector con las animaciones del jugador

        std::unique_ptr<Fade> fade_in_;                    // Objeto para renderizar fades
        std::unique_ptr<Fade> fade_out_;                   // Objeto para renderizar fades
        std::unique_ptr<BalloonManager> balloon_manager_;  // Objeto para gestionar los globos
        std::unique_ptr<Tabe> tabe_;                       // Objeto para gestionar el Tabe Volaor
        std::vector<Path> paths_;                          // Vector con los recorridos precalculados almacenados

        // --- Variables de estado ---
        HiScoreEntry hi_score_ = HiScoreEntry(
            Options::settings.hi_score_table[0].name,
            Options::settings.hi_score_table[0].score);  // Máxima puntuación y nombre de quien la ostenta

        Demo demo_;                                                   // Variable con todas las variables relacionadas con el modo demo
        Difficulty::Code difficulty_ = Options::settings.difficulty;  // Dificultad del juego
        Helper helper_;                                               // Variable para gestionar las ayudas
        Uint64 ticks_ = 0;                                            // Contador de ticks para ajustar la velocidad del programa
        bool coffee_machine_enabled_ = false;                         // Indica si hay una máquina de café en el terreno de juego
        bool hi_score_achieved_ = false;                              // Indica si se ha superado la puntuación máxima
        bool paused_ = false;                                         // Indica si el juego está pausado (no se deberia de poder utilizar en el modo arcade)
        // bool paused_by_service_menu_ = false;
        float difficulty_score_multiplier_;          // Multiplicador de puntos en función de la dificultad
        int counter_ = 0;                            // Contador para el juego
        int game_completed_counter_ = 0;             // Contador para el tramo final, cuando se ha completado la partida y ya no aparecen más globos
        int game_over_counter_ = GAME_OVER_COUNTER;  // Contador para el estado de fin de partida
        int time_stopped_counter_ = 0;               // Temporizador para llevar la cuenta del tiempo detenido
        int total_power_to_complete_game_;           // La suma del poder necesario para completar todas las fases
        int menace_current_ = 0;                     // Nivel de amenaza actual
        int menace_threshold_ = 0;                   // Umbral del nivel de amenaza. Si el nivel de amenaza cae por debajo del umbral, se generan más globos. Si el umbral aumenta, aumenta el número de globos
        State state_ = State::FADE_IN;               // Estado
        std::vector<std::shared_ptr<Player>> players_to_put_at_back_;
        std::vector<std::shared_ptr<Player>> players_to_put_at_front_;
        Hit hit_;  // Para representar colisiones en pantalla

#ifdef _DEBUG
        bool auto_pop_balloons_ = false;  // Si es true, incrementa automaticamente los globos explotados
#endif

        // --- Ciclo principal del juego ---
        void update();               // Actualiza la lógica principal del juego
        void render();               // Renderiza todos los elementos del juego
        void checkEvents();          // Procesa los eventos del sistema en cola
        void checkState();           // Verifica y actualiza el estado actual del juego
        void setState(State state);  // Cambia el estado del juego
        void pause(bool value);      // Pausa o reanuda el juego
        void cleanVectors();         // Limpia vectores de elementos deshabilitados

        // --- Gestión de estados del juego ---
        void updateGameStates();                       // Actualiza todos los estados del juego
        void updateGameStateFadeIn();                  // Gestiona el estado de transición de entrada
        void updateGameStateEnteringPlayer();          // Gestiona el estado de entrada de jugador
        void updateGameStateShowingGetReadyMessage();  // Gestiona el estado de mensaje "preparado"
        void updateGameStatePlaying();                 // Gestiona el estado de juego activo
        void updateGameStateCompleted();               // Gestiona el estado de juego completado
        void updateGameStateGameOver();                // Gestiona el estado de fin de partida

        // --- Gestión de jugadores ---
        void initPlayers(int player_id);                    // Inicializa los datos de los jugadores
        void updatePlayers();                               // Actualiza las variables y estados de los jugadores
        void renderPlayers();                               // Renderiza todos los jugadores en pantalla
        void sortPlayersByZOrder();                         // Reorganiza el orden de dibujado de jugadores
        auto getPlayer(int id) -> std::shared_ptr<Player>;  // Obtiene un jugador por su identificador
        static auto getController(int player_id) -> int;    // Obtiene el controlador asignado a un jugador

        // --- Estado de jugadores ---
        void checkAndUpdatePlayerStatus(int active_player_index, int inactive_player_index);  // Actualiza estado entre jugadores
        void checkPlayersStatusPlaying();                                                     // Verifica el estado de juego de todos los jugadores
        auto allPlayersAreWaitingOrGameOver() -> bool;                                        // Verifica si todos esperan o han perdido
        auto allPlayersAreGameOver() -> bool;                                                 // Verifica si todos los jugadores han perdido
        auto allPlayersAreNotPlaying() -> bool;                                               // Verifica si ningún jugador está activo

        // --- Colisiones de jugadores ---
        void handlePlayerCollision(std::shared_ptr<Player> &player, std::shared_ptr<Balloon> &balloon);  // Procesa colisión de jugador con globo
        auto checkPlayerBalloonCollision(std::shared_ptr<Player> &player) -> std::shared_ptr<Balloon>;   // Detecta colisión jugador-globo
        void checkPlayerItemCollision(std::shared_ptr<Player> &player);                                  // Detecta colisión jugador-ítem

        // --- Sistema de entrada (input) ---
        void checkInput();       // Gestiona toda la entrada durante el juego
        void checkPauseInput();  // Verifica solicitudes de pausa de controladores

        // --- Entrada de jugadores normales ---
        void handlePlayersInput();                                                            // Gestiona entrada de todos los jugadores
        void handleNormalPlayerInput(const std::shared_ptr<Player> &player);                  // Procesa entrada de un jugador específico
        void handleFireInput(const std::shared_ptr<Player> &player, BulletType bullet_type);  // Gestiona disparo de jugador
        void handleFireInputs(const std::shared_ptr<Player> &player, bool autofire);          // Procesa disparos automáticos
        void handlePlayerContinueInput(const std::shared_ptr<Player> &player);                // Permite continuar al jugador
        void handlePlayerWaitingInput(const std::shared_ptr<Player> &player);                 // Permite (re)entrar al jugador
        void handleNameInput(const std::shared_ptr<Player> &player);                          // Gestiona entrada de nombre del jugador

        // --- Entrada en modo demo ---
        void demoHandleInput();                                                        // Gestiona entrada durante el modo demostración
        void demoHandlePassInput();                                                    // Permite saltar la demostración
        void demoHandlePlayerInput(const std::shared_ptr<Player> &player, int index);  // Procesa entrada de jugador en demo

        // --- Sistema de balas y proyectiles ---
        void updateBullets();                                                          // Actualiza posición y estado de todas las balas
        void renderBullets();                                                          // Renderiza todas las balas activas
        void createBullet(int x, int y, BulletType kind, bool powered_up, int owner);  // Crea una nueva bala
        void checkBulletCollision();                                                   // Verifica colisiones de todas las balas
        void freeBullets();                                                            // Libera memoria del vector de balas

        // --- Colisiones específicas de balas ---
        auto checkBulletTabeCollision(std::shared_ptr<Bullet> bullet) -> bool;                     // Detecta colisión bala-Tabe
        auto checkBulletBalloonCollision(std::shared_ptr<Bullet> bullet) -> bool;                  // Detecta colisión bala-globo
        void processBalloonHit(std::shared_ptr<Bullet> bullet, std::shared_ptr<Balloon> balloon);  // Procesa impacto en globo

        // --- Sistema de ítems y power-ups ---
        void updateItems();                                // Actualiza posición y estado de todos los ítems
        void renderItems();                                // Renderiza todos los ítems activos
        auto dropItem() -> ItemType;                       // Determina aleatoriamente qué ítem soltar
        void createItem(ItemType type, float x, float y);  // Crea un nuevo ítem en posición específica
        void freeItems();                                  // Libera memoria del vector de ítems
        void destroyAllItems();                            // Elimina todos los ítems activos de la pantalla

        // --- ítems especiales ---
        void enableTimeStopItem();       // Activa el efecto de detener el tiempo
        void disableTimeStopItem();      // Desactiva el efecto de detener el tiempo
        void updateTimeStopped();        // Actualiza el estado del tiempo detenido
        void throwCoffee(int x, int y);  // Crea efecto de café arrojado al ser golpeado

        // --- Gestión de caída de ítems ---
        void handleItemDrop(std::shared_ptr<Balloon> balloon, std::shared_ptr<Player> player);  // Gestiona caída de ítem desde globo

        // --- Sprites inteligentes (smartsprites) ---
        void updateSmartSprites();  // Actualiza todos los sprites con lógica propia
        void renderSmartSprites();  // Renderiza todos los sprites inteligentes
        void freeSmartSprites();    // Libera memoria de sprites inteligentes

        // --- Sprites por ruta (pathsprites) ---
        void updatePathSprites();  // Actualiza sprites que siguen rutas predefinidas
        void renderPathSprites();  // Renderiza sprites animados por ruta
        void freePathSprites();    // Libera memoria de sprites por ruta
        void initPaths();          // Inicializa rutas predefinidas para animaciones

        // --- Creación de sprites especiales ---
        void createItemText(int x, std::shared_ptr<Texture> texture);                          // Crea texto animado para ítems
        void createMessage(const std::vector<Path> &paths, std::shared_ptr<Texture> texture);  // Crea mensaje con animación por ruta

        // --- Sistema de globos y enemigos ---
        void handleBalloonDestruction(std::shared_ptr<Balloon> balloon, std::shared_ptr<Player> player);  // Procesa destrucción de globo
        void handleTabeHitEffects();                                                                      // Gestiona efectos al golpear a Tabe
        void checkAndUpdateBalloonSpeed();                                                                // Ajusta velocidad de globos según progreso

        // --- Gestión de fases y progresión ---
        void updateStage();         // Verifica y actualiza cambio de fase
        void setTotalPower();       // Calcula poder total necesario para completar el juego
        void initDifficultyVars();  // Inicializa variables de dificultad

        // --- Sistema de amenaza ---
        void updateMenace();          // Gestiona el nivel de amenaza del juego
        void evaluateAndSetMenace();  // Calcula y establece amenaza según globos activos

        // --- Puntuación y marcador ---
        void updateHiScore();                                              // Actualiza el récord máximo si es necesario
        void updateScoreboard();                                           // Actualiza la visualización del marcador
        void addScoreToScoreBoard(const std::shared_ptr<Player> &player);  // Añade puntuación del jugador al marcador
        void initScoreboard();                                             // Inicializa el sistema de puntuación

        // --- Modo demostración ---
        void initDemo(int player_id);  // Inicializa variables para el modo demostración
        void updateDemo();             // Actualiza lógica específica del modo demo

        // --- Recursos y renderizado ---
        void setResources();      // Asigna texturas y animaciones a los objetos
        void updateBackground();  // Actualiza elementos del fondo
        void fillCanvas();        // Renderiza elementos del área de juego en su textura
        void updateHelper();      // Actualiza variables auxiliares de renderizado

        // --- Sistema de audio ---
        static void playMusic();                        // Reproduce la música de fondo
        void stopMusic() const;                         // Detiene la reproducción de música
        void pauseMusic();                              // Pausa la música
        void resumeMusic();                             // Retoma la música que eestaba pausada
        void playSound(const std::string &name) const;  // Reproduce un efecto de sonido específico

        // --- Utilidades y servicios ---
        void checkServiceMenu();  // Verifica si el menú de servicio está activo

        void sendPlayerToTheBack(const std::shared_ptr<Player> &player);   // Mueve el jugador para pintarlo al fondo de la lista de jugadores
        void sendPlayerToTheFront(const std::shared_ptr<Player> &player);  // Mueve el jugador para pintarlo el primero de la lista de jugadores

// SISTEMA DE GRABACIÓN (CONDICIONAL)
#ifdef RECORDING
        void updateRecording();  // Actualiza variables durante modo de grabación
#endif

// --- Depuración (solo en modo DEBUG) ---
#ifdef _DEBUG
        void checkDebugEvents(const SDL_Event &event);  // Comprueba los eventos en el modo DEBUG
#endif
};