#include <Machine/MQuartzClock/MQuartzClock.hpp>
#include <MachineComponent/DataCableComponent/DataCableComponent.hpp>
#include <Serialize/Serialize.hpp>
#include <Item/IEnergyContainingItem.hpp>
#include <World/LocalWorld/LocalWorld.hpp>
#include <MachineComponent/ElectricComponent/ElectricComponent.hpp>

ItemStack& MQuartzClock::energySlot()
{
    return stacks[0];
}

Side MQuartzClock::getOutputSide()
{
    return Side(*m_world.getBlockMeta(pos));
}

MQuartzClock::MQuartzClock(LocalWorld &world, glm::ivec3 pos) :
    Machine(world, pos),
    stacks(1)
{
    attach<ElectricComponent>();
    attach<DataCableComponent>();
}

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

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

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

void MQuartzClock::saveMainConfig(MachineConfig &outConfig)
{
    outConfig.blockName = "main:quartzClock";
    outConfig.label = "Quartz clock";

    serializeInto(outConfig.components[""], periods);
}

void MQuartzClock::loadMainConfig(const MachineConfig &config, bool consumed)
{
    const auto &data = config.components.at("");
    deserialize(data.data(), data.size(), periods);
}

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

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

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

bool MQuartzClock::isAllowedStackInSlot(int slot, ItemStack stack)
{
    return true;
}

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

    electricComp.rechargeFrom(energySlot(), energyCapacity);
    electricComp.rechargeFrom(Side::Bottom, energyCapacity);

    if (electricComp.getEnergy() >= powerConsumption)
    {
        electricComp.drainEnergy(powerConsumption);
    }
}

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

Side MQuartzClock::getDataInputSide()
{
    return getOutputSide();
}

DataLink::ChannelBitmap MQuartzClock::getActiveChannels()
{
    ElectricComponent& electricComp = getComponent<ElectricComponent>();

    if (electricComp.getEnergy() < powerConsumption)
    {
        return {};
    }

    DataLink::ChannelBitmap bitmap;
    for (size_t i = 0; i < 4; i++)
    {
        uint64_t period = periods[i];
        if (period == 0)
        {
            continue;
        }

        uint64_t offset = m_world.getWorldTime() % (2 * period);
        if (offset < period)
        {
            bitmap += static_cast<DataLink::Channel>(i);
        }
    }

    return bitmap;
}