#include <Machine/MAccumulator/MAccumulator.hpp>
#include <MachineComponent/AttributionComponent/AttributionComponent.hpp>
#include <Milestone/ElectronicsMilestone/ElectronicsMilestone.hpp>
#include <Serialize/Serialize.hpp>
#include <Item/IEnergyContainingItem.hpp>
#include <World/LocalWorld/LocalWorld.hpp>
#include <MachineComponent/ElectricComponent/ElectricComponent.hpp>

ItemStack& MAccumulator::inputSlot()
{
    return stacks[0];
}

ItemStack& MAccumulator::outputSlot()
{
    return stacks[1];
}

Side MAccumulator::getInputSide()
{
    return Side(*m_world.getBlockMeta(pos));
}

MAccumulator::MAccumulator(LocalWorld &world, glm::ivec3 pos) :
    Machine(world, pos),
    stacks(2)
{
    attach<AttributionComponent>();
    attach<ElectricComponent>();
}

int MAccumulator::getEnergy()
{
    return getComponent<ElectricComponent>().getEnergy();
}

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

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

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

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

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

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

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

void MAccumulator::update()
{
    ElectricComponent& electricComp = getComponent<ElectricComponent>();

    electricComp.rechargeFrom(inputSlot(), capacity);
    electricComp.rechargeFrom(getInputSide(), capacity);
    electricComp.drainEnergyInto(outputSlot());

    if (electricComp.getEnergy() == capacity)
    {
        getComponent<AttributionComponent>().grantAchievementFor(
            std::string(Machine::Action::Placed),
            ElectronicsMilestone::ID, ElectronicsMilestone::FullyChargeAccumulator
        );
    }
}

bool MAccumulator::canConnectElectricWire(Side side)
{
    return true;
}

bool MAccumulator::canDrainEnergyFromSide(Side side)
{
    return side != getInputSide();
}