#pragma once

#include <SDL2/SDL_rect.h>	 // for SDL_Rect
#include <SDL2/SDL_stdinc.h> // for Uint32
#include <memory>			 // for unique_ptr
#include <string>			 // for string, basic_string
#include <vector>			 // for vector
#include "animated_sprite.h" // for AnimatedSprite
#include "enter_name.h"		 // for EnterName
#include "utils.h"			 // for Circle
#include "texture.h"		 // lines 12-12
enum class ScoreboardMode;

// Estados del jugador
enum class PlayerStatus
{
	WALKING_LEFT,
	WALKING_RIGHT,
	WALKING_STOP,

	FIRING_UP,
	FIRING_LEFT,
	FIRING_RIGHT,
	FIRING_NO,

	PLAYING,
	CONTINUE,
	WAITING,
	ENTERING_NAME,
	DYING,
	DIED,
	GAME_OVER,
};

// Variables del jugador
constexpr int PLAYER_INVULNERABLE_COUNTER = 200;
constexpr int PLAYER_POWERUP_COUNTER = 1500;

// Clase Player
class Player
{
private:
	// Objetos y punteros
	std::unique_ptr<AnimatedSprite> playerSprite; // Sprite para dibujar el jugador
	std::unique_ptr<AnimatedSprite> powerSprite;  // Sprite para dibujar el aura del jugador con el poder a tope
	std::unique_ptr<EnterName> enterName;		  // Clase utilizada para introducir el nombre
	SDL_Rect *playArea;							  // Rectangulo con la zona de juego

	// Variables
	int id;						// Numero de identificación para el jugador
	float posX;					// Posicion en el eje X
	int posY;					// Posicion en el eje Y
	float defaultPosX;			// Posición inicial para el jugador
	int defaultPosY;			// Posición inicial para el jugador
	int width;					// Anchura
	int height;					// Altura
	float velX;					// Cantidad de pixeles a desplazarse en el eje X
	int velY;					// Cantidad de pixeles a desplazarse en el eje Y
	float baseSpeed;			// Velocidad base del jugador
	int cooldown;				// Contador durante el cual no puede disparar
	int score;					// Puntos del jugador
	float scoreMultiplier;		// Multiplicador de puntos
	PlayerStatus statusWalking; // Estado del jugador al moverse
	PlayerStatus statusFiring;	// Estado del jugador al disparar
	PlayerStatus statusPlaying; // Estado del jugador en el juego
	bool invulnerable;			// Indica si el jugador es invulnerable
	int invulnerableCounter;	// Contador para la invulnerabilidad
	bool extraHit;				// Indica si el jugador tiene un toque extra
	int coffees;				// Indica cuantos cafes lleva acumulados
	bool powerUp;				// Indica si el jugador tiene activo el modo PowerUp
	int powerUpCounter;			// Temporizador para el modo PowerUp
	int powerUpDespX;			// Desplazamiento del sprite de PowerUp respecto al sprite del jugador
	bool input;					// Indica si puede recibir ordenes de entrada
	Circle collider;			// Circulo de colisión del jugador
	int continueCounter;		// Contador para poder continuar
	Uint32 continueTicks;		// Variable para poder cambiar el contador de continue en función del tiempo
	int scoreBoardPanel;		// Panel del marcador asociado al jugador
	std::string name;			// Nombre del jugador
	std::string recordName;		// Nombre del jugador para l atabla de mejores puntuaciones
	int controllerIndex;		// Indice del array de mandos que utilizará para moverse
	bool demo;					// Para que el jugador sepa si está en el modo demostración

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

	// Monitoriza el estado
	void updateInvulnerable();

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

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

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

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

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

public:
	// Constructor
	Player(int id, float x, int y, bool demo, SDL_Rect *playArea, std::vector<std::shared_ptr<Texture>> texture, 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(std::vector<std::shared_ptr<Texture>> texture);

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

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

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

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

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

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

	// 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 está jugando
	bool isPlaying() const;

	// Indica si el jugador está continuando
	bool isContinue() const;

	// Indica si el jugador está esperando
	bool isWaiting() const;

	// Indica si el jugador está introduciendo su nombre
	bool isEnteringName() const;

	// Indica si el jugador está muriendose
	bool isDying() const;

	// Indica si el jugador ha terminado de morir
	bool hasDied() const;

	// Indica si el jugador ya ha terminado de jugar
	bool isGameOver() const;

	// Establece el estado del jugador en el juego
	void setStatusPlaying(PlayerStatus value);

	// Obtiene el estado del jugador en el juego
	PlayerStatus getStatusPlaying() 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 updatePowerUpCounter();

	// 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();

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

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

	// 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(std::string name);

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

	// 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´´on 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;
};
