#include <Block/SpriteBlock/Slab/Slab.hpp>
#include <Item/StatefulItem/BlockItem/BlockItem.hpp>
#include <World/LocalWorld/LocalWorld.hpp>

std::vector<Slab::Sprite> Slab::makeSprites(SpriteBlock &base)
{
    std::vector<Sprite> sprites;

    const Sprite &templSprite = base.sprites[0];
    for (uint8_t meta = 0; meta <= metaFull; meta++)
    {
        Sprite sprite = templSprite;

        if (meta != metaFull)
        {
            Side filledSide(meta);
            sprite.segments[0].getSideValue(filledSide.opposite()) = 4;
            sprite.stateName = formatString("filled$+", filledSide);
        }
        else
        {
            sprite.stateName = "full";
        }

        sprites.push_back(sprite);
    }

    return sprites;
}

Slab::Slab(std::string name, SpriteBlock &base) :
    SpriteBlock(
        name,
        base.displayName + " Slab",
        base.hardness,
        base.iconName,
        makeSprites(base)
    ),
    base(base)
{}

bool Slab::canManuallyDig()
{
    return base.canManuallyDig();
}

void Slab::onInteract(LocalWorld &world, Player &player, glm::ivec3 pos, Side side, const RGBABytes &collisionKey)
{
    ItemStack &holdingStack = player.holdingStack();
    BlockItem *blockItem = holdingStack.getItemAs<BlockItem>();

    if (blockItem == nullptr)
    {
        SpriteBlock::onInteract(world, player, pos, side, collisionKey);
        return;
    }

    Block &holdingBlock = blockItem->getBlockInStack(holdingStack);
    if (holdingBlock.name != name)
    {
        SpriteBlock::onInteract(world, player, pos, side, collisionKey);
        return;
    }

    uint8_t meta = *world.getBlockMeta(pos);
    if (meta == side.opposite().asNumber())
    {
        holdingStack.extractOne();
        world.setBlockMeta(pos, metaFull);
        return;
    }

    SpriteBlock::onInteract(world, player, pos, side, collisionKey);
}

bool Slab::canPlace(LocalWorld &world, IWorldActor &actor, glm::ivec3 pos, Side side, uint8_t &meta)
{
    meta = side.asNumber();
    return true;
}

void Slab::getHarvestedStacks(LocalWorld &world, glm::ivec3 pos, std::vector<ItemStack> &stacks)
{
    uint8_t meta = *world.getBlockMeta(pos);
    stacks.push_back(toItemStack(meta == metaFull ? 2 : 1));
}

void Slab::onDesignerInitializing(BlockSpriteDesigner &designer, ScriptEngine &engine)
{
    withSoundsFrom(base);

    engine.push(designer.addSprite(std::make_shared<VoxelSprite>(base.generatedSprites[0]->frames[0].colorMap)));
    engine.setGlobal("SLAB_BASE_COLORMAP");
    engine.push(designer.addSprite(std::make_shared<VoxelSprite>(base.generatedSprites[0]->frames[0].biasMap)));
    engine.setGlobal("SLAB_BASE_BIASMAP");
    engine.push(designer.addSprite(std::make_shared<VoxelSprite>(base.generatedSprites[0]->frames[0].refractMap)));
    engine.setGlobal("SLAB_BASE_REFRACTMAP");
    engine.push(designer.addSprite(std::make_shared<VoxelSprite>(base.generatedSprites[0]->frames[0].lightSpecMap)));
    engine.setGlobal("SLAB_BASE_LIGHTSPECMAP");
    engine.push(designer.addSprite(std::make_shared<VoxelSprite>(base.generatedSprites[0]->collisionMask)));
    engine.setGlobal("SLAB_BASE_COLLISION_MASK");

    designer.useFallbackScript("slab.lua");
}