#pragma once

#include <SDL3/SDL_rect.h>        // Para SDL_FRect
#include <SDL3/SDL_stdinc.h>      // Para Uint32
#include <memory>                 // Para unique_ptr, shared_ptr
#include <string>                 // Para string
#include <vector>                 // Para vector
#include "animated_sprite.h"      // Para AnimatedSprite
#include "enter_name.h"           // Para EnterName
#include "manage_hiscore_table.h" // Para HiScoreEntry
#include "options.h"              // Para Options, OptionsGame, options
#include "utils.h"                // Para Circle

class Texture;
enum class InputAction : int;
enum class ScoreboardMode;

// --- Estados del jugador ---
enum class PlayerState
{
    WALKING_LEFT,
    WALKING_RIGHT,
    WALKING_STOP,

    FIRING_UP,
    FIRING_LEFT,
    FIRING_RIGHT,
    FIRING_NONE,

    COOLING_UP,
    COOLING_LEFT,
    COOLING_RIGHT,

    PLAYING,                       // Está jugando
    CONTINUE,                      // Cuenta atrás para continuar
    WAITING,                       // No está jugando pero puede entrar a jugar
    ENTERING_NAME,                 // Introduciendo nombre
    SHOWING_NAME,                  // Mostrando el nombre introducido
    DYING,                         // El cadáver está volando por ahí
    DIED,                          // El cadáver ha desaparecido por el fondo
    GAME_OVER,                     // No está jugando y no puede entrar a jugar
    CELEBRATING,                   // Poniendo pose de victoria
    ENTERING_NAME_GAME_COMPLETED,  // Poniendo nombre en el tramo final del juego
    LEAVING_SCREEN,                // Moviéndose fuera de la pantalla
    ENTERING_SCREEN,               // Entrando a la pantalla
    CREDITS,                       // Estado para los créditos del juego
};

// --- Clase Player ---
class Player
{
public:
    // --- Constructor y destructor ---
    Player(int id, float x, int y, bool demo, SDL_FRect &play_area, std::vector<std::shared_ptr<Texture>> texture, const std::vector<std::vector<std::string>> &animations);
    ~Player() = default;

    // --- Inicialización y ciclo de vida ---
    void init();        // Inicializa el jugador
    void update();      // Actualiza estado, animación y contadores
    void render();      // Dibuja el jugador en pantalla

    // --- Entrada y control ---
    void setInput(InputAction input);                // Procesa entrada general
    void setInputPlaying(InputAction input);         // Procesa entrada en modo jugando
    void setInputEnteringName(InputAction input);    // Procesa entrada al introducir nombre

    // --- Movimiento y animación ---
    void move();             // Mueve el jugador
    void setAnimation();     // Establece la animación según el estado

    // --- Texturas y animaciones ---
    void setPlayerTextures(const std::vector<std::shared_ptr<Texture>> &texture); // Cambia las texturas del jugador

    // --- Estados y contadores ---
    void updateCooldown();           // Actualiza el cooldown de disparo

    // --- Puntuación y marcador ---
    void addScore(int score);                // Añade puntos
    void incScoreMultiplier();               // Incrementa el multiplicador
    void decScoreMultiplier();               // Decrementa el multiplicador

    // --- Estados de juego ---
    void setPlayingState(PlayerState state);     // Cambia el estado de juego
    void setInvulnerable(bool value);           // Establece el valor del estado de invulnerabilidad
    void setPowerUp();                          // Activa el modo PowerUp
    void updatePowerUp();                       // Actualiza el valor de PowerUp
    void giveExtraHit();                        // Concede un toque extra al jugador
    void removeExtraHit();                      // Quita el toque extra al jugador
    void decContinueCounter();                  // Decrementa el contador de continuar

    // --- Getters y comprobaciones de estado ---
    int getRecordNamePos() const; // Obtiene la posición que se está editando del nombre del jugador para la tabla de mejores puntuaciones

    // Comprobación de playing_state
    bool hasDied() const { return playing_state_ == PlayerState::DIED; }
    bool isCelebrating() const { return playing_state_ == PlayerState::CELEBRATING; }
    bool isContinue() const { return playing_state_ == PlayerState::CONTINUE; }
    bool isDying() const { return playing_state_ == PlayerState::DYING; }
    bool isEnteringName() const { return playing_state_ == PlayerState::ENTERING_NAME; }
    bool isShowingName() const { return playing_state_ == PlayerState::SHOWING_NAME; }
    bool isEnteringNameGameCompleted() const { return playing_state_ == PlayerState::ENTERING_NAME_GAME_COMPLETED; }
    bool isLeavingScreen() const { return playing_state_ == PlayerState::LEAVING_SCREEN; }
    bool isGameOver() const { return playing_state_ == PlayerState::GAME_OVER; }
    bool isPlaying() const { return playing_state_ == PlayerState::PLAYING; }
    bool isWaiting() const { return playing_state_ == PlayerState::WAITING; }

    // Getters
    bool canFire() const { return cool_down_ <= 0; }
    bool hasExtraHit() const { return extra_hit_; }
    bool isCooling() const { return firing_state_ == PlayerState::COOLING_LEFT || firing_state_ == PlayerState::COOLING_UP || firing_state_ == PlayerState::COOLING_RIGHT; }
    bool IsEligibleForHighScore() const { return score_ > options.game.hi_score_table.back().score; }
    bool isInvulnerable() const { return invulnerable_; }
    bool isPowerUp() const { return power_up_; }
    Circle &getCollider() { return collider_; }
    float getScoreMultiplier() const { return score_multiplier_; }
    int getCoffees() const { return coffees_; }
    int getContinueCounter() const { return continue_counter_; }
    int getController() const { return controller_index_; }
    int getHeight() const { return HEIGHT_; }
    int getId() const { return id_; }
    int getInvulnerableCounter() const { return invulnerable_counter_; }
    int getPosX() const { return static_cast<int>(pos_x_); }
    int getPosY() const { return pos_y_; }
    int getPowerUpCounter() const { return power_up_counter_; }
    std::string getRecordName() const { return enter_name_ ? enter_name_->getFinalName() : ""; }
    int getScore() const { return score_; }
    int getScoreBoardPanel() const { return scoreboard_panel_; }
    int getWidth() const { return WIDTH_; }
    PlayerState getPlayingState() const { return playing_state_; }
    const std::string &getName() const { return name_; }
    bool get1CC() const { return game_completed_ && credits_used_ == 1; }
    bool getEnterNamePositionOverflow() const { return enter_name_ ? enter_name_->getPositionOverflow() : false; }

    // Setters inline
    void setController(int index) { controller_index_ = index; }
    void setFireCooldown(int time) { cool_down_ = time; }
    void setFiringState(PlayerState state) { firing_state_ = state; }
    void setInvulnerableCounter(int value) { invulnerable_counter_ = value; }
    void setName(const std::string &name) { name_ = name; }
    void setPowerUpCounter(int value) { power_up_counter_ = value; }
    void setScore(int score) { score_ = score; }
    void setScoreBoardPanel(int panel) { scoreboard_panel_ = panel; }
    void setScoreMultiplier(float value) { score_multiplier_ = value; }
    void setWalkingState(PlayerState state) { walking_state_ = state; }
    void addCredit() { ++credits_used_; }

private:
    // --- Constantes ---
    static constexpr int POWERUP_COUNTER_ = 1500;      // Duración del estado PowerUp
    static constexpr int INVULNERABLE_COUNTER_ = 200;  // Duración del estado invulnerable
    static constexpr int WIDTH_ = 30;                  // Anchura
    static constexpr int HEIGHT_ = 30;                 // Altura
    static constexpr float BASE_SPEED_ = 1.5f;         // Velocidad base del jugador

    // --- Objetos y punteros ---
    std::unique_ptr<AnimatedSprite> player_sprite_; // Sprite para dibujar el jugador
    std::unique_ptr<AnimatedSprite> power_sprite_;  // Sprite para dibujar el aura del jugador con el poder a tope
    std::unique_ptr<EnterName> enter_name_;         // Clase utilizada para introducir el nombre

    // --- Variables de estado ---
    int id_;                        // Número de identificación para el jugador. Player1 = 1, Player2 = 2
    SDL_FRect play_area_;           // Rectángulo con la zona de juego
    float pos_x_ = 0.0f;            // Posición en el eje X
    int pos_y_ = 0;                 // Posición en el eje Y
    float default_pos_x_;           // Posición inicial para el jugador
    int default_pos_y_;             // Posición inicial para el jugador
    float vel_x_ = 0.0f;            // Cantidad de píxeles a desplazarse en el eje X
    int vel_y_ = 0.0f;              // Cantidad de píxeles a desplazarse en el eje Y
    int cool_down_ = 0;             // Contador durante el cual no puede disparar
    int cooling_state_counter_ = 0; // Contador para la animación del estado cooling
    int score_ = 0;                 // Puntos del jugador
    float score_multiplier_ = 1.0f; // Multiplicador de puntos
    PlayerState walking_state_ = PlayerState::WALKING_STOP; // Estado del jugador al moverse
    PlayerState firing_state_ = PlayerState::FIRING_NONE;   // Estado del jugador al disparar
    PlayerState playing_state_ = PlayerState::WAITING;      // Estado del jugador en el juego
    bool invulnerable_ = true;          // Indica si el jugador es invulnerable
    int invulnerable_counter_ = INVULNERABLE_COUNTER_; // Contador para la invulnerabilidad
    bool extra_hit_ = false;             // Indica si el jugador tiene un toque extra
    int coffees_ = 0;                    // Indica cuántos cafés lleva acumulados
    bool power_up_ = false;              // Indica si el jugador tiene activo el modo PowerUp
    int power_up_counter_ = POWERUP_COUNTER_; // Temporizador para el modo PowerUp
    int power_up_desp_x_ = 0;            // Desplazamiento del sprite de PowerUp respecto al sprite del jugador
    Circle collider_ = Circle(0, 0, 9);  // Círculo de colisión del jugador
    int continue_counter_ = 10;           // Contador para poder continuar
    Uint32 continue_ticks_ = 0;           // Variable para poder cambiar el contador de continue en función del tiempo
    int scoreboard_panel_ = 0;            // Panel del marcador asociado al jugador
    std::string name_;                    // Nombre del jugador
    int controller_index_ = 0;            // Índice del array de mandos que utilizará para moverse
    bool demo_;                           // Para que el jugador sepa si está en el modo demostración
    int enter_name_counter_;              // Contador para poner nombre
    Uint32 enter_name_ticks_ = 0;         // Variable para poder cambiar el contador de poner nombre en función del tiempo
    Uint32 showing_name_ticks_ = 0;       // Tiempo en el que se entra al estado SHOWING_NAME
    int step_counter_ = 0;                // Cuenta los pasos para los estados en los que camina automáticamente
    bool game_completed_ = false;         // Indica si ha completado el juego
    int credits_used_ = 1;                // Indica el número de veces que ha continuado
    std::string last_enter_name_;         // Último nombre introducido en la tabla de puntuaciones

    // --- Métodos internos ---
    void shiftColliders();        // Actualiza el círculo de colisión a la posición del jugador
    void shiftSprite();           // Recoloca el sprite
    void updateInvulnerable();    // Monitoriza el estado de invulnerabilidad
    void updateContinueCounter(); // Actualiza el contador de continue
    void updateEnterNameCounter();// Actualiza el contador de entrar nombre
    void updateShowingName();     // Actualiza el estado SHOWING_NAME
    void decEnterNameCounter();   // Decrementa el contador de entrar nombre
    void updateScoreboard();      // Actualiza el panel del marcador
    void setScoreboardMode(ScoreboardMode mode); // Cambia el modo del marcador
    void playRandomBubbleSound(); // Hace sonar un sonido aleatorio
	bool isRenderable() const { return !isWaiting() && !isGameOver(); }
};