#include "CellGrid.hpp"
#include <algorithm>

Cell* CellGridComponent::findCell(int x, int y, int z)
{
    int64_t key = ((int64_t)x + 2048 * (int64_t)y + 2048 * 2048 * (int64_t)z);
    for (auto& cell : cells) {
        if (cell.getKey() == key) {
            return &cell;
        }
    }
    return nullptr;
}

const Cell* CellGridComponent::findCell(int x, int y, int z) const
{
    int64_t key = ((int64_t)x + 2048 * (int64_t)y + 2048 * 2048 * (int64_t)z);
    for (const auto& cell : cells) {
        if (cell.getKey() == key) {
            return &cell;
        }
    }
    return nullptr;
}

Cell& CellGridComponent::getOrCreateCell(int x, int y, int z)
{
    Cell* existing = findCell(x, y, z);
    if (existing) {
        return *existing;
    }
    Cell newCell;
    newCell.x = x;
    newCell.y = y;
    newCell.z = z;
    cells.push_back(newCell);
    return cells.back();
}

void CellGridComponent::removeCell(int x, int y, int z)
{
    int64_t key = ((int64_t)x + 2048 * (int64_t)y + 2048 * 2048 * (int64_t)z);
    auto it = std::remove_if(cells.begin(), cells.end(),
        [key](const Cell& c) { return c.getKey() == key; });
    cells.erase(it, cells.end());
}

FurnitureCell* CellGridComponent::findFurnitureCell(int x, int y, int z)
{
    int64_t key = ((int64_t)x + 2048 * (int64_t)y + 2048 * 2048 * (int64_t)z);
    for (auto& cell : furnitureCells) {
        if (cell.getKey() == key) {
            return &cell;
        }
    }
    return nullptr;
}

FurnitureCell& CellGridComponent::getOrCreateFurnitureCell(int x, int y, int z)
{
    FurnitureCell* existing = findFurnitureCell(x, y, z);
    if (existing) {
        return *existing;
    }
    FurnitureCell newCell;
    newCell.x = x;
    newCell.y = y;
    newCell.z = z;
    furnitureCells.push_back(newCell);
    return furnitureCells.back();
}

void CellGridComponent::removeFurnitureCell(int x, int y, int z)
{
    int64_t key = ((int64_t)x + 2048 * (int64_t)y + 2048 * 2048 * (int64_t)z);
    auto it = std::remove_if(furnitureCells.begin(), furnitureCells.end(),
        [key](const FurnitureCell& c) { return c.getKey() == key; });
    furnitureCells.erase(it, furnitureCells.end());
}

void CellGridComponent::clear()
{
    cells.clear();
    furnitureCells.clear();
    markDirty();
}

Ogre::Vector3 CellGridComponent::cellToWorld(int x, int y, int z) const
{
    return Ogre::Vector3(
        x * cellSize,
        y * cellHeight,
        z * cellSize
    );
}

void CellGridComponent::worldToCell(const Ogre::Vector3& worldPos, int& x, int& y, int& z) const
{
    x = Ogre::Math::Floor(worldPos.x / cellSize);
    y = Ogre::Math::Floor(worldPos.y / cellHeight);
    z = Ogre::Math::Floor(worldPos.z / cellSize);
}

const char* CellGridComponent::getFlagName(uint64_t flag)
{
    switch (flag) {
        case CellFlags::Floor: return "Floor";
        case CellFlags::Ceiling: return "Ceiling";
        case CellFlags::WallXNeg: return "Ext Wall X-";
        case CellFlags::WallXPos: return "Ext Wall X+";
        case CellFlags::WallZPos: return "Ext Wall Z+";
        case CellFlags::WallZNeg: return "Ext Wall Z-";
        case CellFlags::DoorXNeg: return "Ext Door X-";
        case CellFlags::DoorXPos: return "Ext Door X+";
        case CellFlags::DoorZPos: return "Ext Door Z+";
        case CellFlags::DoorZNeg: return "Ext Door Z-";
        case CellFlags::WindowXNeg: return "Ext Window X-";
        case CellFlags::WindowXPos: return "Ext Window X+";
        case CellFlags::WindowZPos: return "Ext Window Z+";
        case CellFlags::WindowZNeg: return "Ext Window Z-";
        case CellFlags::IntWallXNeg: return "Int Wall X-";
        case CellFlags::IntWallXPos: return "Int Wall X+";
        case CellFlags::IntWallZPos: return "Int Wall Z+";
        case CellFlags::IntWallZNeg: return "Int Wall Z-";
        case CellFlags::IntDoorXNeg: return "Int Door X-";
        case CellFlags::IntDoorXPos: return "Int Door X+";
        case CellFlags::IntDoorZPos: return "Int Door Z+";
        case CellFlags::IntDoorZNeg: return "Int Door Z-";
        case CellFlags::IntWindowXNeg: return "Int Window X-";
        case CellFlags::IntWindowXPos: return "Int Window X+";
        case CellFlags::IntWindowZPos: return "Int Window Z+";
        case CellFlags::IntWindowZNeg: return "Int Window Z-";
        default: return "Unknown";
    }
}

uint64_t CellGridComponent::getFlagByName(const std::string& name)
{
    auto flags = getAllFlags();
    for (const auto& [flag, flagName] : flags) {
        if (name == flagName) {
            return flag;
        }
    }
    return 0;
}

std::vector<std::pair<uint64_t, const char*>> CellGridComponent::getAllFlags()
{
    return {
        { CellFlags::Floor, "Floor" },
        { CellFlags::Ceiling, "Ceiling" },
        { CellFlags::WallXNeg, "Ext Wall X-" },
        { CellFlags::WallXPos, "Ext Wall X+" },
        { CellFlags::WallZPos, "Ext Wall Z+" },
        { CellFlags::WallZNeg, "Ext Wall Z-" },
        { CellFlags::DoorXNeg, "Ext Door X-" },
        { CellFlags::DoorXPos, "Ext Door X+" },
        { CellFlags::DoorZPos, "Ext Door Z+" },
        { CellFlags::DoorZNeg, "Ext Door Z-" },
        { CellFlags::WindowXNeg, "Ext Window X-" },
        { CellFlags::WindowXPos, "Ext Window X+" },
        { CellFlags::WindowZPos, "Ext Window Z+" },
        { CellFlags::WindowZNeg, "Ext Window Z-" },
        { CellFlags::IntWallXNeg, "Int Wall X-" },
        { CellFlags::IntWallXPos, "Int Wall X+" },
        { CellFlags::IntWallZPos, "Int Wall Z+" },
        { CellFlags::IntWallZNeg, "Int Wall Z-" },
        { CellFlags::IntDoorXNeg, "Int Door X-" },
        { CellFlags::IntDoorXPos, "Int Door X+" },
        { CellFlags::IntDoorZPos, "Int Door Z+" },
        { CellFlags::IntDoorZNeg, "Int Door Z-" },
        { CellFlags::IntWindowXNeg, "Int Window X-" },
        { CellFlags::IntWindowXPos, "Int Window X+" },
        { CellFlags::IntWindowZPos, "Int Window Z+" },
        { CellFlags::IntWindowZNeg, "Int Window Z-" },
    };
}
