#include <BlockRegistry/BlockRegistry.hpp>
#include <Controller/InventoryController/CombustionGeneratorController/CombustionGeneratorController.hpp>
#include <Widget/Label/Label.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>
#include <Widget/Switch/Switch.hpp>
#include <Item/IEnergyContainingItem.hpp>
#include <Item/IFluidContainingItem.hpp>
#include <Widget/IconLabel/IconLabel.hpp>

glm::ivec4 CombustionGeneratorController::petrolHighlightColor(1.0f, 1.0f, 0.0f, 1.0f);

CombustionGeneratorController::CombustionGeneratorController(ControllerChannel &channel) :
    InventoryController(channel),
    waterName("main:water"),
    petrolName("main:petrol"),
    updateCombustionGeneratorState(this, RunsOn::Client,
        [this](bool isSwitchedOn, int petrolLevel, int storedEnergy) -> uint32_t
    {
        this->isSwitchedOn = isSwitchedOn;
        this->petrolLevel = petrolLevel;
        this->storedEnergy = storedEnergy;

        return 0;
    }),
    setSwitch(this, RunsOn::Server,
        [this](bool on) -> uint32_t
    {
        if (this->combustionGenerator != nullptr)
        {
            this->combustionGenerator->setSwitch(on);
        }

        return 0;
    })
{}

void CombustionGeneratorController::onConnect(std::shared_ptr<Machine> combustionGeneratorMachine)
{
    bindMachine(combustionGeneratorMachine);
    combustionGenerator = std::dynamic_pointer_cast<MCombustionGenerator>(combustionGeneratorMachine);
}

Widget* CombustionGeneratorController::getRightPanel()
{
    return new Column(
        new Row(
            new Center(new IconText(
                blockRegistry.getBlockByName("main:combustionGenerator").iconMaterial,
                "Combustion generator"
            )),

            Layout::WithProportion<1>(),
            new Panel(),

            new Switch(isSwitchedOn, [this](bool on)
            {
                this->setSwitch(on);
            })
        ),

        new Panel(glm::ivec2(0, 2)),

        Layout::WithProportion<1>(),
        new TabList(
            "/combustion-generator/tablist",
            new TabList::Page(
                blockRegistry.getBlockByName("main:combustionGenerator").iconMaterial,
                "Generator",
                new Center(
                    new Row(
                        new Center(
                            new Column(
                                new Center(
                                    (new ItemSlot(this, "machine", 0))
                                        ->withEmptyToolTip(
                                            GraphicsContext::colorstr(petrolHighlightColor) +
                                            "Drain petrol from item"
                                        )
                                ),
                                new Center(new DownArrow()),
                                new Center(new FluidLevelIndicator(
                                    petrolName,
                                    petrolLevel,
                                    MCombustionGenerator::fluidCapacity
                                ))
                            )
                        ),

                        new Center(new IconLabel(Material::rightArrow)),

                        new Center(
                            new Column(
                                new Center(new BatteryView(
                                    storedEnergy,
                                    MCombustionGenerator::energyCapacity
                                )),
                                new Center(new DownArrow()),
                                new Center(
                                    (new ItemSlot(this, "machine", 1))
                                        ->withEmptyToolTip(
                                            GraphicsContext::colorstr(Color::rechargeHighlight) +
                                            "Recharge from generator"
                                        )
                                )
                            )
                        )
                    )
                )
            ),

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

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

    if (world.isLocal() && combustionGenerator != nullptr)
    {
        if (isSwitchedOn != combustionGenerator->isSwitchedOn()
            || petrolLevel != combustionGenerator->getPetrolLevel()
            || storedEnergy != combustionGenerator->getStoredEnergy())
        {
            isSwitchedOn = combustionGenerator->isSwitchedOn();
            petrolLevel = combustionGenerator->getPetrolLevel();
            storedEnergy = combustionGenerator->getStoredEnergy();
            
            updateCombustionGeneratorState(
                combustionGenerator->isSwitchedOn(),
                combustionGenerator->getPetrolLevel(),
                combustionGenerator->getStoredEnergy()
            );
        }
    }
}

glm::vec4 CombustionGeneratorController::getSlotHighlight(const std::string &id, int slot, ItemStack &stack)
{
    if (id == "machine" && slot == 0)
    {
        IFluidContainingItem *movingFluidItem = player->movingStack.getItemAs<IFluidContainingItem>();
        if (movingFluidItem != nullptr && movingFluidItem->getFluidName(player->movingStack) == petrolName)
        {
            return petrolHighlightColor;
        }
    }

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

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

    if (stack.isEmpty())
    {
        return InventoryController::getSlotHighlight(id, slot, stack);
    }

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

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

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