#include <Controller/InventoryController/SteamGeneratorController/SteamGeneratorController.hpp>
#include <FuelRuleRegistry/FuelRuleRegistry.hpp>
#include <Item/IEnergyContainingItem.hpp>
#include <Item/IFluidContainingItem.hpp>
#include <Widget/Label/Label.hpp>
#include <Widget/Layout/CompoundWidget/Deck/Deck.hpp>
#include <Widget/Layout/CompoundWidget/FuelRuleExplorer/FuelRuleExplorer.hpp>
#include <Widget/Layout/CompoundWidget/IconText/IconText.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/FlameStatusIndicator/FlameStatusIndicator.hpp>
#include <Widget/Panel/Panel.hpp>
#include <Widget/Layout/PotView/PotView.hpp>
#include <Widget/FluidLevelIndicator/FluidLevelIndicator.hpp>
#include <Widget/BatteryView/BatteryView.hpp>
#include <Widget/DownArrow/DownArrow.hpp>
#include <Widget/IconLabel/IconLabel.hpp>

SteamGeneratorController::SteamGeneratorController(ControllerChannel &channel) :
    InventoryController(channel),
    m_waterName("main:water"),
    updateSteamGeneratorState(this, RunsOn::Client,
        [this](uint32_t temperature, int waterLevel, uint32_t fuelCurrent, uint32_t fuelBatch, int storedEnergy) -> uint32_t
    {
        this->m_temperature = temperature;
        this->m_waterLevel = waterLevel;
        this->m_fuelCurrent = fuelCurrent;
        this->m_fuelBatch = fuelBatch;
        this->m_storedEnergy = storedEnergy;

        return 0;
    })
{}

void SteamGeneratorController::onConnect(std::shared_ptr<Machine> steamGeneratorMachine)
{
    bindMachine(steamGeneratorMachine);
    m_steamGenerator = std::dynamic_pointer_cast<MSteamGenerator>(steamGeneratorMachine);
}

Widget* SteamGeneratorController::getRightPanel()
{
    return new Column(
        new Center(new IconText(Material::steamGeneratorTodo, "Steam generator")),

        Layout::WithProportion<1>(),
        new TabList(
            "/steam-generator/tab-list",
            new TabList::Page(
                Material::steamGeneratorTodo,
                "Generator",
                new Center(
                    new Deck<Row, Center>(
                        new Column(
                            new Row(
                                (new ItemSlot(this, "machine", 1))
                                    ->withEmptyToolTip(GraphicsContext::colorstr(Color::inputHighlight) + "Drain water into generator")
                            ),
        
                            new Center(new DownArrow()),
        
                            new PotView(
                                m_temperature,
                                new Row(
                                    new Panel(glm::ivec2(5, 0)),
                                    new Column(
                                        new FluidLevelIndicator(m_waterName, m_waterLevel, MSteamGenerator::waterCapacity),
                                        new Panel(glm::ivec2(0, 3))
                                    )
                                )
                            ),
        
                            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")
                            )
                        ),
        
                        new IconLabel(Material::rightArrow),
        
                        new Column(
                            new Panel(glm::ivec2(0, 34)),
                            new Center(new BatteryView(m_storedEnergy, MSteamGenerator::energyCapacity)),
                            new Center(new DownArrow()),
                            new Center(
                                (new ItemSlot(this, "machine", 2))
                                    ->withEmptyToolTip(GraphicsContext::colorstr(Color::rechargeHighlight) + "Recharge from generator")
                            )
                        )
                    )
                )
            ),

            new TabList::Page(
                Material::charcoal,
                "Fuels",
                new FuelRuleExplorer()
            ),

            new MachineHelpTab("articles/steam-generator", "/steam-generator/help")
        )
    );
}

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

    if (world.isLocal() && m_steamGenerator != nullptr)
    {
        if (m_temperature != m_steamGenerator->getTemperature() || m_waterLevel != m_steamGenerator->getWaterLevel()
            || m_fuelCurrent != m_steamGenerator->getFuelCurrent() || m_fuelBatch != m_steamGenerator->getFuelBatch()
            || m_storedEnergy != m_steamGenerator->getStoredEnergy())
        {
            m_temperature = m_steamGenerator->getTemperature();
            m_waterLevel = m_steamGenerator->getWaterLevel();
            m_fuelCurrent = m_steamGenerator->getFuelCurrent();
            m_fuelBatch = m_steamGenerator->getFuelBatch();
            m_storedEnergy = m_steamGenerator->getStoredEnergy(),
            
            updateSteamGeneratorState(
                m_steamGenerator->getTemperature(),
                m_steamGenerator->getWaterLevel(),
                m_steamGenerator->getFuelCurrent(),
                m_steamGenerator->getFuelBatch(),
                m_steamGenerator->getStoredEnergy()
            );
        }
    }
}

glm::vec4 SteamGeneratorController::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)
    {
        // Water slot.
        IFluidContainingItem *fluidContainer = player->movingStack.getItemAs<IFluidContainingItem>();
        if (fluidContainer != nullptr)
        {
            if (fluidContainer->getFluidName(player->movingStack) == "main:water")
            {
                return Color::inputHighlight;
            }
        }
    }

    if (id == "machine" && slot == 2)
    {
        // Recharge slot.
        IEnergyContainingItem *energyContainer = player->movingStack.getItemAs<IEnergyContainingItem>();
        if (energyContainer != nullptr)
        {
            return Color::rechargeHighlight;
        }
    }

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

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

    IEnergyContainingItem *energyContainer = stack.getItemAs<IEnergyContainingItem>();
    if (energyContainer != nullptr)
    {
        return Color::rechargeHighlight;
    }

    IFluidContainingItem *fluidContainer = stack.getItemAs<IFluidContainingItem>();
    if (fluidContainer != nullptr)
    {
        if (fluidContainer->getFluidName(stack) == "main:water")
        {
            return Color::inputHighlight;
        }
    }

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

void SteamGeneratorController::getExtraToolTipLines(const std::string &id, int slot, ItemStack &stack, ToolTip &tip)
{
    if (fuelRuleRegistry.isValidFuel(stack))
    {
        tip.addLine(Color::fuelHighlight, formatString("Fuel: $+", fuelRuleRegistry.getFuelAmount(stack)));
    }
}