#pragma once

#include <SDL3/SDL.h>  // Para Uint8, Uint16, SDL_FRect, Uint32

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

#include "animated_sprite.h"  // Para AnimatedSprite
#include "utils.h"            // Para Circle

class Texture;

// --- Clase Balloon ---
class Balloon {
    public:
        // --- Constantes relacionadas con globos ---
        static constexpr int MAX_BOUNCE = 10;  // Cantidad de elementos del vector de deformación

        static constexpr std::array<int, 4> SCORE = {50, 100, 200, 400};
        static constexpr std::array<int, 4> POWER = {1, 3, 7, 15};
        static constexpr std::array<int, 4> MENACE = {1, 2, 4, 8};
        static constexpr std::array<int, 5> SIZE = {10, 16, 26, 48, 49};

        const std::array<std::string, 4> BOUNCING_SOUND = {
            "balloon_bounce0.wav", "balloon_bounce1.wav", "balloon_bounce2.wav", "balloon_bounce3.wav"};

        const std::array<std::string, 4> POPPING_SOUND = {
            "balloon_pop0.wav", "balloon_pop1.wav", "balloon_pop2.wav", "balloon_pop3.wav"};

        static constexpr float VELX_POSITIVE = 0.7F;
        static constexpr float VELX_NEGATIVE = -0.7F;

        static constexpr int MOVING_ANIMATION = 0;
        static constexpr int POP_ANIMATION = 1;
        static constexpr int BORN_ANIMATION = 2;

        static constexpr std::array<float, 5> SPEED = {0.60F, 0.70F, 0.80F, 0.90F, 1.00F};

        static constexpr int POWERBALL_SCREENPOWER_MINIMUM = 10;
        static constexpr int POWERBALL_COUNTER = 8;

        enum class Size : Uint8 {
            SMALL = 0,
            MEDIUM = 1,
            LARGE = 2,
            EXTRALARGE = 3,
        };

        enum class Type : Uint8 {
            BALLOON = 0,
            FLOATER = 1,
            POWERBALL = 2,
        };

        // --- Constructores y destructor ---
        Balloon(
            float x,
            float y,
            Type type,
            Size size,
            float vel_x,
            float speed,
            Uint16 creation_timer,
            SDL_FRect play_area,
            std::shared_ptr<Texture> texture,
            const std::vector<std::string> &animation);
        ~Balloon() = default;

        // --- Métodos principales ---
        void alignTo(int x);                  // Centra el globo en la posición X
        void render();                        // Pinta el globo en la pantalla
        void move();                          // Actualiza la posición y estados del globo
        void update();                        // Actualiza el globo (posición, animación, contadores)
        void stop();                          // Detiene el globo
        void start();                         // Pone el globo en movimiento
        void pop(bool should_sound = false);  // Explota el globo

        // --- Métodos de color ---
        void useReverseColor();  // Pone el color alternativo en el globo
        void useNormalColor();   // Pone el color normal en el globo

        // --- Getters ---
        [[nodiscard]] auto getPosX() const -> float { return x_; }
        [[nodiscard]] auto getPosY() const -> float { return y_; }
        [[nodiscard]] auto getWidth() const -> int { return w_; }
        [[nodiscard]] auto getHeight() const -> int { return h_; }
        [[nodiscard]] auto getSize() const -> Size { return size_; }
        [[nodiscard]] auto getType() const -> Type { return type_; }
        [[nodiscard]] auto getScore() const -> Uint16 { return score_; }
        auto getCollider() -> Circle & { return collider_; }
        [[nodiscard]] auto getMenace() const -> Uint8 { return isEnabled() ? menace_ : 0; }
        [[nodiscard]] auto getPower() const -> Uint8 { return power_; }
        [[nodiscard]] auto isStopped() const -> bool { return stopped_; }
        [[nodiscard]] auto isPowerBall() const -> bool { return type_ == Type::POWERBALL; }
        [[nodiscard]] auto isInvulnerable() const -> bool { return invulnerable_; }
        [[nodiscard]] auto isBeingCreated() const -> bool { return being_created_; }
        [[nodiscard]] auto isEnabled() const -> bool { return enabled_; }
        [[nodiscard]] auto isUsingReversedColor() const -> bool { return use_reversed_colors_; }
        [[nodiscard]] auto canBePopped() const -> bool { return !isBeingCreated(); }

        // --- Setters ---
        void setVelY(float vel_y) { vy_ = vel_y; }
        void setSpeed(float speed) { speed_ = speed; }
        void setInvulnerable(bool value) { invulnerable_ = value; }
        void setBouncingSound(bool value) { bouncing_sound_enabled_ = value; }
        void setPoppingSound(bool value) { poping_sound_enabled_ = value; }
        void setSound(bool value) { sound_enabled_ = value; }

    private:
        // --- Estructura para el efecto de rebote ---
        struct Bouncing {
                bool enabled = false;          // Si el efecto está activo
                Uint8 counter = 0;             // Contador para el efecto
                Uint8 speed = 2;               // Velocidad del efecto
                float horizontal_zoom = 1.0F;  // Zoom en anchura
                float verical_zoom = 1.0F;     // Zoom en altura
                float desp_x = 0.0F;           // Desplazamiento X antes de pintar
                float desp_y = 0.0F;           // Desplazamiento Y antes de pintar

                std::array<float, MAX_BOUNCE> horizontal_zoom_values = {1.10F, 1.05F, 1.00F, 0.95F, 0.90F, 0.95F, 1.00F, 1.02F, 1.05F, 1.02F};  // Zoom ancho
                std::array<float, MAX_BOUNCE> vertical_zoom_values = {0.90F, 0.95F, 1.00F, 1.05F, 1.10F, 1.05F, 1.00F, 0.98F, 0.95F, 0.98F};    // Zoom alto

                Bouncing() = default;
                void reset() {
                    counter = 0;
                    horizontal_zoom = 1.0F;
                    verical_zoom = 1.0F;
                    desp_x = 0.0F;
                    desp_y = 0.0F;
                }
        } bounce_effect_;

        // --- Objetos y punteros ---
        std::unique_ptr<AnimatedSprite> sprite_;  // Sprite del objeto globo

        // --- Variables de estado y físicas ---
        float x_;                              // Posición X
        float y_;                              // Posición Y
        float w_;                              // Ancho
        float h_;                              // Alto
        float vx_;                             // Velocidad X
        float vy_;                             // Velocidad Y
        float gravity_;                        // Aceleración en Y
        float default_vy_;                     // Velocidad inicial al rebotar
        float max_vy_;                         // Máxima velocidad en Y
        bool being_created_;                   // Si el globo se está creando
        bool enabled_ = true;                  // Si el globo está activo
        bool invulnerable_;                    // Si el globo es invulnerable
        bool stopped_;                         // Si el globo está parado
        bool use_reversed_colors_ = false;     // Si se usa el color alternativo
        Circle collider_;                      // Círculo de colisión
        Uint16 creation_counter_;              // Temporizador de creación
        Uint16 creation_counter_ini_;          // Valor inicial del temporizador de creación
        Uint16 score_;                         // Puntos al destruir el globo
        Type type_;                            // Tipo de globo
        Size size_;                            // Tamaño de globo
        Uint8 menace_;                         // Amenaza que genera el globo
        Uint32 counter_ = 0;                   // Contador interno
        float travel_y_ = 1.0F;                // Distancia a recorrer en Y antes de aplicar gravedad
        float speed_;                          // Velocidad del globo
        Uint8 power_;                          // Poder que alberga el globo
        SDL_FRect play_area_;                  // Zona de movimiento del globo
        std::string bouncing_sound_;           // Archivo de sonido al rebotar
        std::string popping_sound_;            // Archivo de sonido al explotar
        bool bouncing_sound_enabled_ = false;  // Si debe sonar el globo al rebotar
        bool poping_sound_enabled_ = true;     // Si debe sonar el globo al explotar
        bool sound_enabled_ = true;            // Indica si los globos deben hacer algun sonido

        // --- Posicionamiento y transformación ---
        void shiftColliders();     // Alinea el círculo de colisión con el sprite
        void shiftSprite();        // Alinea el sprite en pantalla
        void applyBounceEffect();  // Aplica la deformación visual causada por el efecto de rebote

        // --- Animación y sonido ---
        void setAnimation();                            // Establece la animación correspondiente
        void playSound(const std::string &name) const;  // Reproduce un sonido por nombre
        void playBouncingSound();                       // Reproduce el sonido de rebote

        // --- Movimiento y física ---
        void handleHorizontalMovement();  // Maneja el movimiento horizontal
        void handleVerticalMovement();    // Maneja el movimiento vertical
        void applyGravity();              // Aplica la gravedad al objeto

        // --- Rebote ---
        void enableBounceEffect();                              // Activa el efecto de rebote
        void disableBounceEffect();                             // Detiene el efecto de rebote
        void updateBounceEffect();                              // Actualiza el estado del rebote
        void handleHorizontalBounce(float min_x, float max_x);  // Maneja el rebote horizontal dentro de límites

        // --- Colisiones ---
        [[nodiscard]] auto isOutOfHorizontalBounds(float min_x, float max_x) const -> bool;  // Verifica si está fuera de los límites horizontales
        [[nodiscard]] auto shouldCheckTopCollision() const -> bool;                          // Determina si debe comprobarse la colisión superior
        void handleTopCollision();                                                           // Maneja la colisión superior
        void handleBottomCollision();                                                        // Maneja la colisión inferior

        // --- Lógica de estado ---
        void updateState();  // Actualiza los estados del globo
};