#include <Color/Color.hpp>
#include <FluidRegistry/FluidRegistry.hpp>
#include <Item/StatefulItem/Jetpack/Jetpack.hpp>
#include <Milestone/DripMilestone/DripMilestone.hpp>
#include <Widget/Builder/Builder.hpp>
#include <Widget/FluidLevelIndicator/FluidLevelIndicator.hpp>
#include <Widget/IconLabel/IconLabel.hpp>
#include <Widget/Layout/Center/Center.hpp>
#include <Widget/Layout/Column/Column.hpp>
#include <Widget/Layout/CompoundWidget/Deck/Deck.hpp>
#include <Widget/Layout/Frame/Frame.hpp>
#include <Widget/Layout/Margin/Margin.hpp>
#include <Widget/Layout/Row/Row.hpp>
#include <Widget/Panel/Panel.hpp>

bool Jetpack::canStoreFluid(const std::string &fluidName)
{
    return fluidName == "main:petrol";
}

Jetpack::Jetpack(std::string name) : StatefulItem(name)
{
    setMaxStackSize(1);
}

int Jetpack::getFluidAmount(const ItemStack &stack)
{
    const JetpackState &jetpackState = state(stack);
    return jetpackState.amount;
}

void Jetpack::draw(GraphicsContext &context, int x, int y, const ItemStack &stack)
{
    const JetpackState &jetpackState = state(stack);

    context.color = glm::vec4(1.0f);
    context.drawMaterial(Material::jetpack, x, y);

    context.color = glm::vec4(0.0f, 0.0f, 0.0f, 1.0f);
    context.drawRect(x+7, y, 1, 8);

    int fuelHeight = 8 * jetpackState.amount / capacity;
    context.color = Color::toolUsesLeftHigh;
    context.drawRect(x+7, y+8-fuelHeight, 1, fuelHeight);
}

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

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

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

void Jetpack::getExtraToolTipLines(const ItemStack &stack, ToolTip &tip)
{
    const JetpackState &jetpackState = state(stack);
    Fluid *fluid = fluidRegistry.getFluidByName(jetpackState.fluidName);

    std::string displayName = fluid == nullptr ? "" : fluid->displayName;
    tip.addLine(formatString("$+/$+ ml $+", jetpackState.amount, capacity, displayName));
}

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

std::string Jetpack::getFluidName(const ItemStack &stack)
{
    return state(stack).fluidName;
}

int Jetpack::pullFluid(ItemStack &stack, std::string fluidName, int maxToPull)
{
    JetpackState &jetpackState = state(stack);
    if (fluidName != jetpackState.fluidName)
    {
        return 0;
    }

    int willPull = jetpackState.amount > maxToPull ? maxToPull : jetpackState.amount;
    jetpackState.amount -= willPull;
    return willPull;
}

int Jetpack::pushFluid(ItemStack &stack, std::string fluidName, int amount)
{
    JetpackState &jetpackState = state(stack);
    if (fluidName != jetpackState.fluidName)
    {
        if (jetpackState.fluidName == "" || jetpackState.amount == 0)
        {
            if (canStoreFluid(fluidName))
            {
                jetpackState.fluidName = fluidName;
            }
            else
            {
                return 0;
            }
        }
        else
        {
            return 0;
        }
    }

    int maxAccept = capacity - jetpackState.amount;
    int willPush = amount > maxAccept ? maxAccept : amount;
    jetpackState.amount += willPush;
    return willPush;
}

float Jetpack::generateThrust(ItemStack &stack)
{
    JetpackState &jetpackState = state(stack);
    if (jetpackState.amount == 0)
    {
        return 0.0f;
    }

    jetpackState.amount--;
    return thrust;
}

void Jetpack::onPlayerUpdating(Player &player, ItemStack &stack)
{
    const JetpackState &constJetpackState = state(static_cast<const ItemStack&>(stack));
    if (constJetpackState.fluidName.empty())
    {
        return;
    }

    if (constJetpackState.amount == capacity)
    {
        return;
    }

    JetpackState &jetpackState = state(stack);
    jetpackState.amount += player.pullFluidFromEquipment(jetpackState.fluidName, capacity - jetpackState.amount);
}

std::string Jetpack::getGadgetControllerID()
{
    return "main:JetpackController";
}

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

    ItemStack fullStack = baseStack;
    JetpackState &jetpackState = state(fullStack);
    jetpackState.fluidName = "main:petrol";
    jetpackState.amount = capacity;
    outStacks.push_back(fullStack);
}

void Jetpack::onEquippedBy(Player &player, const ItemStack &stack)
{
    StatefulItem::onEquippedBy(player, stack);

    const JetpackState &jetpackState = state(stack);
    if (jetpackState.amount != 0)
    {
        player.achieve(DripMilestone::ID, DripMilestone::EquipFuelledJetpack);
    }
}