#pragma once

#include <SDL2/SDL_rect.h>	 // for SDL_Rect, SDL_Point
#include <SDL2/SDL_render.h> // for SDL_Texture
#include <memory>			 // for shared_ptr
#include <string>			 // for string
#include <vector>			 // for vector
#include "enemy.h"			 // for EnemyData
#include "item.h"			 // for ItemData
#include "options.h"		 // for Options, OptionsVideo, options
#include "utils.h"			 // for stringToColor, Color
class SSprite;				 // lines 17-17
class Surface;				 // lines 18-18
struct JA_Sound_t;			 // lines 19-19
struct ScoreboardData;		 // lines 20-20

enum class TileType
{
	EMPTY,
	WALL,
	PASSABLE,
	SLOPE_L,
	SLOPE_R,
	KILL,
	ANIMATED
};

struct AnimatedTile
{
	std::shared_ptr<SSprite> sprite; // SSprite para dibujar el tile
	int x_orig;						 // Poicion X donde se encuentra el primer tile de la animacion en la tilesheet
};

struct RoomData
{
	std::string number;				// Numero de la habitación
	std::string name;				// Nombre de la habitación
	std::string bg_color;			// Color de fondo de la habitación
	std::string border_color;		// Color del borde de la pantalla
	std::string item_color1;		// Color 1 para los items de la habitación
	std::string item_color2;		// Color 2 para los items de la habitación
	std::string upper_room;			// Identificador de la habitación que se encuentra arriba
	std::string lower_room;			// Identificador de la habitación que se encuentra abajp
	std::string left_room;			// Identificador de la habitación que se encuentra a la izquierda
	std::string right_room;			// Identificador de la habitación que se encuentra a la derecha
	std::string tile_set_file;		// Imagen con los graficos para la habitación
	std::string tile_map_file;		// Fichero con el mapa de indices de tile
	int conveyor_belt_direction;	// Sentido en el que arrastran las superficies automáticas de la habitación
	std::vector<int> tile_map;		// Indice de los tiles a dibujar en la habitación
	std::vector<EnemyData> enemies; // Listado con los enemigos de la habitación
	std::vector<ItemData> items;	// Listado con los items que hay en la habitación
};

// Carga las variables desde un fichero de mapa
RoomData loadRoomFile(const std::string &file_path, bool verbose = false);

// Carga las variables y texturas desde un fichero de mapa de tiles
std::vector<int> loadRoomTileFile(const std::string &file_path, bool verbose = false);

// Asigna variables a una estructura RoomData
bool setRoom(RoomData *room, const std::string &key, const std::string &value);

// Asigna variables a una estructura EnemyData
bool setEnemy(EnemyData *enemy, const std::string &key, const std::string &value);

// Asigna variables a una estructura ItemData
bool setItem(ItemData *item, const std::string &key, const std::string &value);

class Room
{
private:
	// Constantes
	static constexpr int TILE_SIZE_ = 8;   // Ancho del tile en pixels
	static constexpr int MAP_WIDTH_ = 32;  // Ancho del mapa en tiles
	static constexpr int MAP_HEIGHT_ = 16; // Alto del mapa en tiles

	// Objetos y punteros
	std::vector<std::shared_ptr<Enemy>> enemies_; // Listado con los enemigos de la habitación
	std::vector<std::shared_ptr<Item>> items_;	  // Listado con los items que hay en la habitación
	std::shared_ptr<Surface> surface_;			  // Textura con los graficos de la habitación
	std::shared_ptr<Surface> map_surface_;		  // Textura para dibujar el mapa de la habitación
	std::shared_ptr<ScoreboardData> data_;		  // Puntero a los datos del marcador

	// Variables
	std::string number_;							   // Numero de la habitación
	std::string name_;								   // Nombre de la habitación
	std::string bg_color_;							   // Color de fondo de la habitación
	std::string border_color_;						   // Color del borde de la pantalla
	std::string item_color1_;						   // Color 1 para los items de la habitación
	std::string item_color2_;						   // Color 2 para los items de la habitación
	std::string upper_room_;						   // Identificador de la habitación que se encuentra arriba
	std::string lower_room_;						   // Identificador de la habitación que se encuentra abajp
	std::string left_room_;							   // Identificador de la habitación que se encuentra a la izquierda
	std::string right_room_;						   // Identificador de la habitación que se encuentra a la derecha
	std::string tile_set_file_;						   // Imagen con los graficos para la habitación
	std::string tile_map_file_;						   // Fichero con el mapa de indices de tile
	std::vector<int> tile_map_;						   // Indice de los tiles a dibujar en la habitación
	int conveyor_belt_direction_;					   // Sentido en el que arrastran las superficies automáticas de la habitación
	JA_Sound_t *item_sound_;						   // Sonido producido al coger un objeto
	std::vector<LineHorizontal> bottom_floors_;		   // Lista con las superficies inferiores de la habitación
	std::vector<LineHorizontal> top_floors_;		   // Lista con las superficies superiores de la habitación
	std::vector<LineVertical> left_walls_;			   // Lista con las superficies laterales de la parte izquierda de la habitación
	std::vector<LineVertical> right_walls_;			   // Lista con las superficies laterales de la parte derecha de la habitación
	std::vector<LineDiagonal> left_slopes_;			   // Lista con todas las rampas que suben hacia la izquierda
	std::vector<LineDiagonal> right_slopes_;		   // Lista con todas las rampas que suben hacia la derecha
	int counter_;									   // Contador para lo que haga falta
	bool is_paused_;								   // Indica si el mapa esta en modo pausa
	std::vector<AnimatedTile> animated_tiles_;		   // Vector con los indices de tiles animados
	std::vector<LineHorizontal> conveyor_belt_floors_; // Lista con las superficies automaticas de la habitación
	int tile_set_width_;							   // Ancho del tileset en tiles

	void initializeRoom(const RoomData &room);

	// Pinta el mapa de la habitación en la textura
	void fillMapTexture();

	// Calcula las superficies inferiores
	void setBottomSurfaces();

	// Calcula las superficies superiores
	void setTopSurfaces();

	// Calcula las superficies laterales izquierdas
	void setLeftSurfaces();

	// Calcula las superficies laterales derechas
	void setRightSurfaces();

	// Encuentra todas las rampas que suben hacia la izquierda
	void setLeftSlopes();

	// Encuentra todas las rampas que suben hacia la derecha
	void setRightSlopes();

	// Calcula las superficies automaticas
	void setAutoSurfaces();

	// Localiza todos los tiles animados de la habitación
	void setAnimatedTiles();

	// Actualiza los tiles animados
	void updateAnimatedTiles();

	// Pinta los tiles animados en pantalla
	void renderAnimatedTiles();

	// Devuelve el tipo de tile que hay en ese indice
	TileType getTile(int index);

	// Abre la jail para poder entrar
	void openTheJail();

	// Inicializa las superficies de colision
	void initRoomSurfaces();

public:
	// Constructor
	Room(const std::string &room_path, std::shared_ptr<ScoreboardData> data);

	// Destructor
	~Room() = default;

	// Devuelve el nombre de la habitación
	std::string getName() const { return name_; }

	// Devuelve el color de la habitación
	Uint8 getBGColor() const { return stringToColor(bg_color_); }

	// Devuelve el color del borde
	Uint8 getBorderColor() const { return stringToColor(border_color_); }

	// Dibuja el mapa en pantalla
	void renderMap();

	// Dibuja los enemigos en pantalla
	void renderEnemies();

	// Dibuja los objetos en pantalla
	void renderItems();

	// Actualiza las variables y objetos de la habitación
	void update();

	// Devuelve la cadena del fichero de la habitación contigua segun el borde
	std::string getRoom(int border);

	// Devuelve el tipo de tile que hay en ese pixel
	TileType getTile(SDL_Point point);

	// Indica si hay colision con un enemigo a partir de un rectangulo
	bool enemyCollision(SDL_Rect &rect);

	// Indica si hay colision con un objeto a partir de un rectangulo
	bool itemCollision(SDL_Rect &rect);

	// Obten el tamaño del tile
	int getTileSize() const { return TILE_SIZE_; }

	// Obten la coordenada de la cuesta a partir de un punto perteneciente a ese tile
	int getSlopeHeight(SDL_Point p, TileType slope);

	// Comprueba las colisiones
	int checkRightSurfaces(SDL_Rect *rect);

	// Comprueba las colisiones
	int checkLeftSurfaces(SDL_Rect *rect);

	// Comprueba las colisiones
	int checkTopSurfaces(SDL_Rect *rect);

	// Comprueba las colisiones
	int checkBottomSurfaces(SDL_Rect *rect);

	// Comprueba las colisiones
	int checkAutoSurfaces(SDL_Rect *rect);

	// Comprueba las colisiones
	bool checkTopSurfaces(SDL_Point *p);

	// Comprueba las colisiones
	bool checkAutoSurfaces(SDL_Point *p);

	// Comprueba las colisiones
	int checkLeftSlopes(const LineVertical *line);

	// Comprueba las colisiones
	bool checkLeftSlopes(SDL_Point *p);

	// Comprueba las colisiones
	int checkRightSlopes(const LineVertical *line);

	// Comprueba las colisiones
	bool checkRightSlopes(SDL_Point *p);

	// Pone el mapa en modo pausa
	void setPaused(bool value) { is_paused_ = value; };

	// Obten la direccion de las superficies automaticas
	int getAutoSurfaceDirection() const { return conveyor_belt_direction_; }
};