#include "RoomLayoutSystem.hpp"
#include "FurnitureLibrary.hpp"
#include "../components/CellGrid.hpp"
#include <OgreLogManager.h>
#include <climits>
#include <set>
#include <unordered_map>
#include <random>

RoomLayoutSystem::RoomLayoutSystem(flecs::world& world, Ogre::SceneManager* sceneMgr)
	: m_world(world)
	, m_sceneMgr(sceneMgr)
	, m_roomQuery(world.query<RoomComponent>())
	, m_clearAreaQuery(world.query<ClearAreaComponent>())
{
}

RoomLayoutSystem::~RoomLayoutSystem() = default;

void RoomLayoutSystem::initialize()
{
	if (m_initialized)
		return;
	m_initialized = true;
	Ogre::LogManager::getSingleton().logMessage("RoomLayoutSystem initialized");
}

void RoomLayoutSystem::update()
{
	if (!m_initialized)
		return;

	// Check if any component is dirty - if so, we need to reprocess everything
	// because room layout is interdependent
	bool anyDirty = false;
	
	m_clearAreaQuery.each([&](flecs::entity e, ClearAreaComponent& c) {
		if (c.dirty || !c.processed) anyDirty = true;
	});
	m_roomQuery.each([&](flecs::entity e, RoomComponent& r) {
		if (r.dirty) anyDirty = true;
	});
	
	// If anything changed, reprocess everything
	if (anyDirty || m_needsFullReprocess) {
		reprocessAll();
		m_needsFullReprocess = false;
	}
}

void RoomLayoutSystem::reprocessAll()
{
	Ogre::LogManager::getSingleton().logMessage("RoomLayoutSystem: Full reprocess starting...");
	
	// STEP 1: Clear areas FIRST
	m_clearAreaQuery.each([&](flecs::entity clearEntity, ClearAreaComponent& clearArea) {
		CellGridComponent* grid = findParentGrid(clearEntity);
		if (grid) {
			processClearArea(clearEntity, clearArea, *grid);
			clearArea.dirty = false;
			clearArea.processed = true;
		}
	});

	// STEP 2: Process all rooms (creates floor, ceiling, interior walls)
	m_roomQuery.each([&](flecs::entity roomEntity, RoomComponent& room) {
		CellGridComponent* grid = findParentGrid(roomEntity);
		if (grid) {
			processRoom(roomEntity, room, *grid);
			room.dirty = false;
		}
	});

	// STEP 3: Process connections for all rooms (creates doors between rooms)
	// Build a map of persistent ID -> room entity for connection lookup
	std::unordered_map<std::string, flecs::entity> roomIdMap;
	m_roomQuery.each([&](flecs::entity roomEntity, RoomComponent& room) {
		room.ensurePersistentId(roomEntity.id());
		roomIdMap[room.persistentId] = roomEntity;
	});
	
	// Track which room pairs we've processed to avoid duplicates
	std::set<std::pair<std::string, std::string>> processedPairs;
	m_roomQuery.each([&](flecs::entity roomEntity, RoomComponent& room) {
		CellGridComponent* grid = findParentGrid(roomEntity);
		if (grid) {
			// Only process connections where this room's ID is "less than" the other
			// to avoid creating duplicate doors
			for (const std::string& otherRoomId : room.connectedRoomIds) {
				std::string id1 = room.persistentId;
				std::string id2 = otherRoomId;
				if (id1 > id2) std::swap(id1, id2);
				
				auto pair = std::make_pair(id1, id2);
				if (processedPairs.insert(pair).second) {
					// New pair - process it
					auto it = roomIdMap.find(otherRoomId);
					if (it != roomIdMap.end()) {
						flecs::entity otherRoom = it->second;
						if (otherRoom.is_alive() && otherRoom.has<RoomComponent>()) {
							processRoomConnection(roomEntity, room, otherRoom, otherRoom.get<RoomComponent>(), *grid);
						}
					}
				}
			}
		}
	});

	// STEP 4: Process exits for all rooms (creates external walls/doors/windows)
	m_roomQuery.each([&](flecs::entity roomEntity, RoomComponent& room) {
		CellGridComponent* grid = findParentGrid(roomEntity);
		if (grid) {
			processRoomExits(roomEntity, room, *grid);
		}
	});
	
	// STEP 5: Process windows for rooms that have createWindows=true
	// This runs after all doors/exits are created
	m_roomQuery.each([&](flecs::entity roomEntity, RoomComponent& room) {
		if (room.createWindows) {
			CellGridComponent* grid = findParentGrid(roomEntity);
			if (grid) {
				processRoomWindows(roomEntity, room, *grid);
			}
		}
	});

	// STEP 6: Process exterior generation LAST (always runs for all rooms)
	// This mirrors the original Lua create_exterior() function
	m_roomQuery.each([&](flecs::entity roomEntity, RoomComponent& room) {
		CellGridComponent* grid = findParentGrid(roomEntity);
		if (grid) {
			processExteriorGeneration(*grid);
		}
	});

	// STEP 7: Generate furniture for all rooms
	FurnitureLibrary::getInstance().loadAll();
	m_roomQuery.each([&](flecs::entity roomEntity, RoomComponent& room) {
		CellGridComponent* grid = findParentGrid(roomEntity);
		if (grid) {
			generateRoomFurniture(room, *grid);
		}
	});
	
	Ogre::LogManager::getSingleton().logMessage("RoomLayoutSystem: Full reprocess complete.");
}

void RoomLayoutSystem::processClearArea(flecs::entity clearEntity, ClearAreaComponent& clearArea,
                                        CellGridComponent& grid)
{
	Ogre::LogManager::getSingleton().logMessage(
		"RoomLayoutSystem: Clearing area [" + 
		std::to_string(clearArea.minX) + "," + std::to_string(clearArea.minZ) + "] to [" +
		std::to_string(clearArea.maxX) + "," + std::to_string(clearArea.maxZ) + "]");

	// Clear cells in the area
	if (clearArea.clearCells) {
		for (int y = clearArea.minY; y < clearArea.maxY; y++) {
			for (int z = clearArea.minZ; z < clearArea.maxZ; z++) {
				for (int x = clearArea.minX; x < clearArea.maxX; x++) {
					grid.removeCell(x, y, z);
				}
			}
		}
	}

	// Clear furniture cells in the area
	if (clearArea.clearFurniture) {
		for (int y = clearArea.minY; y < clearArea.maxY; y++) {
			for (int z = clearArea.minZ; z < clearArea.maxZ; z++) {
				for (int x = clearArea.minX; x < clearArea.maxX; x++) {
					grid.removeFurnitureCell(x, y, z);
				}
			}
		}
	}

	// Clear roof entities (children of the grid entity)
	if (clearArea.clearRoofs) {
		flecs::entity gridEntity = clearEntity.parent();
		if (gridEntity.is_alive()) {
			std::vector<flecs::entity> toRemove;
			gridEntity.children([&](flecs::entity child) {
				if (child.has<RoofComponent>()) {
					const auto& roof = child.get<RoofComponent>();
					if (roof.posX >= clearArea.minX && roof.posX < clearArea.maxX &&
					    roof.posZ >= clearArea.minZ && roof.posZ < clearArea.maxZ) {
						toRemove.push_back(child);
					}
				}
			});
			for (auto e : toRemove) {
				e.destruct();
			}
		}
	}

	// Clear room entities (children of the grid entity)
	if (clearArea.clearRooms) {
		flecs::entity gridEntity = clearEntity.parent();
		if (gridEntity.is_alive()) {
			std::vector<flecs::entity> toRemove;
			gridEntity.children([&](flecs::entity child) {
				if (child.has<RoomComponent>()) {
					const auto& room = child.get<RoomComponent>();
					bool overlaps = !(room.maxX <= clearArea.minX || room.minX >= clearArea.maxX ||
					                  room.maxZ <= clearArea.minZ || room.minZ >= clearArea.maxZ);
					if (overlaps) {
						toRemove.push_back(child);
					}
				}
			});
			for (auto e : toRemove) {
				e.destruct();
			}
		}
	}

	grid.markDirty();
}

void RoomLayoutSystem::processExteriorGeneration(CellGridComponent& grid)
{
	// This mirrors the original Lua create_exterior() function
	// It processes ALL rooms and adds exterior flags where interior walls face empty space
	// Note: This runs AFTER all rooms are created, so it can properly detect shared walls
	
	flecs::entity gridEntity = flecs::entity::null();
	
	// Find the grid entity from the grid component
	m_world.each([&](flecs::entity e, CellGridComponent& cg) {
		if (&cg == &grid) {
			gridEntity = e;
		}
	});
	
	if (!gridEntity.is_alive()) return;

	// Iterate through all rooms in the grid
	gridEntity.children([&](flecs::entity roomEntity) {
		if (!roomEntity.has<RoomComponent>()) return true;

		const auto& room = roomEntity.get<RoomComponent>();
		int y = room.minY;

		// Process Z- side (north)
		for (int x = room.minX; x < room.maxX; x++) {
			// Check if there's a cell OUTSIDE the room (at z-1)
			// If there IS a cell there, skip - this is a shared wall
			if (grid.findCell(x, y, room.minZ - 1)) continue;
			
			Cell* cellPtr = grid.findCell(x, y, room.minZ);
			if (!cellPtr) continue;
			
			// Add exterior flags for interior walls/doors/windows (without clearing interior)
			if (cellPtr->hasFlag(CellFlags::IntWallZNeg))
				cellPtr->setFlag(CellFlags::WallZNeg);
			if (cellPtr->hasFlag(CellFlags::IntDoorZNeg))
				cellPtr->setFlag(CellFlags::DoorZNeg);
			if (cellPtr->hasFlag(CellFlags::IntWindowZNeg))
				cellPtr->setFlag(CellFlags::WindowZNeg);
		}

		// Process Z+ side (south)
		for (int x = room.minX; x < room.maxX; x++) {
			if (grid.findCell(x, y, room.maxZ)) continue;
			
			Cell* cellPtr = grid.findCell(x, y, room.maxZ - 1);
			if (!cellPtr) continue;
			
			if (cellPtr->hasFlag(CellFlags::IntWallZPos))
				cellPtr->setFlag(CellFlags::WallZPos);
			if (cellPtr->hasFlag(CellFlags::IntDoorZPos))
				cellPtr->setFlag(CellFlags::DoorZPos);
			if (cellPtr->hasFlag(CellFlags::IntWindowZPos))
				cellPtr->setFlag(CellFlags::WindowZPos);
		}

		// Process X+ side (east)
		for (int z = room.minZ; z < room.maxZ; z++) {
			if (grid.findCell(room.maxX, y, z)) continue;
			
			Cell* cellPtr = grid.findCell(room.maxX - 1, y, z);
			if (!cellPtr) continue;
			
			if (cellPtr->hasFlag(CellFlags::IntWallXPos))
				cellPtr->setFlag(CellFlags::WallXPos);
			if (cellPtr->hasFlag(CellFlags::IntDoorXPos))
				cellPtr->setFlag(CellFlags::DoorXPos);
			if (cellPtr->hasFlag(CellFlags::IntWindowXPos))
				cellPtr->setFlag(CellFlags::WindowXPos);
		}

		// Process X- side (west)
		for (int z = room.minZ; z < room.maxZ; z++) {
			if (grid.findCell(room.minX - 1, y, z)) continue;
			
			Cell* cellPtr = grid.findCell(room.minX, y, z);
			if (!cellPtr) continue;
			
			if (cellPtr->hasFlag(CellFlags::IntWallXNeg))
				cellPtr->setFlag(CellFlags::WallXNeg);
			if (cellPtr->hasFlag(CellFlags::IntDoorXNeg))
				cellPtr->setFlag(CellFlags::DoorXNeg);
			if (cellPtr->hasFlag(CellFlags::IntWindowXNeg))
				cellPtr->setFlag(CellFlags::WindowXNeg);
		}

		return true;
	});

	grid.markDirty();
}

void RoomLayoutSystem::processRoom(flecs::entity roomEntity, RoomComponent& room, 
                                   CellGridComponent& grid)
{
	Ogre::LogManager::getSingleton().logMessage(
		"RoomLayoutSystem: Processing room " + std::to_string(roomEntity.id()) +
		" at [" + std::to_string(room.minX) + "," + std::to_string(room.minZ) + 
		"] size [" + std::to_string(room.getSizeX()) + "," + std::to_string(room.getSizeZ()) + "]");

	int y = room.minY;

	// Create floor and ceiling for the room area
	for (int z = room.minZ; z < room.maxZ; z++) {
		for (int x = room.minX; x < room.maxX; x++) {
			Cell& cell = grid.getOrCreateCell(x, y, z);
			
			if (room.createFloor)
				cell.setFlag(CellFlags::Floor);
			if (room.createCeiling)
				cell.setFlag(CellFlags::Ceiling);
		}
	}

	// Create interior walls around the room perimeter
	if (room.createInteriorWalls) {
		// Z- side (north)
		for (int x = room.minX; x < room.maxX; x++) {
			Cell& cell = grid.getOrCreateCell(x, y, room.minZ);
			cell.setFlag(CellFlags::IntWallZNeg);
		}
		// Z+ side (south)
		for (int x = room.minX; x < room.maxX; x++) {
			Cell& cell = grid.getOrCreateCell(x, y, room.maxZ - 1);
			cell.setFlag(CellFlags::IntWallZPos);
		}
		// X- side (west)
		for (int z = room.minZ; z < room.maxZ; z++) {
			Cell& cell = grid.getOrCreateCell(room.minX, y, z);
			cell.setFlag(CellFlags::IntWallXNeg);
		}
		// X+ side (east)
		for (int z = room.minZ; z < room.maxZ; z++) {
			Cell& cell = grid.getOrCreateCell(room.maxX - 1, y, z);
			cell.setFlag(CellFlags::IntWallXPos);
		}
	}

	grid.markDirty();
}

void RoomLayoutSystem::processRoomConnections(flecs::entity roomEntity, RoomComponent& room,
                                              CellGridComponent& grid)
{
	// This is now handled in reprocessAll() to avoid duplicates
}

void RoomLayoutSystem::processRoomConnection(flecs::entity roomAEntity, const RoomComponent& roomA,
                                             flecs::entity roomBEntity, const RoomComponent& roomB,
                                             CellGridComponent& grid)
{
	Ogre::LogManager::getSingleton().logMessage(
		"RoomLayoutSystem: Processing connection between rooms " +
		std::to_string(roomAEntity.id()) + " and " + std::to_string(roomBEntity.id()));

	int y = roomA.minY;

	// Find adjacent cells between the two rooms
	std::vector<std::pair<int, int>> cellsA, cellsB;
	findAdjacentCells(roomA, roomB, cellsA, cellsB);

	if (cellsA.empty()) {
		Ogre::LogManager::getSingleton().logMessage(
			"RoomLayoutSystem: No adjacent cells found between rooms");
		return;
	}

	// Find the cell closest to the geometric center of all adjacent cells
	// This mirrors the original Lua connectRooms() logic
	size_t index = 0;
	if (cellsA.size() > 1) {
		// Calculate average (center) point using the SAME method as original Lua
		// Note: Original Lua uses integer division which truncates toward zero
		int sumX = 0, sumZ = 0;
		for (size_t i = 0; i < cellsA.size(); i++) {
			sumX += cellsA[i].first;
			sumZ += cellsA[i].second;
		}
		sumX /= cellsA.size();
		sumZ /= cellsA.size();
		
		
		// Find closest point to average (using same logic as original Lua)
		// Original: if (distance > mdx * mdx + mdz * mdz) - uses > not >=
		// This keeps the first cell in case of ties
		int minDistance = -1;
		for (size_t i = 0; i < cellsA.size(); i++) {
			int mdx = cellsA[i].first - sumX;
			int mdz = cellsA[i].second - sumZ;
			int dist = mdx * mdx + mdz * mdz;
			if (i == 0) {
				index = 0;
				minDistance = dist;
			} else if (dist < minDistance) {
				minDistance = dist;
				index = i;
			}
		}
	}
	
	// Door position in roomA (closest to center of shared wall)
	int xA = cellsA[index].first;
	int zA = cellsA[index].second;
	
	// Door position in roomB (adjacent to roomA's door)
	int xB = cellsB[index].first;
	int zB = cellsB[index].second;
	
	// Calculate relative position (like original Lua: dx = seg1[index].first - seg2[index].first)
	int dx = xA - xB;
	int dz = zA - zB;
	
	// Determine sides based on relative position of cells (matching original Lua)
	// If dz < 0: roomA is north of roomB, so roomA needs Z+ door, roomB needs Z- door
	// If dz > 0: roomA is south of roomB, so roomA needs Z- door, roomB needs Z+ door
	// If dx < 0: roomA is west of roomB, so roomA needs X+ door, roomB needs X- door
	// If dx > 0: roomA is east of roomB, so roomA needs X- door, roomB needs X+ door
	
	int sideA = -1, sideB = -1;
	
	if (dz < 0) {
		// roomA is north of roomB
		sideA = 1; // Z+
		sideB = 0; // Z-
	} else if (dz > 0) {
		// roomA is south of roomB
		sideA = 0; // Z-
		sideB = 1; // Z+
	} else if (dx < 0) {
		// roomA is west of roomB
		sideA = 3; // X+
		sideB = 2; // X-
	} else if (dx > 0) {
		// roomA is east of roomB
		sideA = 2; // X-
		sideB = 3; // X+
	}
	
	// Create door in roomA
	if (sideA >= 0) {
		clearWallFlags(grid, xA, y, zA, sideA);
		setWallFlags(grid, xA, y, zA, sideA, false, true, false); // Interior door
	}
	
	// Create door in roomB
	if (sideB >= 0) {
		clearWallFlags(grid, xB, y, zB, sideB);
		setWallFlags(grid, xB, y, zB, sideB, false, true, false); // Interior door
	}

	grid.markDirty();
}

void RoomLayoutSystem::processRoomExits(flecs::entity roomEntity, RoomComponent& room,
                                        CellGridComponent& grid)
{
	Ogre::LogManager::getSingleton().logMessage(
		"RoomLayoutSystem: Processing exits for room " + std::to_string(roomEntity.id()));

	flecs::entity gridEntity = roomEntity.parent();
	if (!gridEntity.is_alive()) return;

	int y = room.minY;

	// Process each exit side
	// This mirrors the original Lua createExit0-3 functions:
	// - Collect all outside-facing wall positions
	// - Average them to find the middle
	// - Create a SINGLE door at that position
	for (int side = 0; side < 4; side++) {
		if (!room.exits[side]) continue;

		auto cells = getRoomEdgeCells(room, side);
		
		// Collect positions of all cells that face outside and have interior walls
		int sumPos = -1;  // Running sum of positions (X or Z depending on side)
		int count = 0;    // Number of valid positions found
		
		for (const auto& cell : cells) {
			int x = cell.first;
			int z = cell.second;

			// Check if this cell faces outside (no cell at adjacent position)
			bool facesOutside = false;
			switch (side) {
				case 0: facesOutside = !grid.findCell(x, y, z - 1); break;
				case 1: facesOutside = !grid.findCell(x, y, z + 1); break;
				case 2: facesOutside = !grid.findCell(x - 1, y, z); break;
				case 3: facesOutside = !grid.findCell(x + 1, y, z); break;
			}
			
			if (!facesOutside) continue;
			
			// Check if this cell has an interior wall on this side
			Cell* cellPtr = grid.findCell(x, y, z);
			if (!cellPtr) continue;
			
			bool hasIntWall = false;
			switch (side) {
				case 0: hasIntWall = cellPtr->hasFlag(CellFlags::IntWallZNeg); break;
				case 1: hasIntWall = cellPtr->hasFlag(CellFlags::IntWallZPos); break;
				case 2: hasIntWall = cellPtr->hasFlag(CellFlags::IntWallXNeg); break;
				case 3: hasIntWall = cellPtr->hasFlag(CellFlags::IntWallXPos); break;
			}
			
			if (hasIntWall) {
				// Add position to sum (use X for Z- and Z+ sides, Z for X- and X+ sides)
				int pos = (side < 2) ? x : z;
				if (sumPos == -1)
					sumPos = pos;
				else
					sumPos += pos;
				count++;
			}
		}
		
		// Create a single door at the averaged position
		if (count > 0) {
			int midPos = sumPos / count;
			
			// Calculate the actual cell coordinates
			int doorX, doorZ;
			switch (side) {
				case 0: // Z- side: X varies, Z is minZ
					doorX = midPos;
					doorZ = room.minZ;
					break;
				case 1: // Z+ side: X varies, Z is maxZ-1
					doorX = midPos;
					doorZ = room.maxZ - 1;
					break;
				case 2: // X- side: X is minX, Z varies
					doorX = room.minX;
					doorZ = midPos;
					break;
				case 3: // X+ side: X is maxX-1, Z varies
					doorX = room.maxX - 1;
					doorZ = midPos;
					break;
				default:
					continue;
			}
			
			// Convert interior wall to interior door at the middle position
			clearWallFlags(grid, doorX, y, doorZ, side);
			setWallFlags(grid, doorX, y, doorZ, side, false, true, false); // Interior door
		}
	}

	grid.markDirty();
}

void RoomLayoutSystem::processRoomWindows(flecs::entity roomEntity, RoomComponent& room,
                                          CellGridComponent& grid)
{
	Ogre::LogManager::getSingleton().logMessage(
		"RoomLayoutSystem: Processing windows for room " + std::to_string(roomEntity.id()));

	flecs::entity gridEntity = roomEntity.parent();
	if (!gridEntity.is_alive()) return;

	int y = room.minY;

	// Process each side of the room
	for (int side = 0; side < 4; side++) {
		auto cells = getRoomEdgeCells(room, side);

		for (const auto& cell : cells) {
			int x = cell.first;
			int z = cell.second;

			// Check if this cell faces outside (not adjacent to another room)
			bool facesOutside = false;
			switch (side) {
				case 0: facesOutside = !grid.findCell(x, y, z - 1); break;
				case 1: facesOutside = !grid.findCell(x, y, z + 1); break;
				case 2: facesOutside = !grid.findCell(x - 1, y, z); break;
				case 3: facesOutside = !grid.findCell(x + 1, y, z); break;
			}
			
			if (facesOutside) {
				Cell* cellPtr = grid.findCell(x, y, z);
				if (!cellPtr) continue;

				// Convert interior wall to interior window
				// (exterior generation will convert to exterior window later)
				switch (side) {
					case 0: // Z-
						if (cellPtr->hasFlag(CellFlags::IntWallZNeg)) {
							cellPtr->clearFlag(CellFlags::IntWallZNeg);
							cellPtr->setFlag(CellFlags::IntWindowZNeg);
						}
						break;
					case 1: // Z+
						if (cellPtr->hasFlag(CellFlags::IntWallZPos)) {
							cellPtr->clearFlag(CellFlags::IntWallZPos);
							cellPtr->setFlag(CellFlags::IntWindowZPos);
						}
						break;
					case 2: // X-
						if (cellPtr->hasFlag(CellFlags::IntWallXNeg)) {
							cellPtr->clearFlag(CellFlags::IntWallXNeg);
							cellPtr->setFlag(CellFlags::IntWindowXNeg);
						}
						break;
					case 3: // X+
						if (cellPtr->hasFlag(CellFlags::IntWallXPos)) {
							cellPtr->clearFlag(CellFlags::IntWallXPos);
							cellPtr->setFlag(CellFlags::IntWindowXPos);
						}
						break;
				}
			}
		}
	}

	grid.markDirty();
}

CellGridComponent* RoomLayoutSystem::findParentGrid(flecs::entity entity)
{
	flecs::entity current = entity;
	int safety = 0;
	
	while (current.is_alive() && safety < 100) {
		if (current.has<CellGridComponent>()) {
			// get_mut returns a reference, convert to pointer
			return &current.get_mut<CellGridComponent>();
		}
		current = current.parent();
		safety++;
	}
	
	return nullptr;
}

flecs::entity RoomLayoutSystem::findRoomAt(flecs::entity gridEntity, int x, int y, int z)
{
	flecs::entity result = flecs::entity::null();
	gridEntity.children([&](flecs::entity child) {
		if (child.has<RoomComponent>()) {
			const auto& room = child.get<RoomComponent>();
			if (room.contains(x, y, z)) {
				result = child;
				return false;
			}
		}
		return true;
	});
	return result;
}

std::vector<std::pair<int, int>> RoomLayoutSystem::getRoomEdgeCells(const RoomComponent& room, int side)
{
	std::vector<std::pair<int, int>> cells;

	switch (side) {
		case 0:
			for (int x = room.minX; x < room.maxX; x++) {
				cells.push_back({ x, room.minZ });
			}
			break;
		case 1:
			for (int x = room.minX; x < room.maxX; x++) {
				cells.push_back({ x, room.maxZ - 1 });
			}
			break;
		case 2:
			for (int z = room.minZ; z < room.maxZ; z++) {
				cells.push_back({ room.minX, z });
			}
			break;
		case 3:
			for (int z = room.minZ; z < room.maxZ; z++) {
				cells.push_back({ room.maxX - 1, z });
			}
			break;
	}

	return cells;
}

std::vector<std::pair<int, int>> RoomLayoutSystem::getAllEdgeCells(const RoomComponent& room)
{
	// This mirrors the original Lua roomEdge() function
	// Returns ALL cells on the perimeter of the room (all 4 sides)
	std::vector<std::pair<int, int>> cells;
	
	int sizeX = room.getSizeX();
	int sizeZ = room.getSizeZ();
	
	// Handle edge cases (1D rooms)
	if (sizeX <= 0 || sizeZ <= 0) {
		return cells;
	} else if (sizeX == 1 && sizeZ == 1) {
		// Single cell room
		cells.push_back({ room.minX, room.minZ });
		return cells;
	} else if (sizeZ == 1) {
		// Single row
		for (int x = 0; x < sizeX; x++) {
			cells.push_back({ room.minX + x, room.minZ });
		}
		return cells;
	} else if (sizeX == 1) {
		// Single column
		for (int z = 0; z < sizeZ; z++) {
			cells.push_back({ room.minX, room.minZ + z });
		}
		return cells;
	}
	
	// Normal 2D room - collect all perimeter cells
	// Use a set to avoid duplicates at corners
	std::set<std::pair<int, int>> seen;
	
	// Bottom edge (Z-)
	for (int x = 0; x < sizeX; x++) {
		seen.insert({ room.minX + x, room.minZ });
	}
	// Left edge (X-)
	for (int z = 0; z < sizeZ; z++) {
		seen.insert({ room.minX, room.minZ + z });
	}
	// Top edge (Z+)
	for (int x = 0; x < sizeX; x++) {
		seen.insert({ room.minX + x, room.maxZ - 1 });
	}
	// Right edge (X+)
	for (int z = 0; z < sizeZ; z++) {
		seen.insert({ room.maxX - 1, room.minZ + z });
	}
	
	// Copy from set to vector
	for (const auto& cell : seen) {
		cells.push_back(cell);
	}
	
	return cells;
}

bool RoomLayoutSystem::areCellsAdjacent(int x1, int z1, int x2, int z2)
{
	int dx = std::abs(x1 - x2);
	int dz = std::abs(z1 - z2);
	return (dx == 0 && dz == 1) || (dx == 1 && dz == 0);
}

void RoomLayoutSystem::findAdjacentCells(const RoomComponent& roomA, const RoomComponent& roomB,
                                         std::vector<std::pair<int, int>>& outCellsA,
                                         std::vector<std::pair<int, int>>& outCellsB)
{
	// This mirrors the original Lua adjacentCells() function
	// Get ALL edge cells from both rooms (all 4 sides), then find adjacent pairs
	// We use a set to collect all unique edge cells (like the original)
	std::vector<std::pair<int, int>> edgeA = getAllEdgeCells(roomA);
	std::vector<std::pair<int, int>> edgeB = getAllEdgeCells(roomB);
	
	// Find all pairs of adjacent cells between the two rooms
	for (const auto& cellA : edgeA) {
		for (const auto& cellB : edgeB) {
			if (areCellsAdjacent(cellA.first, cellA.second, cellB.first, cellB.second)) {
				outCellsA.push_back(cellA);
				outCellsB.push_back(cellB);
			}
		}
	}
}

void RoomLayoutSystem::setWallFlags(CellGridComponent& grid, int x, int y, int z, 
                                    int side, bool isExternal, bool isDoor, bool isWindow)
{
	Cell* cell = grid.findCell(x, y, z);
	if (!cell) return;

	uint64_t wallFlag = 0;
	uint64_t doorFlag = 0;
	uint64_t windowFlag = 0;

	if (isExternal) {
		switch (side) {
			case 0: wallFlag = CellFlags::WallZNeg; doorFlag = CellFlags::DoorZNeg; windowFlag = CellFlags::WindowZNeg; break;
			case 1: wallFlag = CellFlags::WallZPos; doorFlag = CellFlags::DoorZPos; windowFlag = CellFlags::WindowZPos; break;
			case 2: wallFlag = CellFlags::WallXNeg; doorFlag = CellFlags::DoorXNeg; windowFlag = CellFlags::WindowXNeg; break;
			case 3: wallFlag = CellFlags::WallXPos; doorFlag = CellFlags::DoorXPos; windowFlag = CellFlags::WindowXPos; break;
		}
	} else {
		switch (side) {
			case 0: wallFlag = CellFlags::IntWallZNeg; doorFlag = CellFlags::IntDoorZNeg; windowFlag = CellFlags::IntWindowZNeg; break;
			case 1: wallFlag = CellFlags::IntWallZPos; doorFlag = CellFlags::IntDoorZPos; windowFlag = CellFlags::IntWindowZPos; break;
			case 2: wallFlag = CellFlags::IntWallXNeg; doorFlag = CellFlags::IntDoorXNeg; windowFlag = CellFlags::IntWindowXNeg; break;
			case 3: wallFlag = CellFlags::IntWallXPos; doorFlag = CellFlags::IntDoorXPos; windowFlag = CellFlags::IntWindowXPos; break;
		}
	}

	if (isDoor) {
		cell->setFlag(doorFlag);
	} else if (isWindow) {
		cell->setFlag(windowFlag);
	} else {
		cell->setFlag(wallFlag);
	}
}

void RoomLayoutSystem::clearWallFlags(CellGridComponent& grid, int x, int y, int z, int side)
{
	Cell* cell = grid.findCell(x, y, z);
	if (!cell) return;

	uint64_t flagsToClear = 0;

	switch (side) {
		case 0:
			flagsToClear = CellFlags::WallZNeg | CellFlags::DoorZNeg | CellFlags::WindowZNeg |
			               CellFlags::IntWallZNeg | CellFlags::IntDoorZNeg | CellFlags::IntWindowZNeg;
			break;
		case 1:
			flagsToClear = CellFlags::WallZPos | CellFlags::DoorZPos | CellFlags::WindowZPos |
			               CellFlags::IntWallZPos | CellFlags::IntDoorZPos | CellFlags::IntWindowZPos;
			break;
		case 2:
			flagsToClear = CellFlags::WallXNeg | CellFlags::DoorXNeg | CellFlags::WindowXNeg |
			               CellFlags::IntWallXNeg | CellFlags::IntDoorXNeg | CellFlags::IntWindowXNeg;
			break;
		case 3:
			flagsToClear = CellFlags::WallXPos | CellFlags::DoorXPos | CellFlags::WindowXPos |
			               CellFlags::IntWallXPos | CellFlags::IntDoorXPos | CellFlags::IntWindowXPos;
			break;
	}

	cell->flags &= ~flagsToClear;
}

void RoomLayoutSystem::generateRoomFurniture(RoomComponent& room,
					     CellGridComponent& grid)
{
	if (room.tags.empty())
		return;

	std::mt19937 rng(room.furnitureSeed);

	// Clear existing furniture in room bounds
	for (auto it = grid.furnitureCells.begin();
	     it != grid.furnitureCells.end();) {
		if (it->x >= room.minX && it->x < room.maxX &&
		    it->y >= room.minY && it->y < room.maxY &&
		    it->z >= room.minZ && it->z < room.maxZ) {
			it = grid.furnitureCells.erase(it);
		} else {
			++it;
		}
	}

	int midX = (room.minX + room.maxX - 1) / 2;
	int midZ = (room.minZ + room.maxZ - 1) / 2;

	// Side: 0=Z- (minZ), 1=Z+ (maxZ), 2=X- (minX), 3=X+ (maxX)
	const int ROTATION_BY_SIDE[4] = { 2, 0, 3, 1 };
	const uint64_t WALL_FLAGS_BY_SIDE[4] = {
		CellFlags::IntWallZNeg | CellFlags::WallZNeg,
		CellFlags::IntWallZPos | CellFlags::WallZPos,
		CellFlags::IntWallXNeg | CellFlags::WallXNeg,
		CellFlags::IntWallXPos | CellFlags::WallXPos
	};
	const uint64_t WINDOW_FLAGS_BY_SIDE[4] = {
		CellFlags::IntWindowZNeg | CellFlags::WindowZNeg,
		CellFlags::IntWindowZPos | CellFlags::WindowZPos,
		CellFlags::IntWindowXNeg | CellFlags::WindowXNeg,
		CellFlags::IntWindowXPos | CellFlags::WindowXPos
	};

	auto cellHasAnyDoor = [&](int x, int y, int z) -> bool {
		const Cell *cell = grid.findCell(x, y, z);
		if (!cell)
			return false;
		return (cell->flags & CellFlags::AllDoors) != 0;
	};
	auto cellHasAnyWindow = [&](int x, int y, int z) -> bool {
		const Cell *cell = grid.findCell(x, y, z);
		if (!cell)
			return false;
		return (cell->flags & CellFlags::AllWindows) != 0;
	};

	auto canPlaceFurniture = [&](const FurnitureDefinition *def,
				     int x, int y, int z) -> bool {
		if (!def)
			return false;
		if (cellHasAnyDoor(x, y, z)) {
			if (std::find(def->tags.begin(), def->tags.end(),
				      "door") == def->tags.end())
				return false;
		}
		if (cellHasAnyWindow(x, y, z)) {
			if (std::find(def->tags.begin(), def->tags.end(),
				      "nowindow") != def->tags.end())
				return false;
		}
		return true;
	};

	auto tryPlace = [&](int cx, int cy, int cz, int side,
			    const std::vector<std::string> &tags,
			    const std::vector<std::string> &notags,
			    bool onWindow,
			    std::mt19937 &rng) -> const FurnitureDefinition * {
		if (grid.findFurnitureCell(cx, cy, cz))
			return nullptr;
		const Cell *cell = grid.findCell(cx, cy, cz);
		if (!cell)
			return nullptr;
		uint64_t sideFlags = onWindow ? WINDOW_FLAGS_BY_SIDE[side] :
						    WALL_FLAGS_BY_SIDE[side];
		if ((cell->flags & sideFlags) == 0)
			return nullptr;
		auto *def = FurnitureLibrary::getInstance().selectByTags(
			tags, rng, notags);
		if (!def)
			return nullptr;
		if (!canPlaceFurniture(def, cx, cy, cz))
			return nullptr;
		return def;
	};

	auto placeOne = [&](int cx, int cy, int cz, int side,
			    const std::vector<std::string> &tags,
			    const std::vector<std::string> &fallbackTags,
			    const std::vector<std::string> &notags,
			    bool onWindow,
			    std::mt19937 &rng) -> bool {
		auto *def = tryPlace(cx, cy, cz, side, tags, notags,
				     onWindow, rng);
		if (!def && !fallbackTags.empty()) {
			def = tryPlace(cx, cy, cz, side, fallbackTags,
				       notags, onWindow, rng);
		}
		if (!def)
			return false;
		auto &fcell = grid.getOrCreateFurnitureCell(cx, cy, cz);
		fcell.furnitureType = def->name;
		fcell.rotation = ROTATION_BY_SIDE[side];
		fcell.tags = tags;
		return true;
	};

	std::vector<std::string> baseTags = room.tags;
	std::vector<std::string> etags = baseTags;
	std::vector<std::string> itags = baseTags;
	std::vector<std::string> otags = baseTags;
	std::vector<std::string> ftags = baseTags;
	etags.push_back("essential");
	itags.push_back("important");
	otags.push_back("optional");
	ftags.push_back("filler");

	struct TagData {
		std::vector<std::string> tags;
		std::vector<std::string> fallbackTags;
		std::vector<std::string> notagsWalls;
		std::vector<std::string> notagsWindows;
		bool essential;
		bool filler;
	};
	std::vector<TagData> taglist = {
		{ etags, {}, { "nowall", "nowindow" }, { "nowindow" }, true,
		  false },
		{ itags, {}, { "nowall", "nowindow" }, { "nowindow" }, false,
		  false },
		{ otags, {}, { "nowall", "nowindow" }, { "nowindow" }, false,
		  false },
		{ ftags,
		  room.fillRoomWithFurniture ? baseTags :
					     std::vector<std::string>(),
		  { "nowall", "nowindow" },
		  { "nowindow" }, false, true },
	};

	// Midpoints for each side - original has 8 entries (each wall twice)
	std::vector<std::tuple<int, int, int>> mpoints = {
		{ midX, room.minY, room.minZ },	      // side 0 (Z-)
		{ midX, room.minY, room.maxZ - 1 },   // side 1 (Z+)
		{ room.minX, room.minY, midZ },	      // side 2 (X-)
		{ room.maxX - 1, room.minY, midZ },   // side 3 (X+)
		{ midX, room.minY, room.minZ },	      // side 0 again
		{ midX, room.minY, room.maxZ - 1 },   // side 1 again
		{ room.minX, room.minY, midZ },	      // side 2 again
		{ room.maxX - 1, room.minY, midZ },   // side 3 again
	};

	for (const auto &mtags : taglist) {
		bool placed = false;

		// Try wall midpoints
		for (auto &p : mpoints) {
			int cx = std::get<0>(p);
			int cy = std::get<1>(p);
			int cz = std::get<2>(p);
			int side = -1;
			if (cz == room.minZ)
				side = 0;
			else if (cz == room.maxZ - 1)
				side = 1;
			else if (cx == room.minX)
				side = 2;
			else if (cx == room.maxX - 1)
				side = 3;
			if (side < 0)
				continue;
			if (!cellHasAnyDoor(cx, cy, cz)) {
							placed = placeOne(cx, cy, cz, side,
									  mtags.tags,
									  mtags.fallbackTags,
									  mtags.notagsWalls,
									  false,
									  rng);
				if (placed && !mtags.filler)
					break;
			}
			if (mtags.filler)
				placed = false;
		}

		// Try wall ranges (all edge cells on each side)
		if (!placed || mtags.filler) {
			for (int side = 0; side < 4; ++side) {
				auto edgeCells = getRoomEdgeCells(room, side);
				for (const auto &cell : edgeCells) {
					int cx = cell.first;
					int cz = cell.second;
					int cy = room.minY;
					if (!cellHasAnyDoor(cx, cy, cz)) {
						placed = placeOne(cx, cy, cz, side,
								  mtags.tags,
								  mtags.fallbackTags,
								  mtags.notagsWalls,
								  false,
								  rng);
						if (placed && !mtags.filler)
							break;
					}
					if (mtags.filler)
						placed = false;
				}
				if (placed && !mtags.filler)
					break;
			}
		}

		if (mtags.filler)
			placed = false;

		// Try window midpoints
		if (!placed || mtags.filler) {
			for (auto &p : mpoints) {
				int cx = std::get<0>(p);
				int cy = std::get<1>(p);
				int cz = std::get<2>(p);
				int side = -1;
				if (cz == room.minZ)
					side = 0;
				else if (cz == room.maxZ - 1)
					side = 1;
				else if (cx == room.minX)
					side = 2;
				else if (cx == room.maxX - 1)
					side = 3;
				if (side < 0)
					continue;
				if (!cellHasAnyDoor(cx, cy, cz)) {
					placed = placeOne(cx, cy, cz, side,
							  mtags.tags,
							  mtags.fallbackTags,
							  mtags.notagsWindows,
							  true,
							  rng);
					if (placed && !mtags.filler)
						break;
				}
				if (mtags.filler)
					placed = false;
			}
		}

		// Try window ranges
		if (!placed || mtags.filler) {
			for (int side = 0; side < 4; ++side) {
				auto edgeCells = getRoomEdgeCells(room, side);
				for (const auto &cell : edgeCells) {
					int cx = cell.first;
					int cz = cell.second;
					int cy = room.minY;
					if (!cellHasAnyDoor(cx, cy, cz)) {
						placed = placeOne(cx, cy, cz, side,
								  mtags.tags,
								  mtags.fallbackTags,
								  mtags.notagsWindows,
								  true,
								  rng);
						if (placed && !mtags.filler)
							break;
					}
					if (mtags.filler)
						placed = false;
				}
				if (placed && !mtags.filler)
					break;
			}
		}

		// Only break if essential was not placed.
		// For important/optional/filler, continue to next stage
		// even if nothing was placed.
		if (!placed && mtags.essential) {
			break;
		}
	}

	grid.markDirty();
}
