#pragma once

#include <SDL3/SDL.h>

#include <string>  // for string
#include <vector>  // for vector

#include "utils/utils.h"  // for Circle
class AnimatedSprite;
class Texture;

// Clase Balloon
class Balloon {
   public:
    // Tipos de globo
    static constexpr int BALLOON_1 = 1;
    static constexpr int BALLOON_2 = 2;
    static constexpr int BALLOON_3 = 3;
    static constexpr int BALLOON_4 = 4;
    static constexpr int HEXAGON_1 = 5;
    static constexpr int HEXAGON_2 = 6;
    static constexpr int HEXAGON_3 = 7;
    static constexpr int HEXAGON_4 = 8;
    static constexpr int POWER_BALL = 9;

    // Puntos de globo
    static constexpr int SCORE_1 = 50;
    static constexpr int SCORE_2 = 100;
    static constexpr int SCORE_3 = 200;
    static constexpr int SCORE_4 = 400;

    // Tamaños de globo
    static constexpr int SIZE_1 = 1;
    static constexpr int SIZE_2 = 2;
    static constexpr int SIZE_3 = 3;
    static constexpr int SIZE_4 = 4;

    // Clases de globo
    static constexpr int BALLOON_CLASS = 0;
    static constexpr int HEXAGON_CLASS = 1;

    // Velocidad del globo
    static constexpr float VELX_POSITIVE = 0.7F;
    static constexpr float VELX_NEGATIVE = -0.7F;

    // Velocidades a las que se mueven los globos
    static constexpr float SPEED_1 = 0.60F;
    static constexpr float SPEED_2 = 0.70F;
    static constexpr float SPEED_3 = 0.80F;
    static constexpr float SPEED_4 = 0.90F;
    static constexpr float SPEED_5 = 1.00F;

    // Tamaño de los globos
    static constexpr int WIDTH_1 = 8;
    static constexpr int WIDTH_2 = 13;
    static constexpr int WIDTH_3 = 21;
    static constexpr int WIDTH_4 = 37;

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

    Balloon(float x, float y, Uint8 kind, float velx, float speed, Uint16 creationtimer, Texture *texture, std::vector<std::string> *animation, SDL_Renderer *renderer);  // Constructor
    ~Balloon();                                                                                                                                                           // Destructor

    Balloon(const Balloon &) = delete;
    auto operator=(const Balloon &) -> Balloon & = delete;

    void allignTo(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 disable();        // Deshabilita el globo y pone a cero todos los valores
    void pop();            // Explosiona el globo
    void update();         // Actualiza al globo a su posicion, animación y controla los contadores

    [[nodiscard]] auto isEnabled() const -> bool;       // Comprueba si el globo está habilitado
    [[nodiscard]] auto isStopped() const -> bool;       // Obtiene del valor de la variable
    [[nodiscard]] auto isBlinking() const -> bool;      // Obtiene del valor de la variable
    [[nodiscard]] auto isVisible() const -> bool;       // Obtiene del valor de la variable
    [[nodiscard]] auto isInvulnerable() const -> bool;  // Obtiene del valor de la variable
    [[nodiscard]] auto isBeingCreated() const -> bool;  // Obtiene del valor de la variable
    [[nodiscard]] auto isPopping() const -> bool;       // Obtiene del valor de la variable

    [[nodiscard]] auto getPosX() const -> float;           // Obtiene del valor de la variable
    [[nodiscard]] auto getPosY() const -> float;           // Obtiene del valor de la variable
    [[nodiscard]] auto getVelY() const -> float;           // Obtiene del valor de la variable
    [[nodiscard]] auto getWidth() const -> int;            // Obtiene del valor de la variable
    [[nodiscard]] auto getHeight() const -> int;           // Obtiene del valor de la variable
    [[nodiscard]] auto getKind() const -> int;             // Obtiene del valor de la variable
    [[nodiscard]] auto getSize() const -> Uint8;           // Obtiene del valor de la variable
    [[nodiscard]] auto getClass() const -> Uint8;          // Obtiene la clase a la que pertenece el globo
    [[nodiscard]] auto getStoppedTimer() const -> Uint16;  // Obtiene del valor de la variable
    [[nodiscard]] auto getScore() const -> Uint16;         // Obtiene del valor de la variable
    [[nodiscard]] auto getMenace() const -> Uint8;         // Obtiene le valor de la variable
    [[nodiscard]] auto getPower() const -> Uint8;          // Obtiene le valor de la variable

    void setVelY(float vel_y);          // Establece el valor de la variable
    void setSpeed(float speed);         // Establece el valor de la variable
    void setStop(bool state);           // Establece el valor de la variable
    void setBlink(bool value);          // Establece el valor de la variable
    void setVisible(bool value);        // Establece el valor de la variable
    void setInvulnerable(bool value);   // Establece el valor de la variable
    void setPopping(bool value);        // Establece el valor de la variable
    void setStoppedTimer(Uint16 time);  // Establece el valor de la variable

    auto getCollider() -> Circle &;  // Obtiene el circulo de colisión

   private:
    // Cantidad de elementos del vector con los valores de la deformación del globo al rebotar
    static constexpr int MAX_BOUNCE = 10;

    // Estructura para las variables para el efecto de los rebotes
    struct Bouncing {
        bool enabled;          // Si el efecto está activo
        Uint8 counter;         // Countador para el efecto
        Uint8 speed;           // Velocidad a la que transcurre el efecto
        float zoom_width;      // Zoom aplicado a la anchura
        float zoom_height;     // Zoom aplicado a la altura
        float desp_x;          // Desplazamiento de pixeles en el eje X antes de pintar el objeto con zoom
        float desp_y;          // Desplazamiento de pixeles en el eje Y antes de pintar el objeto con zoom
        std::vector<float> w;  // Vector con los valores de zoom para el ancho del globo
        std::vector<float> h;  // Vector con los valores de zoom para el alto del globo
    };

    // Objetos y punteros
    AnimatedSprite *sprite_;  // Sprite del objeto globo

    // Variables
    float pos_x_;                  // Posición en el eje X
    float pos_y_;                  // Posición en el eje Y
    Uint8 width_;                  // Ancho
    Uint8 height_;                 // Alto
    float vel_x_;                  // Velocidad en el eje X. Cantidad de pixeles a desplazarse
    float vel_y_;                  // Velocidad en el eje Y. Cantidad de pixeles a desplazarse
    float gravity_;                // Aceleración en el eje Y. Modifica la velocidad
    float default_vel_y_;          // Velocidad inicial que tienen al rebotar contra el suelo
    float max_vel_y_;              // Máxima velocidad que puede alcanzar el objeto en el eje Y
    bool being_created_;           // Indica si el globo se está creando
    bool blinking_;                // Indica si el globo está intermitente
    bool enabled_;                 // Indica si el globo esta activo
    bool invulnerable_;            // Indica si el globo es invulnerable
    bool popping_;                 // Indica si el globo está explotando
    bool stopped_;                 // Indica si el globo está parado
    bool visible_;                 // Indica si el globo es visible
    Circle collider_;              // Circulo de colisión del objeto
    Uint16 creation_counter_;      // Temporizador para controlar el estado "creandose"
    Uint16 creation_counter_ini_;  // Valor inicial para el temporizador para controlar el estado "creandose"
    Uint16 score_;                 // Puntos que da el globo al ser destruido
    Uint16 stopped_counter_;       // Contador para controlar el estado "parado"
    Uint8 kind_;                   // Tipo de globo
    Uint8 menace_;                 // Cantidad de amenaza que genera el globo
    Uint32 counter_;               // Contador interno
    float travel_y_;               // Distancia que ha de recorrer el globo en el eje Y antes de que se le aplique la gravedad
    float speed_;                  // Velocidad a la que se mueven los globos
    Uint8 size_;                   // Tamaño del globo
    Uint8 power_;                  // Cantidad de poder que alberga el globo
    Bouncing bouncing_;            // Contiene las variables para el efecto de rebote

    void updateColliders();            // Alinea el circulo de colisión con la posición del objeto globo
    void bounceStart();                // Activa el efecto
    void bounceStop();                 // Detiene el efecto
    void updateBounce();               // Aplica el efecto
    void updateAnimation();            // Establece la animación correspondiente
    void setBeingCreated(bool value);  // Establece el valor de la variable

    void updateState();  // Actualiza los estados del globo

    // Helpers de updateState, uno por cada rama de estado
    void updateStatePopping();
    void updateStateBeingCreated();
    void updateStateStopped();
};
