#include <Controller/InventoryController/PotController/PotController.hpp>
#include <Widget/Label/Label.hpp>
#include <Widget/Layout/CompoundWidget/FuelRuleExplorer/FuelRuleExplorer.hpp>
#include <Widget/Layout/CompoundWidget/IconText/IconText.hpp>
#include <Widget/Layout/CompoundWidget/ItemProcRuleExplorer/ItemProcRuleExplorer.hpp>
#include <Widget/StaticItemSlot/ItemSlot/ItemSlot.hpp>
#include <Widget/Layout/Row/Row.hpp>
#include <Widget/Layout/Column/Column.hpp>
#include <Widget/Layout/Center/Center.hpp>
#include <Widget/Builder/Builder.hpp>
#include <Utilities/Utilities.hpp>
#include <Entity/PhysicsEntity/CameraEntity/Player/Player.hpp>
#include <Widget/TabList/MachineHelpTab/MachineHelpTab.hpp>
#include <Widget/TabList/TabList.hpp>
#include <World/World.hpp>
#include <Widget/ProgressIndicator/ProgressIndicator.hpp>
#include <Widget/FlameStatusIndicator/FlameStatusIndicator.hpp>
#include <Widget/Panel/Panel.hpp>
#include <Widget/Layout/PotView/PotView.hpp>
#include <FuelRuleRegistry/FuelRuleRegistry.hpp>
#include <SmeltingRuleRegistry/SmeltingRuleRegistry.hpp>

Widget* PotController::makePotTab()
{
    return new Center(
        new Column(
            new PotView(
                m_temperature,
                new Row(
                    (new ItemSlot(this, "machine", 1))
                        ->withEmptyToolTip(GraphicsContext::colorstr(Color::inputHighlight) + "Input"),
                    new Center(
                        new ProgressIndicator(
                            progress,
                            m_maxProgress
                        )
                    ),
                    (new ItemSlot(this, "machine", 2))
                        ->withEmptyToolTip(GraphicsContext::colorstr(Color::faded) + "Output")
                )
            ),

            new Row(
                new Panel(glm::ivec2(6, 1)),
                new FlameStatusIndicator<uint32_t>(
                    m_fuelCurrent,
                    m_fuelBatch
                )
            ),

            new Row(
                (new ItemSlot(this, "machine", 0))
                    ->withEmptyToolTip(GraphicsContext::colorstr(Color::fuelHighlight) + "Fuel")
            )
        )
    );
}

PotController::PotController(ControllerChannel &channel) :
    InventoryController(channel),
    m_maxProgress(MPot::getCompletedProgress(false)),
    updatePotState(this, RunsOn::Client,
        [this](uint32_t temperature, uint32_t progress, uint32_t fuelCurrent, uint32_t fuelBatch, bool isIron) -> uint32_t
    {
        this->m_temperature = temperature;
        this->progress = progress;
        this->m_fuelCurrent = fuelCurrent;
        this->m_fuelBatch = fuelBatch;
        this->m_maxProgress = MPot::getCompletedProgress(isIron);
        this->m_isIron = isIron;

        return 0;
    })
{}

void PotController::onConnect(std::shared_ptr<Machine> potMachine)
{
    bindMachine(potMachine);
    m_pot = std::dynamic_pointer_cast<MPot>(potMachine);
}

Widget* PotController::getRightPanel()
{
    return new Column(
        new Center(
            new Builder<bool>(
                m_isIron,
                [](const bool &isIron)
                {
                    return new IconText(
                        isIron ? Material::ironPotIconTodo : Material::stonePotIconTodo,
                        isIron ? "Iron Pot" : "Stone Pot"
                    );
                }
            )
        ),

        Layout::WithProportion<1>(),
        new TabList(
            "/pot/tab-list",
            new TabList::Page(
                Material::flameIcon,
                "Pot",
                makePotTab()
            ),
            new TabList::Page(
                Material::charcoal,
                "Fuels",
                new FuelRuleExplorer()
            ),
            new TabList::Page(
                Material::recipeBookIcon,
                "Recipes",
                new ItemProcRuleExplorer(
                    "/smelting-rule-explorer",
                    [](ItemStack &input) -> ItemStack
                    {
                        if (smeltingRuleRegistry.canSmelt(input))
                        {
                            return smeltingRuleRegistry.getSmeltingResult(input);
                        }

                        return ItemStack();
                    }
                )
            ),
            new MachineHelpTab("articles/pot", "/pot/help")
        )
    );
}

void PotController::update()
{
    InventoryController::update();

    if (world.isLocal() && m_pot != nullptr)
    {
        if (m_temperature != m_pot->getTemperature() || progress != m_pot->getProgress()
            || m_fuelCurrent != m_pot->getFuelCurrent() || m_fuelBatch != m_pot->getFuelBatch()
            || m_isIron != m_pot->isIron())
        {
            m_temperature = m_pot->getTemperature();
            progress = m_pot->getProgress();
            m_fuelCurrent = m_pot->getFuelCurrent();
            m_fuelBatch = m_pot->getFuelBatch();
            
            updatePotState(m_pot->getTemperature(), m_pot->getProgress(), m_pot->getFuelCurrent(), m_pot->getFuelBatch(),
                m_pot->isIron());
        }
    }
}

glm::vec4 PotController::getSlotHighlight(const std::string &id, int slot, ItemStack &stack)
{
    if (id == "machine" && slot == 0)
    {
        // Fuel slot.
        if (fuelRuleRegistry.isValidFuel(player->movingStack))
        {
            return Color::fuelHighlight;
        }
    }

    if (id == "machine" && slot == 1)
    {
        // Input slot.
        if (smeltingRuleRegistry.canSmelt(player->movingStack))
        {
            return Color::inputHighlight;
        }
    }

    if (id == "machine")
    {
        return InventoryController::getSlotHighlight(id, slot, stack);
    }

    if (fuelRuleRegistry.isValidFuel(stack))
    {
        return Color::fuelHighlight;
    }

    if (smeltingRuleRegistry.canSmelt(stack))
    {
        return Color::inputHighlight;
    }

    return InventoryController::getSlotHighlight(id, slot, stack);
}

void PotController::getExtraToolTipLines(const std::string &id, int slot, ItemStack &stack, ToolTip &tip)
{
    if (smeltingRuleRegistry.canSmelt(stack))
    {
        ItemStack smeltingResult = smeltingRuleRegistry.getSmeltingResult(stack);
        tip.addLine(
            Color::inputHighlight,
            formatString(
                "Smelts to $+ $+",
                smeltingResult.getStackSize(),
                smeltingResult.getDisplayName()
            )
        );
    }

    if (fuelRuleRegistry.isValidFuel(stack))
    {
        tip.addLine(Color::fuelHighlight, formatString("Fuel: $+", fuelRuleRegistry.getFuelAmount(stack)));
    }
}