#include <Machine/MBarrel/MBarrel.hpp>
#include <Item/IFluidContainingItem.hpp>
#include <Utilities/Utilities.hpp>
#include <FluidRegistry/FluidRegistry.hpp>
#include <Block/SpriteBlock/Barrel/Barrel.hpp>
#include <MachineComponent/FluidComponent/FluidComponent.hpp>

bool MBarrel::canStoreFluid(std::string name)
{
    Fluid *fluid = fluidRegistry.getFluidByName(name);
    Barrel *barrel = dynamic_cast<Barrel*>(m_world.getBlockAt(pos));

    if (barrel == nullptr)
    {
        return false;
    }

    return barrel->tier >= fluid->minBarrelTier;
}

MBarrel::MBarrel(LocalWorld &world, glm::ivec3 pos) :
    Machine(world, pos),
    stacks(2)
{
    attach<FluidComponent>();    
}

std::string MBarrel::getFluidName()
{
    return getComponent<FluidComponent>().getDefaultFluidOnSide(Side::Top).value_or("");
}

int MBarrel::getFluidAmount()
{
    return getComponent<FluidComponent>().getFluidAmount(getFluidName());
}

void MBarrel::serializeState(std::vector<uint8_t> &outBytes)
{
    serializeInto(outBytes, stacks);
}

void MBarrel::deserializeState(const uint8_t *data, size_t size)
{
    deserialize(data, size, stacks);
}

int MBarrel::getNumSlots()
{
    return (int) stacks.size();
}

ItemStack MBarrel::getStackInSlot(int slot)
{
    return stacks[slot];
}

void MBarrel::setStackInSlot(int slot, ItemStack stack)
{
    stacks[slot] = stack;
}

bool MBarrel::isAllowedStackInSlot(int slot, ItemStack stack)
{
    if (stack.isEmpty())
    {
        return true;
    }

    return stack.getItemAs<IFluidContainingItem>() != nullptr;
}

void MBarrel::update()
{
    FluidComponent &fluidComponent = getComponent<FluidComponent>();
    fluidComponent.refillFrom(stacks[0]);
    fluidComponent.drainInto(stacks[1], getFluidName());
}

bool MBarrel::canConnectFluidPipe(Side side)
{
    // Pipes can only be connected at the top.
    return side == Side::Top;
}

int MBarrel::getMaxFluidToAccept(std::optional<Side> side, std::string fluidName)
{
    FluidComponent &fluidComponent = getComponent<FluidComponent>();

    if (side.has_value() && *side != Side::Top)
    {
        return 0;
    }

    if (!canStoreFluid(fluidName))
    {
        return 0;
    }

    std::optional<std::string> currentFluidName = fluidComponent.getDefaultFluidOnSide(Side::Top);
    if (!currentFluidName.has_value())
    {
        currentFluidName = fluidName;
        fluidComponent.setDefaultFluidOnSide(Side::Top, currentFluidName);
    }

    if (currentFluidName != fluidName)
    {
        return 0;
    }

    return capacity - fluidComponent.getFluidAmount(fluidName);
}

int MBarrel::getMaxFluidToRelease(std::optional<Side> side, std::string fluidName)
{
    if (side.has_value() && side != Side::Top)
    {
        return 0;
    }

    return INT_MAX;
}