#pragma once

#include "animated_sprite.h" // Para AnimatedSprite
#include "enter_name.h"		 // Para EnterName
#include "utils.h"			 // Para Circle
#include <SDL2/SDL_rect.h>	 // Para SDL_Rect
#include <SDL2/SDL_stdinc.h> // Para Uint32
#include <memory>			 // Para unique_ptr, shared_ptr
#include <string>			 // Para string
#include <vector>			 // Para vector

class Texture;				// lines 12-12
enum class InputType : int; // lines 13-13
enum class ScoreboardMode;	// lines 14-14

// 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,					  // Está con la cuenta atras para continuar
	WAITING,					  // No está jugando pero puede entrar a jugar
	ENTERING_NAME,				  // Introduciendo nombre
	DYING,						  // El cadaver está volando por ahi
	DIED,						  // El cadaver 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
	GAME_COMPLETED,				  // Moviendose fuera de la pantalla
};

// Clase Player
class Player
{
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
	int id_;												// Numero de identificación para el jugador. Player1 = 1, Player2 = 2
	SDL_Rect play_area_;									// Rectangulo con la zona de juego
	float pos_x_ = 0.0f;									// Posicion en el eje X
	int pos_y_ = 0;											// Posicion 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 pixeles a desplazarse en el eje X
	int vel_y_ = 0.0f;										// Cantidad de pixeles a desplazarse en el eje Y
	int cooldown_ = 0;										// Contador durante el cual no puede disparar
	int cooling_status_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 cuantos cafes 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);						// Circulo 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
	std::string record_name_;								// Nombre del jugador para la tabla de mejores puntuaciones
	int controller_index_ = 0;								// Indice 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

	// Actualiza el circulo de colisión a la posición del jugador
	void shiftColliders();

	// Recoloca el sprite
	void shiftSprite();

	// Monitoriza el estado
	void updateInvulnerable();

	// Actualiza el contador de continue
	void updateContinueCounter();

	// Actualiza el contador de entrar nombre
	void updateEnterNameCounter();

	// Decrementa el contador de entrar nombre
	void decEnterNameCounter();

	// Indica si el jugador se puede dibujar
	bool isRenderable() const;

	// Actualiza el panel del marcador
	void updateScoreboard();

	// Cambia el modo del marcador
	void setScoreboardMode(ScoreboardMode mode);

	// Se encuentra en alguno de los estados "COOLING"
	bool isCooling();

public:
	// Constructor
	Player(int id, float x, int y, bool demo, SDL_Rect &play_area, std::vector<std::shared_ptr<Texture>> texture, const std::vector<std::vector<std::string>> &animations);

	// Destructor
	~Player() = default;

	// Iniciador
	void init();

	// Actualiza al jugador a su posicion, animación y controla los contadores
	void update();

	// Pinta el jugador en pantalla
	void render();

	// Pone las texturas del jugador
	void setPlayerTextures(const std::vector<std::shared_ptr<Texture>> &texture);

	// Actua en consecuencia de la entrada recibida
	void setInput(InputType input);

	// Procesa inputs para cuando está jugando
	void setInputPlaying(InputType input);

	// Procesa inputs para cuando está introduciendo el nombre
	void setInputEnteringName(InputType input);

	// Mueve el jugador a la posición y animación que le corresponde
	void move();

	// Establece el estado del jugador
	void setWalkingState(PlayerState state);

	// Establece el estado del jugador
	void setFiringState(PlayerState state);

	// Establece la animación correspondiente al estado
	void setAnimation();

	// Obtiene el valor de la variable
	int getPosX() const;

	// Obtiene el valor de la variable
	int getPosY() const;

	// Obtiene el valor de la variable
	int getWidth() const;

	// Obtiene el valor de la variable
	int getHeight() const;

	// Indica si el jugador puede disparar
	bool canFire() const;

	// Establece el valor de la variable
	void setFireCooldown(int time);

	// Actualiza el valor de la variable
	void updateCooldown();

	// Obtiene la puntuación del jugador
	int getScore() const;

	// Asigna un valor a la puntuación del jugador
	void setScore(int score);

	// Incrementa la puntuación del jugador
	void addScore(int score);

	// Indica si el jugador se encuentra en ese estado
	bool isPlaying() const;
	bool isContinue() const;
	bool isWaiting() const;
	bool isEnteringName() const;
	bool isDying() const;
	bool hasDied() const;
	bool isGameOver() const;
	bool isEnteringNameGameCompleted() const;
	bool isGameCompleted() const;
	bool isCelebrating() const;

	// Establece el estado del jugador en el juego
	void setPlayingState(PlayerState state);

	// Obtiene el estado del jugador en el juego
	PlayerState getPlayingState() const;

	// Obtiene el valor de la variable
	float getScoreMultiplier() const;

	// Establece el valor de la variable
	void setScoreMultiplier(float value);

	// Aumenta el valor de la variable hasta un máximo
	void incScoreMultiplier();

	// Decrementa el valor de la variable hasta un mínimo
	void decScoreMultiplier();

	// Obtiene el valor de la variable
	bool isInvulnerable() const;

	// Establece el valor del estado
	void setInvulnerable(bool value);

	// Obtiene el valor de la variable
	int getInvulnerableCounter() const;

	// Establece el valor de la variable
	void setInvulnerableCounter(int value);

	// Obtiene el valor de la variable
	bool isPowerUp() const;

	// Establece el valor de la variable a verdadero
	void setPowerUp();

	// Obtiene el valor de la variable
	int getPowerUpCounter() const;

	// Establece el valor de la variable
	void setPowerUpCounter(int value);

	// Actualiza el valor de la variable
	void updatePowerUp();

	// Obtiene el valor de la variable
	bool hasExtraHit() const;

	// Concede un toque extra al jugador
	void giveExtraHit();

	// Quita el toque extra al jugador
	void removeExtraHit();

	// Devuelve el número de cafes actuales
	int getCoffees() const;

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

	// Obtiene el valor de la variable
	int getContinueCounter() const;

	// Le asigna un panel en el marcador al jugador
	void setScoreBoardPanel(int panel);

	// Obtiene el valor de la variable
	int getScoreBoardPanel() const;

	// Decrementa el contador de continuar
	void decContinueCounter();

	// Establece el nombre del jugador
	void setName(const std::string &name);

	// Establece el nombre del jugador para la tabla de mejores puntuaciones
	void setRecordName(const std::string &record_name);

	// Obtiene el nombre del jugador
	std::string getName() const;

	// Obtiene el nombre del jugador para la tabla de mejores puntuaciones
	std::string getRecordName() const;

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

	// Establece el mando que usará para ser controlado
	void setController(int index);

	// Obtiene el mando que usa para ser controlado
	int getController() const;

	// Obtiene el "id" del jugador
	int getId() const;

	// Comprueba si la puntuación entra en la tabla de mejores puntuaciones
	bool IsEligibleForHighScore();
};
