#include <Block/SpriteBlock/Door/Door.hpp>
#include <World/LocalWorld/LocalWorld.hpp>

std::vector<Door::Sprite> Door::makeSprites()
{
    static Side openingSides[4] = {Side::South, Side::North, Side::East, Side::West};

    std::vector<Sprite> sprites;

    for (uint8_t meta = 0; meta < numStates; meta++)
    {
        int segment = metaGetSegment(meta);
        if (segment == 3)
        {
            sprites.push_back({});
            continue;
        }

        Side closingSide = metaGetClosingSide(meta);
        const char *closingStateLabel = metaIsOpen(meta) ? "open" : "closed";

        BoundingBox<int> bounds = {0, 0, 0, 8, 8, 8};

        Side currentSide = metaIsOpen(meta) ? openingSides[closingSide.asNumber()] : closingSide;
        if (currentSide.sign() == -1)
        {
            bounds.getSideValue(currentSide.opposite()) = 3;
        }
        else
        {
            bounds.getSideValue(currentSide.opposite()) = 5;
        }

        Sprite sprite = {
            {"door", "door", "door", "door", "door", "door"},
            {bounds},
            formatString("$+$+$+", closingStateLabel, closingSide, segment),
        };

        sprites.push_back(sprite);
    }

    return sprites;
}

Door::Door(std::string name) :
    SpriteBlock(
        name,
        "Oak door",
        90.0f,
        "oakDoor",
        makeSprites()
    )
{
    withWoodSounds();
}

bool Door::canManuallyDig()
{
    return true;
}

bool Door::canPlace(LocalWorld &world, IWorldActor &actor, glm::ivec3 pos, Side side, uint8_t &meta)
{
    meta = actor.getSideFacing2D().asNumber() << 2;

    for (int i = 0; i < 3; i++)
    {
        glm::ivec3 offset = glm::ivec3(0, 0, i);
        if (!world.getBlockAt(pos + offset)->canReplaceWithPlacedBlock())
        {
            return false;
        }
    }

    for (int i = 1; i < 3; i++)
    {
        glm::ivec3 offset = glm::ivec3(0, 0, i);
        world.setBlockAt(pos + offset, name, meta | i);
    }

    return true;
}

void Door::onRemoved(LocalWorld &world, Chunk &chunk, glm::ivec3 pos, uint8_t meta)
{
    pos.z -= metaGetSegment(meta);
    int end = pos.z + 3;
    for (; pos.z < end; pos.z++)
    {
        if (world.getBlockAt(pos)->name == name)
        {
            world.setBlockAt(pos, 0);
        }
    }
}

void Door::onInteract(LocalWorld &world, Player &player, glm::ivec3 pos, Side side, const RGBABytes &collisionKey)
{
    pos.z -= metaGetSegment(*world.getBlockMeta(pos));
    int end = pos.z + 3;
    for (; pos.z < end; pos.z++)
    {
        world.setBlockMeta(pos, *world.getBlockMeta(pos) ^ metaOpen);
    }
}

bool Door::isIntersectedBy(World &world, glm::ivec3 pos, const BoundingBox<wfixed> &box)
{
    if (metaIsOpen(*world.getBlockMeta(pos)))
    {
        return false;
    }

    return Block::isIntersectedBy(world, pos, box);
}