#pragma once

#include <SDL3/SDL.h>

#include <memory>  // Para shared_ptr
#include <string>  // Para string
#include <vector>  // Para vector

#include "game/entities/enemy.hpp"  // Para EnemyData
#include "game/entities/item.hpp"   // Para ItemData
#include "utils/utils.hpp"          // Para LineHorizontal, LineDiagonal, LineVertical
class SurfaceSprite;                // lines 12-12
class Surface;                      // lines 13-13
struct ScoreboardData;              // lines 15-15

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

enum class RoomBorder : int {
    TOP = 0,
    RIGHT = 1,
    BOTTOM = 2,
    LEFT = 3
};

struct AnimatedTile {
        std::shared_ptr<SurfaceSprite> 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
auto loadRoomFile(const std::string& file_path, bool verbose = false) -> RoomData;

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

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

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

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

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

        // Helper para recopilar tiles inferiores
        auto collectBottomTiles() -> std::vector<int>;

        // Helper para recopilar tiles superiores
        auto collectTopTiles() -> std::vector<int>;

        // Helper para recopilar tiles animados (para superficies automaticas)
        auto collectAnimatedTiles() -> std::vector<int>;

        // Helper para construir lineas horizontales a partir de tiles consecutivos
        static void buildHorizontalLines(const std::vector<int>& tiles, std::vector<LineHorizontal>& lines, bool is_bottom_surface);

        // 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
        auto getTile(int index) -> TileType;

        // 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
        [[nodiscard]] auto getName() const -> const std::string& { return name_; }

        // Devuelve el color de la habitación
        [[nodiscard]] auto getBGColor() const -> Uint8 { return stringToColor(bg_color_); }

        // Devuelve el color del borde
        [[nodiscard]] auto getBorderColor() const -> Uint8 { 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
        auto getRoom(RoomBorder border) -> std::string;

        // Devuelve el tipo de tile que hay en ese pixel
        auto getTile(SDL_FPoint point) -> TileType;

        // Indica si hay colision con un enemigo a partir de un rectangulo
        auto enemyCollision(SDL_FRect& rect) -> bool;

        // Indica si hay colision con un objeto a partir de un rectangulo
        auto itemCollision(SDL_FRect& rect) -> bool;

        // Obten el tamaño del tile
        static auto getTileSize() -> int { return TILE_SIZE; }

        // Obten la coordenada de la cuesta a partir de un punto perteneciente a ese tile
        static auto getSlopeHeight(SDL_FPoint p, TileType slope) -> int;

        // Comprueba las colisiones
        auto checkRightSurfaces(SDL_FRect* rect) -> int;

        // Comprueba las colisiones
        auto checkLeftSurfaces(SDL_FRect* rect) -> int;

        // Comprueba las colisiones
        auto checkTopSurfaces(SDL_FRect* rect) -> int;

        // Comprueba las colisiones
        auto checkBottomSurfaces(SDL_FRect* rect) -> int;

        // Comprueba las colisiones
        auto checkAutoSurfaces(SDL_FRect* rect) -> int;

        // Comprueba las colisiones
        auto checkTopSurfaces(SDL_FPoint* p) -> bool;

        // Comprueba las colisiones
        auto checkAutoSurfaces(SDL_FPoint* p) -> bool;

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

        // Comprueba las colisiones
        auto checkLeftSlopes(SDL_FPoint* p) -> bool;

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

        // Comprueba las colisiones
        auto checkRightSlopes(SDL_FPoint* p) -> bool;

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

        // Obten la direccion de las superficies automaticas
        [[nodiscard]] auto getAutoSurfaceDirection() const -> int { return conveyor_belt_direction_; }
};