#include "door_manager.hpp"

#include <utility>  // Para std::move

#include "game/entities/door.hpp"           // Para Door
#include "game/gameplay/collision_map.hpp"  // Para CollisionMap
#include "game/gameplay/door_tracker.hpp"   // Para DoorTracker
#include "game/gameplay/inventory.hpp"      // Para Inventory
#include "game/gameplay/tile_collider.hpp"  // Para TileCollider::Tile
#include "utils/defines.hpp"                // Para Map::WIDTH, Tile::SIZE
#include "utils/utils.hpp"                  // Para checkCollision

// Constructor
DoorManager::DoorManager(std::string room_id, CollisionMap* collision_map)
    : room_id_(std::move(room_id)),
      collision_map_(collision_map) {
}

// Añade una puerta. Si está bloqueante (CLOSED), pinta sus 4 tiles como WALL
// en el CollisionMap. Si ya está OPENED (porque venía persistida del
// DoorTracker), no se tocan los tiles.
void DoorManager::addDoor(std::shared_ptr<Door> door) {  // NOLINT(readability-identifier-naming)
    if (door->isBlocking()) {
        writeDoorTiles(*door, static_cast<int>(TileCollider::Tile::WALL));
    }
    doors_.push_back(std::move(door));
}

// Elimina todas las puertas
void DoorManager::clear() {
    doors_.clear();
}

// Actualiza animaciones y procesa transiciones a OPENED
void DoorManager::update(float delta_time) {
    for (const auto& door : doors_) {
        door->update(delta_time);

        // Si la puerta acaba de transicionar a OPENED, liberar los tiles y persistir
        if (door->justOpened()) {
            writeDoorTiles(*door, static_cast<int>(TileCollider::Tile::EMPTY));
            DoorTracker::get()->addDoor(room_id_, door->getPos());
        }
    }
}

// Renderiza todas las puertas
void DoorManager::render() {
    for (const auto& door : doors_) {
        door->render();
    }
}

// Pausa/despausa todas las puertas
void DoorManager::setPaused(bool paused) {
    for (const auto& door : doors_) {
        door->setPaused(paused);
    }
}

// Intenta abrir las puertas cercanas al jugador
void DoorManager::tryUnlock(const SDL_FRect& player_rect) {
    // Infla el rect del jugador 1 px en cada lado para detectar contacto
    const SDL_FRect INFLATED = {
        .x = player_rect.x - 1.0F,
        .y = player_rect.y - 1.0F,
        .w = player_rect.w + 2.0F,
        .h = player_rect.h + 2.0F,
    };

    for (const auto& door : doors_) {
        if (door->getState() != Door::State::CLOSED) {
            continue;
        }
        if (::checkCollision(INFLATED, door->getCollider()) && Inventory::get()->hasKey(door->getId())) {
            door->startOpening();
        }
    }
}

// Setea las 4 celdas que ocupa la puerta (1 col × 4 filas) al valor indicado
void DoorManager::writeDoorTiles(const Door& door, int tile_value) {
    // Convertir posición en píxeles a coordenadas de tile
    const SDL_FPoint POS = door.getPos();
    const int COL = static_cast<int>(POS.x) / Tile::SIZE;
    const int ROW = static_cast<int>(POS.y) / Tile::SIZE;

    for (int i = 0; i < DOOR_TILES_HEIGHT; ++i) {
        const int INDEX = ((ROW + i) * Map::WIDTH) + COL;
        collision_map_->setCollisionTile(INDEX, tile_value);
    }
}
