#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 Player
class Player {
   public:
    static constexpr int DEATH_COUNTER = 350;  // Frames de la animación de muerte

    Player(float x, int y, SDL_Renderer *renderer, const std::vector<Texture *> &texture, const std::vector<std::vector<std::string> *> &animations);  // Constructor
    ~Player();                                                                                                                                         // Destructor

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

    void init();    // Iniciador
    void update();  // Actualiza al jugador a su posicion, animación y controla los contadores
    void render();  // Pinta el jugador en pantalla
    void move();    // Mueve el jugador a la posición y animación que le corresponde

    void setPlayerTextures(const std::vector<Texture *> &texture);  // Pone las texturas del jugador
    void setInput(Uint8 input);                                     // Actua en consecuencia de la entrada recibida
    void setAnimation();                                            // Establece la animación correspondiente al estado

    [[nodiscard]] auto getPosX() const -> int;    // Obtiene el valor de la variable
    [[nodiscard]] auto getPosY() const -> int;    // Obtiene el valor de la variable
    [[nodiscard]] auto getWidth() const -> int;   // Obtiene el valor de la variable
    [[nodiscard]] auto getHeight() const -> int;  // Obtiene el valor de la variable

    [[nodiscard]] auto canFire() const -> bool;  // Indica si el jugador puede disparar
    void setFireCooldown(int time);              // Establece el valor de la variable
    void updateCooldown();                       // Actualiza el valor de la variable

    [[nodiscard]] auto getScore() const -> Uint32;  // Obtiene la puntuación del jugador
    void setScore(Uint32 score);                    // Asigna un valor a la puntuación del jugador
    void addScore(Uint32 score);                    // Incrementa la puntuación del jugador

    [[nodiscard]] auto isAlive() const -> bool;  // Obtiene el valor de la variable
    void setAlive(bool value);                   // Establece el valor de la variable

    [[nodiscard]] auto getScoreMultiplier() const -> float;  // Obtiene el valor de la variable
    void setScoreMultiplier(float value);                    // Establece el valor de la variable
    void incScoreMultiplier();                               // Aumenta el valor de la variable hasta un máximo
    void decScoreMultiplier();                               // Decrementa el valor de la variable hasta un mínimo

    [[nodiscard]] auto isInvulnerable() const -> bool;            // Obtiene el valor de la variable
    void setInvulnerable(bool value);                             // Establece el valor de la variable
    [[nodiscard]] auto getInvulnerableCounter() const -> Uint16;  // Obtiene el valor de la variable
    void setInvulnerableCounter(Uint16 value);                    // Establece el valor de la variable

    [[nodiscard]] auto isPowerUp() const -> bool;            // Obtiene el valor de la variable
    void setPowerUp(bool value);                             // Establece el valor de la variable
    [[nodiscard]] auto getPowerUpCounter() const -> Uint16;  // Obtiene el valor de la variable
    void setPowerUpCounter(Uint16 value);                    // Establece el valor de la variable
    void updatePowerUpCounter();                             // Actualiza el valor de la variable

    [[nodiscard]] auto hasExtraHit() const -> bool;  // Obtiene el valor de la variable
    void giveExtraHit();                             // Concede un toque extra al jugador
    void removeExtraHit();                           // Quita el toque extra al jugador

    void enableInput();   // Habilita la entrada de ordenes
    void disableInput();  // Deshabilita la entrada de ordenes

    [[nodiscard]] auto getCoffees() const -> Uint8;        // Devuelve el numero de cafes actuales
    auto getCollider() -> Circle &;                        // Obtiene el circulo de colisión
    auto getDeadTexture() -> Texture *;                    // Obtiene el puntero a la textura con los gráficos de la animación de morir
    [[nodiscard]] auto getDeathCounter() const -> Uint16;  // Obtiene el valor de la variable

   private:
    // Estados del jugador
    static constexpr int STATUS_WALKING_LEFT = 0;
    static constexpr int STATUS_WALKING_RIGHT = 1;
    static constexpr int STATUS_WALKING_STOP = 2;

    static constexpr int STATUS_FIRING_UP = 0;
    static constexpr int STATUS_FIRING_LEFT = 1;
    static constexpr int STATUS_FIRING_RIGHT = 2;
    static constexpr int STATUS_FIRING_NO = 3;

    // Variables del jugador
    static constexpr int INVULNERABLE_COUNTER = 200;
    static constexpr int POWERUP_COUNTER = 1500;

    // Objetos y punteros
    SDL_Renderer *renderer_;        // El renderizador de la ventana
    AnimatedSprite *head_sprite_;   // Sprite para dibujar la cabeza
    AnimatedSprite *body_sprite_;   // Sprite para dibujar el cuerpo
    AnimatedSprite *legs_sprite_;   // Sprite para dibujar las piernas
    AnimatedSprite *death_sprite_;  // Sprite para dibujar el jugador derrotado
    AnimatedSprite *fire_sprite_;   // Sprite para dibujar el aura del jugador con el poder a tope

    // Variables
    float pos_x_;  // Posicion en el eje X
    int pos_y_;    // Posicion en el eje Y

    Uint8 width_;   // Anchura
    Uint8 height_;  // Altura

    float vel_x_;  // Cantidad de pixeles a desplazarse en el eje X
    int vel_y_;    // Cantidad de pixeles a desplazarse en el eje Y

    float base_speed_;  // Velocidad base del jugador
    int cooldown_;      // Contador durante el cual no puede disparar

    Uint32 score_;            // Puntos del jugador
    float score_multiplier_;  // Multiplicador de puntos

    Uint8 status_walking_;  // Estado del jugador
    Uint8 status_firing_;   // Estado del jugador

    bool alive_;                   // Indica si el jugador está vivo
    Uint16 death_counter_;         // Contador para la animación de morirse
    bool invulnerable_;            // Indica si el jugador es invulnerable
    Uint16 invulnerable_counter_;  // Contador para la invulnerabilidad
    bool extra_hit_;               // Indica si el jugador tiene un toque extra
    Uint8 coffees_;                // Indica cuantos cafes lleva acumulados
    bool power_up_;                // Indica si el jugador tiene activo el modo PowerUp
    Uint16 power_up_counter_;      // Temporizador para el modo PowerUp
    bool input_;                   // Indica si puede recibir ordenes de entrada
    Circle collider_;              // Circulo de colisión del jugador

    void setWalkingStatus(Uint8 status);  // Establece el estado del jugador
    void setFiringStatus(Uint8 status);   // Establece el estado del jugador

    void shiftColliders();             // Actualiza el circulo de colisión a la posición del jugador
    void updateInvulnerableCounter();  // Actualiza el valor de la variable
    void updateDeathCounter();         // Actualiza el valor de la variable
    void updatePowerUpHeadOffset();    // Actualiza el valor de la variable
};
