#include <Item/StatefulItem/Battery/Battery.hpp>
#include <Milestone/ElectronicsMilestone/ElectronicsMilestone.hpp>
#include <Model/Model.hpp>
#include <FluidRegistry/FluidRegistry.hpp>
#include <Utilities/Utilities.hpp>
#include <Block/FluidBlock/FluidBlock.hpp>
#include <BlockRegistry/BlockRegistry.hpp>
#include <Block/AirBlock/AirBlock.hpp>
#include <Display/WidgetDisplay/WorldDisplay/WorldDisplay.hpp>
#include <MachineComponent/MultiBlockComponent/MultiBlockComponent.hpp>

Battery::Battery(std::string name) : StatefulItem(name)
{
    setMaxStackSize(1);

    addOption<int>("energy", [this](ItemStack &stack, const int &energy) -> std::string
    {
        if (energy < 0 || energy > capacity)
        {
            return formatString("Energy must be between 0 and $+", capacity);
        }

        state(stack) = energy;
        return "";
    });
}

void Battery::draw(GraphicsContext &context, int x, int y, const ItemStack &stack)
{
    context.color = glm::vec4(1.0f);
    context.drawMaterial(Material::battery, x, y);

    int glowHeight = 8 * state(stack) / capacity;
    context.drawMaterial(Material::chargedBattery, 0, 8-glowHeight, 8, glowHeight, x, y+8-glowHeight, 8, glowHeight);
}

std::string Battery::getDisplayName(const ItemStack &stack)
{
    return "Battery";
}

void Battery::draw3D(GraphicsContext &context, WorldDisplay &worldDisplay, glm::vec3 pos, ItemStack &stack)
{
    worldDisplay.drawMeshedIcon(context, Material::battery, pos);
}

void Battery::drawFlat(GraphicsContext &context, WorldDisplay &worldDisplay, glm::mat4 matrix, ItemStack &stack)
{
    worldDisplay.drawMeshedIconFlat(context, Material::battery, matrix);
}

void Battery::getExtraToolTipLines(const ItemStack &stack, ToolTip &tip)
{
    tip.addLine(formatString("$+/$+ J", state(stack), capacity));
}

bool Battery::canMerge(const ItemStack &a, const ItemStack &b)
{
    return a.getItem() == b.getItem() && state(a) == state(b);
}

int Battery::pullEnergy(ItemStack &stack, int energy)
{
    int &storedEnergy = state(stack);
    if (energy > storedEnergy)
    {
        energy = storedEnergy;
    }

    storedEnergy -= energy;
    return energy;
}

int Battery::pushEnergy(ItemStack &stack, int energy)
{
    int &storedEnergy = state(stack);
    int maxToPush = capacity - storedEnergy;

    if (energy > maxToPush)
    {
        energy = maxToPush;
    }

    storedEnergy += energy;
    return energy;
}

void Battery::describeInChemicalReactor(ItemStack &stack, std::ostream &os)
{
    os << "Powers electric components in reactor." << std::endl;
    os << "Automatically recharges from energy input." << std::endl;
}

void Battery::updateChemicalReactor(ItemStack &stack, MChemicalReactor &reactor)
{
    std::shared_ptr<ElectricLink> energyInput = reactor.getComponent<MultiBlockComponent>().getEnergyInput();
    
    if (energyInput != nullptr)
    {
        int &storedEnergy = state(stack);
        int maxToDrain = capacity - storedEnergy;

        if (maxToDrain > 0)
        {
            storedEnergy += energyInput->drainEnergy(maxToDrain);
        }
    }
}

void Battery::onMovedBy(Player &player, const ItemStack &stack)
{
    StatefulItem::onMovedBy(player, stack);

    const int &storedEnergy = state(stack);
    if (storedEnergy > 0)
    {
        player.achieve(ElectronicsMilestone::ID, ElectronicsMilestone::ChargeBattery);
    }
}

void Battery::getCheatStacks(ItemStack &baseStack, std::vector<ItemStack> &outStacks)
{
    outStacks.push_back(baseStack);

    ItemStack fullStack = baseStack;
    state(fullStack) = capacity;
    outStacks.push_back(fullStack);
}