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

// Carga las variables y texturas desde un fichero de mapa de tiles
std::vector<int> loadRoomTileFile(std::string file_path)
{
    std::vector<int> tileMapFile;
    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", 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, ','))
                    {
                        tileMapFile.push_back(std::stoi(tmp) - 1);
                    }

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

        // Cierra el fichero
        printf("TileMap loaded: %s\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 tileMapFile;
}

// Carga las variables desde un fichero de mapa
room_t loadRoomFile(std::string file_path)
{
    room_t room;
    room.itemColor1 = "yellow";
    room.itemColor2 = "magenta";
    room.autoSurfaceDirection = 1;

    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
        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.flip = false;
                enemy.palette = p_zxspectrum;

                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
                room.enemies.push_back(enemy);
            }

            // Si la linea contiene el texto [item] se realiza el proceso de carga de un item
            else if (line == "[item]")
            {
                item_t item;
                item.counter = 0;
                item.color1 = stringToColor(p_zxspectrum, "yellow");
                item.color2 = stringToColor(p_zxspectrum, "magenta");

                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]");

                room.items.push_back(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(&room, 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("Room loaded: %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 room;
}

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

    if (var == "tileMapFile")
    {
        room->tileMapFile = value;
    }

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

    else if (var == "bgColor")
    {
        room->bgColor = value;
    }

    else if (var == "border")
    {
        room->borderColor = value;
    }

    else if (var == "itemColor1")
    {
        room->itemColor1 = value;
    }

    else if (var == "itemColor2")
    {
        room->itemColor2 = value;
    }

    else if (var == "tileSetFile")
    {
        room->tileSetFile = value;
    }

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

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

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

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

    else if (var == "autoSurface")
    {
        if (value == "right")
        {
            room->autoSurfaceDirection = 1;
        }
        else
        {
            room->autoSurfaceDirection = -1;
        }
    }

    else if (var == "" || var.substr(0, 1) == "#")
    {
    }

    else
    {
        success = false;
    }

    return success;
}

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

    if (var == "animation")
    {
        enemy->animationString = 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 = value;
    }

    else if (var == "[/enemy]" || var == "tileSetFile" || var.substr(0, 1) == "#")
    {
    }

    else
    {
        success = false;
    }

    return success;
}

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

    if (var == "tileSetFile")
    {
        item->tileSetFile = 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;
}

// Constructor
Room::Room(room_t *room, SDL_Renderer *renderer, Screen *screen, Asset *asset, options_t *options, ItemTracker *itemTracker, int *itemsPicked, Debug *debug)
{
    // Copia los punteros a objetos
    this->renderer = renderer;
    this->asset = asset;
    this->screen = screen;
    this->itemTracker = itemTracker;
    this->itemsPicked = itemsPicked;
    this->debug = debug;
    this->options = options;

    name = room->name;
    bgColor = room->bgColor;
    borderColor = room->borderColor;
    itemColor1 = room->itemColor1 == "" ? "yellow" : room->itemColor1;
    itemColor2 = room->itemColor2 == "" ? "magenta" : room->itemColor2;
    roomUp = room->roomUp;
    roomDown = room->roomDown;
    roomLeft = room->roomLeft;
    roomRight = room->roomRight;
    tileSetFile = room->tileSetFile;
    tileMapFile = room->tileMapFile;
    autoSurfaceDirection = room->autoSurfaceDirection;
    textureA = room->textureA;
    textureB = room->textureB;
    tileMap = room->tileMap;

    // Inicializa variables
    tileSize = 8;
    mapWidth = 32;
    mapHeight = 16;
    paused = false;
    counter = 0;
    // itemColor1 = "magenta";
    // itemColor2 = "yellow";
    // autoSurfaceDirection = 1;

    // Crea los enemigos
    for (auto &enemy : room->enemies)
    {
        enemy.renderer = renderer;
        enemies.push_back(new Enemy(enemy));
    }

    // Crea los items
    for (auto &item : room->items)
    {
        const SDL_Point itemPos = {item.x, item.y};

        if (!itemTracker->hasBeenPicked(room->name, itemPos))
        {
            item.renderer = renderer;
            items.push_back(new Item(item));
        }
    }

    // Crea los objetos
    // loadRoomFile(file);
    // texture = new Texture(renderer, asset->get(tileSetFile));
    // texture = resource->getTexture(tileSetFile);
    if (options->palette == p_zxspectrum)
    {
        texture = textureA;
    }
    else
    {
        texture = textureB;
    }

    tileSetWidth = texture->getWidth() / tileSize;

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

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

    // 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(stringToColor(options->palette, room->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 : aTile)
    {
        delete a.sprite;
    }
}

// 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 stringToColor(options->palette, bgColor);
}

// Devuelve el color del borde
color_t Room::getBorderColor()
{
    return stringToColor(options->palette, 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 tileSetFiles 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
            // Tampoco hay que dibujar los tiles animados que estan en la fila 19 (indices)
            const int index = (y * mapWidth) + x;
            const bool a = (tileMap->at(index) >= 18 * tileSetWidth) && (tileMap->at(index) < 19 * tileSetWidth);
            const bool b = tileMap->at(index) > -1;

            if (b && !a)
            {
                clip.x = (tileMap->at(index) % tileSetWidth) * tileSize;
                clip.y = (tileMap->at(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_SetRenderDrawColor(renderer, 0, 0, 255, 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_SetRenderDrawColor(renderer, 255, 255, 0, 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_SetRenderDrawColor(renderer, 0, 255, 255, 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_SetRenderDrawColor(renderer, 255, 0, 255, 0xFF);
                SDL_RenderDrawLine(renderer, l.x1, l.y1, l.x2, l.y2);
            }
        }

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

    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
    if (!debug->getEnabled())
    {
        renderAnimatedTiles();
    }
}

// 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-8 son de tiles t_wall
        if ((tileMap->at(index) >= 0) && (tileMap->at(index) < 9 * tileSetWidth))
        {
            return t_wall;
        }

        // Las filas 9-17 son de tiles t_passable
        else if ((tileMap->at(index) >= 9 * tileSetWidth) && (tileMap->at(index) < 18 * tileSetWidth))
        {
            return t_passable;
        }

        // Las filas 18-20 es de tiles t_animated
        else if ((tileMap->at(index) >= 18 * tileSetWidth) && (tileMap->at(index) < 21 * tileSetWidth))
        {
            return t_animated;
        }

        // La fila 21 es de tiles t_slope_r
        else if ((tileMap->at(index) >= 21 * tileSetWidth) && (tileMap->at(index) < 22 * tileSetWidth))
        {
            return t_slope_r;
        }

        // La fila 22 es de tiles t_slope_l
        else if ((tileMap->at(index) >= 22 * tileSetWidth) && (tileMap->at(index) < 23 * tileSetWidth))
        {
            return t_slope_l;
        }

        // La fila 23 es de tiles t_kill
        else if ((tileMap->at(index) >= 23 * tileSetWidth) && (tileMap->at(index) < 24 * 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();
    }
}

// Recarga la paleta
void Room::reLoadPalette()
{
    // Cambia el color de los items
    for (auto item : items)
    {
        item->setColors(stringToColor(options->palette, itemColor1), stringToColor(options->palette, itemColor2));
    }

    // Cambia el color de los enemigos
    for (auto enemy : enemies)
    {
        enemy->setPalette(options->palette);
    }

    // Establece el color del borde
    screen->setBorderColor(stringToColor(options->palette, borderColor));

    // Cambia la textura
    if (options->palette == p_zxspectrum)
    {
        texture = textureA;
    }
    else
    {
        texture = textureB;
    }

    // Recarga las texturas
    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())
    do
    {
        h_line_t line;
        line.x1 = (tile.at(i) % mapWidth) * tileSize;
        line.y = ((tile.at(i) / mapWidth) * tileSize) + tileSize - 1;
        lastOne = i;
        i++;

        while (tile.at(i) == tile.at(i - 1) + 1)
        {
            lastOne = i;
            if (i == (int)tile.size() - 1)
            {
                break;
            }
            i++;
        }

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

// 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())
    do
    {
        h_line_t line;
        line.x1 = (tile.at(i) % mapWidth) * tileSize;
        line.y = (tile.at(i) / mapWidth) * tileSize;
        lastOne = i;
        i++;

        while (tile.at(i) == tile.at(i - 1) + 1)
        {
            lastOne = i;
            if (i == (int)tile.size() - 1)
            {
                break;
            }
            i++;
        }

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

// 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())
    do
    {
        v_line_t line;
        line.x = (tile.at(i) % mapWidth) * tileSize;
        line.y1 = ((tile.at(i) / mapWidth) * tileSize);
        while (tile.at(i) + mapWidth == tile[i + 1])
        {
            if (i == (int)tile.size() - 1)
            {
                break;
            }
            i++;
        }
        line.y2 = ((tile.at(i) / mapWidth) * tileSize) + tileSize - 1;
        leftSurfaces.push_back(line);
        i++;
    } while (i < (int)tile.size() - 1);
}

// 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())
    do
    {
        v_line_t line;
        line.x = ((tile.at(i) % mapWidth) * tileSize) + tileSize - 1;
        line.y1 = ((tile.at(i) / mapWidth) * tileSize);
        while (tile.at(i) + mapWidth == tile[i + 1])
        {
            if (i == (int)tile.size() - 1)
            {
                break;
            }
            i++;
        }
        line.y2 = ((tile.at(i) / mapWidth) * tileSize) + tileSize - 1;
        rightSurfaces.push_back(line);
        i++;
    } while (i < (int)tile.size() - 1);
}

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

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

    // Busca todos los tiles de tipo animado
    // 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_animated)
        {
            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.at(i) % mapWidth) * tileSize;
        line.y = (tile.at(i) / mapWidth) * tileSize;
        lastOne = i;
        i++;

        while (tile.at(i) == tile.at(i - 1) + 1)
        {
            lastOne = i;
            i++;
        }

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

// 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 es la ubicación
            const int x = (i % mapWidth) * tileSize;
            const int y = (i / mapWidth) * tileSize;

            // TileMap->at(i) es el tile a poner
            const int xc = (tileMap->at(i) % tileSetWidth) * tileSize;
            const int yc = (tileMap->at(i) / tileSetWidth) * tileSize;

            aTile_t at;
            at.sprite = new Sprite(x, y, 8, 8, texture, renderer);
            at.sprite->setSpriteClip(xc, yc, 8, 8);
            at.xcOrig = xc;
            aTile.push_back(at);
        }
    }
}

// Actualiza los tiles animados
void Room::updateAnimatedTiles()
{
    const int numFrames = 4;
    int offset = 0;
    if (autoSurfaceDirection == -1)
    {
        offset = ((counter / 3) % numFrames * tileSize);
    }
    else
    {
        offset = ((numFrames - 1 - ((counter / 3) % numFrames)) * tileSize);
    }

    for (auto &a : aTile)
    {
        SDL_Rect rect = a.sprite->getSpriteClip();
        rect.x = a.xcOrig + offset;
        a.sprite->setSpriteClip(rect);
    }
}

// Pinta los tiles animados en pantalla
void Room::renderAnimatedTiles()
{
    for (auto a : aTile)
    {
        a.sprite->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
int Room::checkAutoSurfaces(SDL_Rect *rect)
{
    for (auto s : autoSurfaces)
    {
        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
bool Room::checkAutoSurfaces(SDL_Point *p)
{
    for (auto s : autoSurfaces)
    {
        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;
}

// Obten la direccion de las superficies automaticas
int Room::getAutoSurfaceDirection()
{
    return autoSurfaceDirection;
}