#pragma once

#include <SDL2/SDL.h>
#include "common/animatedsprite.h"
#include "common/asset.h"
#include "common/debug.h"
#include "common/input.h"
#include "common/resource.h"
#include "common/utils.h"
#include "const.h"
#include "room.h"
#include <string>
#include <vector>

#ifndef PLAYER_H
#define PLAYER_H

enum state_e
{
	s_standing,
	s_jumping,
	s_falling
};

struct playerSpawn_t
{
	float x;
	float y;
	float vx;
	float vy;
	int jumpIni;
	state_e state;
	SDL_RendererFlip flip;
};

struct player_t
{
	playerSpawn_t spawn;
	std::string png;
	std::string animation;
	SDL_Renderer *renderer;
	Resource *resource;
	Asset *asset;
	options_t *options;
	Input *input;
	Room *room;
	Debug *debug;
};

class Player
{
public:
	// Objetos y punteros
	SDL_Renderer *renderer; // El renderizador de la ventana
	Input *input;			// Objeto para gestionar la entrada
	Resource *resource;		// Objeto con los recursos
	Asset *asset;			// Objeto con la ruta a todos los ficheros de recursos
	Room *room;				// Objeto encargado de gestionar cada habitación del juego
	AnimatedSprite *sprite; // Sprite del enemigo
	Debug *debug;			// Objeto para gestionar la información de debug
	options_t *options;		// Puntero a las opciones del juego

	// Variables
	float x;							   // Posición del jugador en el eje X
	float y;							   // Posición del jugador en el eje Y
	float vx;							   // Velocidad/desplazamiento del jugador en el eje X
	float vy;							   // Velocidad/desplazamiento del jugador en el eje Y
	int w;								   // Ancho del jugador
	int h;								   // ALto del jugador
	color_t color;						   // Color del jugador
	SDL_Rect colliderBox;				   // Caja de colisión con los enemigos u objetos
	std::vector<SDL_Point> colliderPoints; // Puntos de colisión con el mapa
	std::vector<SDL_Point> underFeet;	   // Contiene los puntos que hay bajo cada pie del jugador
	std::vector<SDL_Point> feet;		   // Contiene los puntos que hay en el pie del jugador
	state_e state;						   // Estado en el que se encuentra el jugador. Util apara saber si está saltando o cayendo
	state_e prevState;					   // Estado previo en el que se encontraba el jugador
	bool onBorder;						   // Indica si el jugador esta en uno de los cuatro bordes de la pantalla
	int border;							   // Indica en cual de los cuatro bordes se encuentra
	bool autoMovement;					   // Indica si esta siendo arrastrado por una superficie automatica
	bool paused;						   // Indica si el jugador esta en modo pausa
	SDL_Rect lastPosition;				   // Contiene la ultima posición del jugador, por si hay que deshacer algun movimiento
	int jumpIni;						   // Valor del eje Y en el que se inicia el salto
	float maxVY;						   // Velocidad máxima que puede alcanzar al desplazarse en vertical
	std::vector<JA_Sound> jumpSound;	   // Vecor con todos los sonidos del salto
	std::vector<JA_Sound> fallSound;	   // Vecor con todos los sonidos de la caída
	int jumpCounter;					   // Cuenta el tiempo de salto
	int fallCounter;					   // Cuenta el tiempo de caida
	bool alive;							   // Indica si el jugador esta vivo o no
	int maxFallHeight;					   // Altura maxima permitida de caída.
	SDL_Rect r;

	// Comprueba las entradas y modifica variables
	void checkInput();

	// Comprueba si se halla en alguno de los cuatro bordes
	void checkBorders();

	// Comprueba el estado del jugador
	void checkState();

	// Asigna velocidad negativa en el eje Y al jugador
	void jump();

	// Aplica gravedad al jugador
	void applyGravity();

	// Recalcula la posición del jugador y su animación
	void move();

	// Establece la animación del jugador
	void animate();

	// Comprueba si ha finalizado el salto al alcanzar la altura de inicio
	void checkJumpEnd();

	// Calcula y reproduce el sonido de salto
	void playJumpSound();

	// Calcula y reproduce el sonido de caer
	void playFallSound();

	// Comprueba si el jugador tiene suelo debajo de los pies
	bool isOnFloor();

	// Comprueba si el jugador esta sobre una superficie automática
	bool isOnAutoSurface();

	// Comprueba si el jugador está sobre una rampa hacia abajo
	bool isOnDownSlope();

	// Comprueba que el jugador no toque ningun tile de los que matan
	bool checkKillingTiles();

	// Actualiza los puntos de colisión
	void updateColliderPoints();

	// Actualiza los puntos de los pies
	void updateFeet();

	// Cambia el estado del jugador
	void setState(state_e value);

public:
	// Constructor
	Player(player_t player);

	// Destructor
	~Player();

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

	// Actualiza las variables del objeto
	void update();

	// Indica si el jugador esta en uno de los cuatro bordes de la pantalla
	bool getOnBorder();

	// Indica en cual de los cuatro bordes se encuentra
	int getBorder();

	// Cambia al jugador de un borde al opuesto. Util para el cambio de pantalla
	void switchBorders();

	// Obtiene el rectangulo que delimita al jugador
	SDL_Rect getRect();

	// Obtiene el rectangulo de colision del jugador
	SDL_Rect &getCollider();

	// Obtiene algunos parametros del jugador
	playerSpawn_t getSpawnParams();

	// Recarga la textura
	void reLoadTexture();

	// Recarga la paleta
	void reLoadPalette();

	// Establece el valor de la variable
	void setRoom(Room *room);

	// Comprueba si el jugador esta vivo
	bool isAlive();

	// Pone el jugador en modo pausa
	void pause();

	// Quita el modo pausa del jugador
	void resume();
};

#endif
