#include "room.h"
#include <iostream>
#include <fstream>
#include <sstream>

// Constructor
Room::Room(std::string file, SDL_Renderer *renderer, Screen *screen, Asset *asset, ItemTracker *itemTracker, int *items, Debug *debug)
{
    // Inicializa variables
    tileSize = 8;
    mapWidth = 32;
    mapHeight = 16;
    paused = false;
    itemColor1 = stringToColor("magenta");
    itemColor2 = stringToColor("yellow");
    counter = 0;

    // Copia los punteros a objetos
    this->renderer = renderer;
    this->asset = asset;
    this->screen = screen;
    this->itemTracker = itemTracker;
    this->itemsPicked = items;
    this->debug = debug;

    // Crea los objetos
    loadMapFile(file);
    texture = new LTexture(renderer, asset->get(tileset));
    tilesetWidth = texture->getWidth() / tileSize;

    itemSound = JA_LoadSound(asset->get("item.wav").c_str());
    loadMapTileFile(asset->get(tileMapFile));

    // Calcula las superficies
    setBottomSurfaces();
    setTopSurfaces();
    setLeftSurfaces();
    setRightSurfaces();
    setLeftSlopes();
    setRightSlopes();

    // Busca los tiles animados
    setAnimatedTiles();

    // Crea la textura para el mapa de tiles de la habitación
    mapTexture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, GAMECANVAS_WIDTH, GAMECANVAS_HEIGHT);
    if (mapTexture == nullptr)
    {
        printf("Error: mapTexture could not be created!\nSDL Error: %s\n", SDL_GetError());
    }
    SDL_SetTextureBlendMode(mapTexture, SDL_BLENDMODE_BLEND);

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

    // Establece el color del borde
    screen->setBorderColor(borderColor);
}

// Destructor
Room::~Room()
{
    // Reclama la memoria utilizada por los objetos
    delete texture;
    JA_DeleteSound(itemSound);
    SDL_DestroyTexture(mapTexture);

    for (auto enemy : enemies)
    {
        delete enemy;
    }

    for (auto item : items)
    {
        delete item;
    }

    for (auto a: animatedTiles)
    {
        delete a;
    }
}

// Carga las variables desde un fichero de mapa
bool Room::loadMapFile(std::string file_path)
{
    const std::string filename = file_path.substr(file_path.find_last_of("\\/") + 1);
    std::string line;
    std::ifstream file(file_path);

    // El fichero se puede abrir
    if (file.good())
    {
        // Procesa el fichero linea a linea
        printf("Reading file %s\n\n", filename.c_str());
        while (std::getline(file, line))
        {
            // Si la linea contiene el texto [enemy] se realiza el proceso de carga de un enemigo
            if (line == "[enemy]")
            {
                enemy_t enemy;
                enemy.asset = asset;
                enemy.renderer = renderer;
                enemy.flip = false;

                do
                {
                    std::getline(file, line);

                    // Encuentra la posición del caracter '='
                    int pos = line.find("=");
                    // Procesa las dos subcadenas
                    if (!setEnemy(&enemy, line.substr(0, pos), line.substr(pos + 1, line.length())))
                    {
                        printf("Warning: file %s\n, unknown parameter \"%s\"\n", filename.c_str(), line.substr(0, pos).c_str());
                    }
                } while (line != "[/enemy]");

                // Añade el enemigo al vector de enemigos
                enemies.push_back(new Enemy(enemy));
            }

            // Si la linea contiene el texto [tilemap] se realiza el proceso de carga del fichero tmx
            /*else if (line == "[tilemap]")
            {
                do
                {
                    std::getline(file, line);
                    if (line.find(".tmx") != std::string::npos)
                    {
                        std::string filename2 = line;
                        std::ifstream file2(asset->get(filename2)); // Abre el fichero tmx
                        if (file2.good())
                        {
                            bool data_read = false;
                            while (std::getline(file2, line)) // Lee el fichero linea a linea
                            {
                                if (!data_read)
                                { // Lee lineas hasta que encuentre donde empiezan los datos del mapa
                                    int pos = 0;
                                    do
                                    {
                                        std::getline(file2, line);
                                        pos = line.find("data encoding");
                                    } while (pos == std::string::npos);

                                    do
                                    { // Se introducen los valores separados por comas en un vector
                                        data_read = true;
                                        std::getline(file2, line);
                                        if (line != "</data>")
                                        {
                                            std::stringstream ss(line);
                                            std::string tmp;
                                            while (getline(ss, tmp, ','))
                                            { // Se resta 1 ya que tiled numera los tiles de 1 a n
                                                tilemap.push_back(std::stoi(tmp) - 1);
                                            }
                                        }
                                    } while (line != "</data>");
                                }
                            }

                            // Cierra el fichero
                            printf("Closing file %s\n\n", filename2.c_str());
                            file2.close();
                        }
                    }
                } while (line != "[/tilemap]");
            }*/

            // Si la linea contiene el texto [item] se realiza el proceso de carga de un item
            else if (line == "[item]")
            {
                item_t item;
                item.asset = asset;
                item.renderer = renderer;
                item.counter = 0;
                item.color1 = itemColor1;
                item.color2 = itemColor2;

                do
                {
                    std::getline(file, line);

                    // Encuentra la posición del caracter '='
                    int pos = line.find("=");

                    // Procesa las dos subcadenas
                    if (!setItem(&item, line.substr(0, pos), line.substr(pos + 1, line.length())))
                    {
                        printf("Warning: file %s\n, unknown parameter \"%s\"\n", filename.c_str(), line.substr(0, pos).c_str());
                    }

                } while (line != "[/item]");

                // Añade el item al vector de items
                const SDL_Point itemPos = {item.x, item.y};
                if (!itemTracker->hasBeenPicked(name, itemPos))
                {
                    items.push_back(new Item(item));
                }
            }

            // En caso contrario se parsea el fichero para buscar las variables y los valores
            else
            {
                // Encuentra la posición del caracter '='
                int pos = line.find("=");
                // Procesa las dos subcadenas
                if (!setVars(line.substr(0, pos), line.substr(pos + 1, line.length())))
                {
                    printf("Warning: file %s, unknown parameter \"%s\"\n", filename.c_str(), line.substr(0, pos).c_str());
                }
            }
        }

        // Cierra el fichero
        printf("Closing file %s\n", filename.c_str());
        file.close();
    }
    // El fichero no se puede abrir
    else
    {
        printf("Warning: Unable to open %s file\n", filename.c_str());
        return false;
    }

    return true;
}

// Carga las variables y texturas desde un fichero de mapa de tiles
bool Room::loadMapTileFile(std::string file_path)
{
    const std::string filename = file_path.substr(file_path.find_last_of("\\/") + 1);
    std::string line;
    std::ifstream file(file_path);

    // El fichero se puede abrir
    if (file.good())
    {
        // Procesa el fichero linea a linea
        printf("Reading file %s\n\n", filename.c_str());

        while (std::getline(file, line))
        { // Lee el fichero linea a linea
            if (line.find("data encoding") != std::string::npos)
            {
                // Lee la primera linea
                std::getline(file, line);
                while (line != "</data>")
                { // Procesa lineas mientras haya
                    std::stringstream ss(line);
                    std::string tmp;
                    while (getline(ss, tmp, ','))
                    {
                        tilemap.push_back(std::stoi(tmp) - 1);
                    }

                    // Lee la siguiente linea
                    std::getline(file, line);
                }
            }
        }

        // Cierra el fichero
        printf("Closing file %s\n\n", filename.c_str());
        file.close();
    }

    else
    { // El fichero no se puede abrir
        printf("Warning: Unable to open %s file\n", filename.c_str());
        return false;
    }

    return true;
}

// Asigna variables a partir de dos cadenas
bool Room::setVars(std::string var, std::string value)
{
    // Indicador de éxito en la asignación
    bool success = true;

    if (var == "tilemap")
    {
        tileMapFile = value;
    }

    else if (var == "name")
    {
        name = value;
    }

    else if (var == "bgColor")
    {
        bgColor = stringToColor(value);
    }

    else if (var == "border")
    {
        borderColor = stringToColor(value);
    }

    else if (var == "itemColor1")
    {
        itemColor1 = stringToColor(value);
    }

    else if (var == "itemColor2")
    {
        itemColor2 = stringToColor(value);
    }

    else if (var == "tileset")
    {
        tileset = value;
    }

    else if (var == "roomUp")
    {
        roomUp = value;
    }

    else if (var == "roomDown")
    {
        roomDown = value;
    }

    else if (var == "roomLeft")
    {
        roomLeft = value;
    }

    else if (var == "roomRight")
    {
        roomRight = value;
    }

    else if (var == "tilemap")
    {
        // Se introducen los valores separados por comas en un vector
        std::stringstream ss(value);
        std::string tmp;
        while (getline(ss, tmp, ','))
        {
            tilemap.push_back(std::stoi(tmp));
        }
    }

    else if (var == "")
    {
    }

    else
    {
        success = false;
    }

    return success;
}

// Asigna variables a una estructura enemy_t
bool Room::setEnemy(enemy_t *enemy, std::string var, std::string value)
{
    // Indicador de éxito en la asignación
    bool success = true;

    if (var == "tileset")
    {
        enemy->tileset = value;
    }

    else if (var == "animation")
    {
        enemy->animation = value;
    }

    else if (var == "width")
    {
        enemy->w = std::stof(value);
    }

    else if (var == "height")
    {
        enemy->h = std::stof(value);
    }

    else if (var == "x")
    {
        enemy->x = std::stof(value) * BLOCK;
    }

    else if (var == "y")
    {
        enemy->y = std::stof(value) * BLOCK;
    }

    else if (var == "vx")
    {
        enemy->vx = std::stof(value);
    }

    else if (var == "vy")
    {
        enemy->vy = std::stof(value);
    }

    else if (var == "x1")
    {
        enemy->x1 = std::stoi(value) * BLOCK;
    }

    else if (var == "x2")
    {
        enemy->x2 = std::stoi(value) * BLOCK;
    }

    else if (var == "y1")
    {
        enemy->y1 = std::stoi(value) * BLOCK;
    }

    else if (var == "y2")
    {
        enemy->y2 = std::stoi(value) * BLOCK;
    }

    else if (var == "flip")
    {
        enemy->flip = stringToBool(value);
    }

    else if (var == "color")
    {
        enemy->color = stringToColor(value);
    }

    else if (var == "[/enemy]")
    {
    }

    else
    {
        success = false;
    }

    return success;
}

// Asigna variables a una estructura item_t
bool Room::setItem(item_t *item, std::string var, std::string value)
{
    // Indicador de éxito en la asignación
    bool success = true;

    if (var == "tileset")
    {
        item->tileset = value;
    }

    else if (var == "counter")
    {
        item->counter = std::stoi(value);
    }

    else if (var == "x")
    {
        item->x = std::stof(value) * BLOCK;
    }

    else if (var == "y")
    {
        item->y = std::stof(value) * BLOCK;
    }

    else if (var == "tile")
    {
        item->tile = std::stof(value);
    }

    else if (var == "[/item]")
    {
    }

    else
    {
        success = false;
    }

    return success;
}

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

// Devuelve el color de la habitación
color_t Room::getBGColor()
{
    return bgColor;
}

// Devuelve el color del borde
color_t Room::getBorderColor()
{
    return borderColor;
}

// Crea la textura con el mapeado de la habitación
void Room::fillMapTexture()
{
    SDL_SetRenderTarget(renderer, mapTexture);
    SDL_SetRenderDrawColor(renderer, 0x00, 0x00, 0x00, 0x00);
    SDL_RenderClear(renderer);

    // Los tilesets son de 20x20 tiles. El primer tile es el 0. Cuentan hacia la derecha y hacia abajo

    SDL_Rect clip = {0, 0, tileSize, tileSize};
    for (int y = 0; y < mapHeight; ++y)
        for (int x = 0; x < mapWidth; ++x)
        {
            // Tiled pone los tiles vacios del mapa como cero y empieza a contar de 1 a n.
            // Al cargar el mapa en memoria, se resta uno, por tanto los tiles vacios son -1
            const int index = (y * mapWidth) + x;
            if (index > -1)
            {
                clip.x = (tilemap[index] % tilesetWidth) * tileSize;
                clip.y = (tilemap[index] / tilesetWidth) * tileSize;
                texture->render(renderer, x * tileSize, y * tileSize, &clip);

                // ****
                if (debug->getEnabled())
                {
                    if (clip.x != -tileSize)
                    {
                        clip.x = x * tileSize;
                        clip.y = y * tileSize;
                        SDL_SetRenderDrawColor(renderer, 64, 64, 64, 224);
                        SDL_RenderFillRect(renderer, &clip);
                    }
                }
                // ****
            }
        }

    // ****
    if (debug->getEnabled())
    {
        // BottomSurfaces
        if (true)
        {
            for (auto l : bottomSurfaces)
            {
                SDL_SetRenderDrawColor(renderer, (rand() % 128) + 96, (rand() % 128) + 96, (rand() % 128) + 96, 0xFF);
                SDL_SetRenderDrawColor(renderer, 255, 0, 0, 0xFF);
                SDL_RenderDrawLine(renderer, l.x1, l.y, l.x2, l.y);
            }
        }

        // TopSurfaces
        if (true)
        {
            for (auto l : topSurfaces)
            {
                SDL_SetRenderDrawColor(renderer, (rand() % 128) + 96, (rand() % 128) + 96, (rand() % 128) + 96, 0xFF);
                SDL_SetRenderDrawColor(renderer, 0, 255, 0, 0xFF);
                SDL_RenderDrawLine(renderer, l.x1, l.y, l.x2, l.y);
            }
        }

        // LeftSurfaces
        if (true)
        {
            for (auto l : leftSurfaces)
            {
                SDL_SetRenderDrawColor(renderer, (rand() % 128) + 96, (rand() % 128) + 96, (rand() % 128) + 96, 0xFF);
                SDL_RenderDrawLine(renderer, l.x, l.y1, l.x, l.y2);
            }
        }

        // RightSurfaces
        if (true)
        {
            for (auto l : rightSurfaces)
            {
                SDL_SetRenderDrawColor(renderer, (rand() % 128) + 96, (rand() % 128) + 96, (rand() % 128) + 96, 0xFF);
                SDL_RenderDrawLine(renderer, l.x, l.y1, l.x, l.y2);
            }
        }

        // LeftSlopes
        if (true)
        {
            for (auto l : leftSlopes)
            {
                SDL_SetRenderDrawColor(renderer, (rand() % 128) + 96, (rand() % 128) + 96, (rand() % 128) + 96, 0xFF);
                SDL_RenderDrawLine(renderer, l.x1, l.y1, l.x2, l.y2);
            }
        }

        // RightSlopes
        if (true)
        {
            for (auto l : rightSlopes)
            {
                SDL_SetRenderDrawColor(renderer, (rand() % 128) + 96, (rand() % 128) + 96, (rand() % 128) + 96, 0xFF);
                SDL_RenderDrawLine(renderer, l.x1, l.y1, l.x2, l.y2);
            }
        }
    }
    // ****

    SDL_SetRenderTarget(renderer, nullptr);
}

// Dibuja el mapa en pantalla
void Room::renderMap()
{
    // Dibuja la textura con el mapa en pantalla
    SDL_RenderCopy(renderer, mapTexture, nullptr, nullptr);

    // Dibuja los tiles animados
    for (auto a:animatedTiles)
    {
        a->render();
    }
}

// Dibuja los enemigos en pantalla
void Room::renderEnemies()
{
    for (auto enemy : enemies)
    {
        enemy->render();
    }
}

// Dibuja los objetos en pantalla
void Room::renderItems()
{
    for (auto item : items)
    {
        item->render();
    }
}

// Actualiza las variables y objetos de la habitación
void Room::update()
{
    if (paused)
    { // Si está en modo pausa no se actualiza nada
        return;
    }

    // Actualiza el contador
    counter++;

    // Actualiza los tiles animados
    updateAnimatedTiles();

    for (auto enemy : enemies)
    { // Actualiza los enemigos
        enemy->update();
    }

    for (auto item : items)
    { // Actualiza los items
        item->update();
    }
}

// Devuelve la cadena del fichero de la habitación contigua segun el borde
std::string Room::getRoom(int border)
{
    switch (border)
    {
    case BORDER_TOP:
        return roomUp;
        break;

    case BORDER_BOTTOM:
        return roomDown;
        break;

    case BORDER_RIGHT:
        return roomRight;
        break;

    case BORDER_LEFT:
        return roomLeft;
        break;

    default:
        break;
    }
    return "";
}

// Devuelve el tipo de tile que hay en ese pixel
tile_e Room::getTile(SDL_Point point)
{
    const int pos = ((point.y / tileSize) * mapWidth) + (point.x / tileSize);
    return getTile(pos);
}

// Devuelve el tipo de tile que hay en ese indice
tile_e Room::getTile(int index)
{
    const int maxTile = mapWidth * mapHeight;

    if (index < maxTile)
    {
        // Las filas 0-7 son de tiles t_wall
        if ((tilemap[index] >= 0) && (tilemap[index] < 8 * tilesetWidth))
        {
            return t_wall;
        }

        // La fila 8 es de tiles t_slope_r
        else if ((tilemap[index] >= 8 * tilesetWidth) && (tilemap[index] < 9 * tilesetWidth))
        {
            return t_slope_r;
        }

        // La fila 9 es de tiles t_slope_l
        else if ((tilemap[index] >= 9 * tilesetWidth) && (tilemap[index] < 10 * tilesetWidth))
        {
            return t_slope_l;
        }

        // Las filas 10-17 son de tiles t_passable
        if ((tilemap[index] >= 10 * tilesetWidth) && (tilemap[index] < 18 * tilesetWidth))
        {
            return t_passable;
        }

        // La fila 18 es de tiles t_animated
        if ((tilemap[index] >= 18 * tilesetWidth) && (tilemap[index] < 29 * tilesetWidth))
        {
            return t_animated;
        }

        // Las fila 19 es de tiles t_kill
        if ((tilemap[index] >= 19 * tilesetWidth) && (tilemap[index] < 20 * tilesetWidth))
        {
            return t_kill;
        }
    }

    return t_empty;
}

// Indica si hay colision con un enemigo a partir de un rectangulo
bool Room::enemyCollision(SDL_Rect &rect)
{
    bool collision = false;

    for (auto enemy : enemies)
    {
        collision |= checkCollision(rect, enemy->getCollider());
    }

    return collision;
}

// Indica si hay colision con un objeto a partir de un rectangulo
bool Room::itemCollision(SDL_Rect &rect)
{
    for (int i = 0; i < (int)items.size(); ++i)
    {
        if (checkCollision(rect, items[i]->getCollider()))
        {
            itemTracker->addItem(name, items[i]->getPos());
            delete items[i];
            items.erase(items.begin() + i);
            JA_PlaySound(itemSound);
            *itemsPicked = *itemsPicked + 1;
            return true;
        }
    }

    return false;
}

// Recarga la textura
void Room::reLoadTexture()
{
    texture->reLoad();
    fillMapTexture();
    for (auto enemy : enemies)
    {
        enemy->reLoadTexture();
    }

    for (auto item : items)
    {
        item->reLoadTexture();
    }
}

// Obten el tamaño del tile
int Room::getTileSize()
{
    return tileSize;
}

// Obten la coordenada de la cuesta a partir de un punto perteneciente a ese tile
int Room::getSlopeHeight(SDL_Point p, tile_e slope)
{
    // Calcula la base del tile
    int base = ((p.y / tileSize) * tileSize) + tileSize;
    debug->add("BASE = " + std::to_string(base));

    // Calcula cuanto se ha entrado en el tile horizontalmente
    const int pos = (p.x % tileSize); // esto da un valor entre 0 y 7
    debug->add("POS = " + std::to_string(pos));

    // Se resta a la base la cantidad de pixeles pos en funcion de la rampa
    if (slope == t_slope_r)
    {
        base -= pos + 1;
        debug->add("BASE_R = " + std::to_string(base));
    }
    else
    {
        base -= (tileSize - pos);
        debug->add("BASE_L = " + std::to_string(base));
    }

    return base;
}

// Calcula las superficies inferiores
void Room::setBottomSurfaces()
{
    std::vector<int> tile;

    // Busca todos los tiles de tipo muro que no tengan debajo otro muro
    // Hay que recorrer la habitación por filas (excepto los de la última fila)
    for (int i = 0; i < (int)tilemap.size() - mapWidth; ++i)
    {
        if (getTile(i) == t_wall && getTile(i + mapWidth) != t_wall)
        {
            tile.push_back(i);

            // Si llega al final de la fila, introduce un separador
            if (i % mapWidth == mapWidth - 1)
            {
                tile.push_back(-1);
            }
        }
    }

    // Recorre el vector de tiles buscando tiles consecutivos para localizar las superficies
    int i = 0;
    int lastOne = 0;
    while (i < (int)tile.size())
    {
        h_line_t line;
        line.x1 = (tile[i] % mapWidth) * tileSize;
        line.y = ((tile[i] / mapWidth) * tileSize) + tileSize - 1;
        lastOne = i;
        ++i;

        while (tile[i] == tile[i - 1] + 1)
        {
            lastOne = i;
            ++i;
        }

        line.x2 = ((tile[lastOne] % mapWidth) * tileSize) + tileSize - 1;
        bottomSurfaces.push_back(line);
        if (tile[i] == -1)
        { // Si el siguiente elemento es un separador, hay que saltarlo
            ++i;
        }
    }
}

// Calcula las superficies superiores
void Room::setTopSurfaces()
{
    std::vector<int> tile;

    // Busca todos los tiles de tipo muro o pasable que no tengan encima un muro
    // Hay que recorrer la habitación por filas (excepto los de la primera fila)
    for (int i = mapWidth; i < (int)tilemap.size(); ++i)
    {
        if ((getTile(i) == t_wall || getTile(i) == t_passable) && getTile(i - mapWidth) != t_wall)
        {
            tile.push_back(i);

            // Si llega al final de la fila, introduce un separador
            if (i % mapWidth == mapWidth - 1)
            {
                tile.push_back(-1);
            }
        }
    }

    // Recorre el vector de tiles buscando tiles consecutivos para localizar las superficies
    int i = 0;
    int lastOne = 0;
    while (i < (int)tile.size())
    {
        h_line_t line;
        line.x1 = (tile[i] % mapWidth) * tileSize;
        line.y = (tile[i] / mapWidth) * tileSize;
        lastOne = i;
        ++i;

        while (tile[i] == tile[i - 1] + 1)
        {
            lastOne = i;
            ++i;
        }

        line.x2 = ((tile[lastOne] % mapWidth) * tileSize) + tileSize - 1;
        topSurfaces.push_back(line);
        if (tile[i] == -1)
        { // Si el siguiente elemento es un separador, hay que saltarlo
            ++i;
        }
    }
}

// Calcula las superficies laterales izquierdas
void Room::setLeftSurfaces()
{
    std::vector<int> tile;

    // Busca todos los tiles de tipo muro que no tienen a su izquierda un tile de tipo muro
    // Hay que recorrer la habitación por columnas (excepto los de la primera columna)
    for (int i = 1; i < mapWidth; ++i)
    {
        for (int j = 0; j < mapHeight; ++j)
        {
            const int pos = (j * mapWidth + i);
            if (getTile(pos) == t_wall && getTile(pos - 1) != t_wall)
            {
                tile.push_back(pos);
            }
        }
    }

    // Recorre el vector de tiles buscando tiles consecutivos
    // (Los tiles de la misma columna, la diferencia entre ellos es de mapWidth)
    // para localizar las superficies
    int i = 0;
    while (i < (int)tile.size())
    {
        v_line_t line;
        line.x = (tile[i] % mapWidth) * tileSize;
        line.y1 = ((tile[i] / mapWidth) * tileSize);
        while (tile[i] + mapWidth == tile[i + 1])
        {
            ++i;
        }
        line.y2 = ((tile[i] / mapWidth) * tileSize) + tileSize - 1;
        leftSurfaces.push_back(line);
        ++i;
    }
}

// Calcula las superficies laterales derechas
void Room::setRightSurfaces()
{
    std::vector<int> tile;

    // Busca todos los tiles de tipo muro que no tienen a su derecha un tile de tipo muro
    // Hay que recorrer la habitación por columnas (excepto los de la última columna)
    for (int i = 0; i < mapWidth - 1; ++i)
    {
        for (int j = 0; j < mapHeight; ++j)
        {
            const int pos = (j * mapWidth + i);
            if (getTile(pos) == t_wall && getTile(pos + 1) != t_wall)
            {
                tile.push_back(pos);
            }
        }
    }

    // Recorre el vector de tiles buscando tiles consecutivos
    // (Los tiles de la misma columna, la diferencia entre ellos es de mapWidth)
    // para localizar las superficies
    int i = 0;
    while (i < (int)tile.size())
    {
        v_line_t line;
        line.x = ((tile[i] % mapWidth) * tileSize) + tileSize - 1;
        line.y1 = ((tile[i] / mapWidth) * tileSize);
        while (tile[i] + mapWidth == tile[i + 1])
        {
            ++i;
        }
        line.y2 = ((tile[i] / mapWidth) * tileSize) + tileSize - 1;
        rightSurfaces.push_back(line);
        ++i;
    }
}

// Encuentra todas las rampas que suben hacia la izquierda
void Room::setLeftSlopes()
{
    // Recorre la habitación entera por filas buscando tiles de tipo t_slope_l
    std::vector<int> found;
    for (int i = 0; i < (int)tilemap.size(); ++i)
    {
        if (getTile(i) == t_slope_l)
        {
            found.push_back(i);
        }
    }

    // El primer elemento es el inicio de una rampa. Se añade ese elemento y se buscan los siguientes,
    // que seran i + mapWidth + 1. Conforme se añaden se eliminan y se vuelve a escudriñar el vector de
    // tiles encontrados hasta que esté vacío

    while (found.size() > 0)
    {
        d_line_t line;
        line.x1 = (found[0] % mapWidth) * tileSize;
        line.y1 = (found[0] / mapWidth) * tileSize;
        int lookingFor = found[0] + mapWidth + 1;
        int lastOneFound = found[0];
        found.erase(found.begin());
        for (int i = 0; i < (int)found.size(); ++i)
        {
            if (found[i] == lookingFor)
            {
                lastOneFound = lookingFor;
                lookingFor += mapWidth + 1;
                found.erase(found.begin() + i);
                --i;
            }
        }
        line.x2 = ((lastOneFound % mapWidth) * tileSize) + tileSize - 1;
        line.y2 = ((lastOneFound / mapWidth) * tileSize) + tileSize - 1;
        leftSlopes.push_back(line);
    }
}

// Encuentra todas las rampas que suben hacia la derecha
void Room::setRightSlopes()
{
    // Recorre la habitación entera por filas buscando tiles de tipo t_slope_r
    std::vector<int> found;
    for (int i = 0; i < (int)tilemap.size(); ++i)
    {
        if (getTile(i) == t_slope_r)
        {
            found.push_back(i);
        }
    }

    // El primer elemento es el inicio de una rampa. Se añade ese elemento y se buscan los siguientes,
    // que seran i + mapWidth - 1. Conforme se añaden se eliminan y se vuelve a escudriñar el vector de
    // tiles encontrados hasta que esté vacío

    while (found.size() > 0)
    {
        d_line_t line;
        line.x1 = ((found[0] % mapWidth) * tileSize) + tileSize - 1;
        line.y1 = (found[0] / mapWidth) * tileSize;
        int lookingFor = found[0] + mapWidth - 1;
        int lastOneFound = found[0];
        found.erase(found.begin());
        for (int i = 0; i < (int)found.size(); ++i)
        {
            if (found[i] == lookingFor)
            {
                lastOneFound = lookingFor;
                lookingFor += mapWidth - 1;
                found.erase(found.begin() + i);
                --i;
            }
        }
        line.x2 = (lastOneFound % mapWidth) * tileSize;
        line.y2 = ((lastOneFound / mapWidth) * tileSize) + tileSize - 1;
        rightSlopes.push_back(line);
    }
}

// Localiza todos los tiles animados de la habitación
void Room::setAnimatedTiles()
{
    // Recorre la habitación entera por filas buscando tiles de tipo t_animated
    for (int i = 0; i < (int)tilemap.size(); ++i)
    {
        if (getTile(i) == t_animated)
        {
            // la i me da la ubicación
            int x = (i % mapWidth) * tileSize;
            int y = (i / mapWidth) * tileSize;

            // tilemap[i] me da el tile a poner
            int xc = (tilemap[i] % tilesetWidth) * tileSize;
            int yc = (tilemap[i] / tilesetWidth) * tileSize;

            Sprite *sp = new Sprite(x, y, 8, 8, texture, renderer);
            sp->setSpriteClip(xc, yc, 8, 8);
            animatedTiles.push_back(sp);
        }
    }
}

// Actualiza los tiles animados
void Room::updateAnimatedTiles()
{
    const int frame = (counter / 8) % 4;
    for (auto &a : animatedTiles)
    { // Frame vale 0, 1, 2 o 3. La x se aumenta de 8 en 8, salvo cuando frame vale 0 que ha de volver 3*8
        SDL_Rect rect = a->getSpriteClip();
        if (frame == 0)
            rect.x -= 24;
        else
            rect.x += 8;
        a->setSpriteClip(rect);
    }
}

// Pinta los tiles animados en pantalla
void Room::renderAnimatedTiles()
{
    for (auto a : animatedTiles)
    {
        a->render();
    }
}

// Comprueba las colisiones
int Room::checkRightSurfaces(SDL_Rect *rect)
{
    for (auto s : rightSurfaces)
    {
        if (checkCollision(s, *rect))
        {
            return s.x;
        }
    }

    return -1;
}

// Comprueba las colisiones
int Room::checkLeftSurfaces(SDL_Rect *rect)
{
    for (auto s : leftSurfaces)
    {
        if (checkCollision(s, *rect))
        {
            return s.x;
        }
    }

    return -1;
}

// Comprueba las colisiones
int Room::checkTopSurfaces(SDL_Rect *rect)
{
    for (auto s : topSurfaces)
    {
        if (checkCollision(s, *rect))
        {
            return s.y;
        }
    }

    return -1;
}

// Comprueba las colisiones
int Room::checkBottomSurfaces(SDL_Rect *rect)
{
    for (auto s : bottomSurfaces)
    {
        if (checkCollision(s, *rect))
        {
            return s.y;
        }
    }

    return -1;
}

// Comprueba las colisiones
bool Room::checkTopSurfaces(SDL_Point *p)
{
    for (auto s : topSurfaces)
    {
        if (checkCollision(s, *p))
        {
            return true;
        }
    }

    return false;
}

// Comprueba las colisiones
int Room::checkLeftSlopes(v_line_t *line)
{
    for (auto s : leftSlopes)
    {
        const SDL_Point p = checkCollision(s, *line);
        if (p.x != -1)
        {
            return p.y;
        }
    }

    return -1;
}

// Comprueba las colisiones
bool Room::checkLeftSlopes(SDL_Point *p)
{
    for (auto s : leftSlopes)
    {
        if (checkCollision(*p, s))
        {
            return true;
        }
    }

    return false;
}

// Comprueba las colisiones
int Room::checkRightSlopes(v_line_t *line)
{
    for (auto s : rightSlopes)
    {
        const SDL_Point p = checkCollision(s, *line);
        if (p.x != -1)
        {
            return p.y;
        }
    }

    return -1;
}

// Comprueba las colisiones
bool Room::checkRightSlopes(SDL_Point *p)
{
    for (auto s : rightSlopes)
    {
        if (checkCollision(*p, s))
        {
            return true;
        }
    }

    return false;
}

// Pone el mapa en modo pausa
void Room::pause()
{
    paused = true;
}

// Quita el modo pausa del mapa
void Room::resume()
{
    paused = false;
}