#include <Item/StatefulItem/BlockItem/BlockItem.hpp>
#include <Machine/MBlockPlacer/MBlockPlacer.hpp>
#include <MachineComponent/ElectricComponent/ElectricComponent.hpp>
#include <World/LocalWorld/LocalWorld.hpp>
#include <Block/SpriteBlock/BlockPlacer/BlockPlacer.hpp>
#include <Block/SpriteBlock/ConveyorBelt/ConveyorBelt.hpp>
#include <MachineComponent/BeltComponent/BeltComponent.hpp>
#include <Belt/Belt.hpp>

Side MBlockPlacer::getPlacingSide()
{
    return Side(*m_world.getBlockMeta(pos));
}

MBlockPlacer::MBlockPlacer(LocalWorld &world, glm::ivec3 pos) : Machine(world, pos)
{
    attach<ElectricComponent>();
    attach<BeltComponent>();
}

void MBlockPlacer::serializeState(std::vector<uint8_t> &outBytes)
{
    // NOP
}

void MBlockPlacer::deserializeState(const uint8_t *data, size_t size)
{
    // NOP
}

bool MBlockPlacer::canConnectElectricWire(Side side)
{
    return side == Side::Bottom;
}

void MBlockPlacer::onItemStackEntering(Side side, ItemStack &stack)
{
    ElectricComponent &electricComp = getComponent<ElectricComponent>();

    auto blockItem = stack.getItemAs<BlockItem>();
    if (blockItem == nullptr)
    {
        return;
    }

    Side placingSide = getPlacingSide();
    glm::ivec3 placingPos = pos + placingSide.out();

    Block *blockInFront = m_world.getBlockAt(placingPos);
    if (blockInFront == nullptr)
    {
        return;
    }

    if (!blockInFront->canReplaceWithPlacedBlock())
    {
        return;
    }

    Block &placingBlock = blockItem->getBlockInStack(stack);
    uint8_t placingMeta = 0;

    if (!placingBlock.canPlace(m_world, *this, placingPos, placingSide.opposite(), placingMeta))
    {
        return;
    }

    electricComp.rechargeFrom(Side::Bottom, energyPerItem);
    if (!electricComp.tryConsume(energyPerItem))
    {
        return;
    }

    m_world.setBlockAt(placingPos, placingBlock.name, placingMeta);
    stack.extractOne();

    m_world.playSound(placingBlock.getPlacingSound(), placingPos);
}

Side MBlockPlacer::getSideFacing2D()
{
    return getPlacingSide().opposite();
}

bool MBlockPlacer::isUsingItemDirectly()
{
    return false;
}