#include <BlockRegistry/BlockRegistry.hpp>
#include <Controller/InventoryController/OilRefineryController/OilRefineryController.hpp>
#include <GraphicsContext/GraphicsContext.hpp>
#include <Widget/Label/Label.hpp>
#include <Widget/Layout/CompoundWidget/FormSection/FormSection.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/FluidLevelIndicator/FluidLevelIndicator.hpp>
#include <Widget/Panel/Panel.hpp>
#include <World/World.hpp>
#include <FluidRegistry/FluidRegistry.hpp>
#include <Widget/DownArrow/DownArrow.hpp>
#include <Widget/BatteryView/BatteryView.hpp>
#include <Widget/IconLabel/IconLabel.hpp>
#include <Widget/Switch/Switch.hpp>
#include <Item/IEnergyContainingItem.hpp>
#include <Item/IFluidContainingItem.hpp>

OilRefineryController::OilRefineryController(ControllerChannel &channel) :
    InventoryController(channel),
    updateRefineryState(this, RunsOn::Client,
        [this](bool isSwitchedOn, int oilAmount, int petrolAmount, int storedEnergy) -> uint32_t
    {
        this->isSwitchedOnPriv = isSwitchedOn;
        this->m_oilAmount = oilAmount;
        this->m_petrolAmount = petrolAmount;
        this->m_storedEnergy = storedEnergy;

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

        return 0;
    })
{}

void OilRefineryController::onConnect(std::shared_ptr<Machine> oilRefinery)
{
    bindMachine(oilRefinery);
    m_oilRefinery = std::dynamic_pointer_cast<MOilRefinery>(oilRefinery);
}

Widget* OilRefineryController::getRightPanel()
{
    static std::string oilName("main:oil");
    static std::string petrolName("main:petrol");

    return new Column(
        new Row(
            new IconText(blockRegistry.getBlockByName("main:oilRefinery").iconMaterial, "Oil refinery"),
            
            Layout::WithProportion<1>(),
            new Panel(),

            new Switch(isSwitchedOnPriv, setSwitch)
        ),

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

        Layout::WithProportion<1>(),
        new TabList(
            "/oil-refinery/tablist",
            new TabList::Page(
                blockRegistry.getBlockByName("main:oilRefinery").iconMaterial,
                "Refinery",
                new Column(
                    Layout::WithProportion<1>(),
                    new Center(
                        new FormSection(
                            new Row(
                                new Column(
                                    new Center(
                                        (new ItemSlot(this, "machine", 0))
                                            ->withEmptyToolTip(
                                                GraphicsContext::colorstr(Color::oilHighlight) +
                                                "Drain crude oil from item"
                                            )
                                    ),
                                    new Center(new DownArrow()),
                                    new Row(
                                        Layout::WithProportion<1>(),
                                        new Panel(),
                                        new FluidLevelIndicator(oilName, m_oilAmount, MOilRefinery::fluidCapacity)
                                    )
                                ),

                                new IconLabel(Material::distiller),

                                new Column(
                                    new Row(
                                        new FluidLevelIndicator(petrolName, m_petrolAmount, MOilRefinery::fluidCapacity)
                                    ),
                                    new DownArrow(),
                                    new Center(
                                        (new ItemSlot(this, "machine", 1))
                                            ->withEmptyToolTip(
                                                GraphicsContext::colorstr(Color::petrolHighlight) +
                                                "Collect petrol into item"
                                            )
                                    )
                                )
                            )
                        )
                    ),
                    new Center(
                        new FormSection(
                            new Row(
                                new Center(
                                    (new ItemSlot(this, "machine", 2))
                                        ->withEmptyToolTip(
                                            GraphicsContext::colorstr(Color::rechargeHighlight) +
                                            "Recharge machine from item"
                                        )
                                ),
                                new Panel(glm::ivec2(2, 0)),
                                new Center(new IconLabel(Material::rightArrow)),
                                new Panel(glm::ivec2(2, 0)),
                                new Center(new BatteryView(m_storedEnergy, MOilRefinery::energyCapacity))
                            )
                        )
                    )
                )
            ),

            new MachineHelpTab(
                "articles/oil-refinery",
                "/oil-refinery-controller/help"
            )
        )
    );
}

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

    if (world.isLocal() && m_oilRefinery != nullptr)
    {
        if (isSwitchedOnPriv != m_oilRefinery->isSwitchedOn() || m_oilAmount != m_oilRefinery->getOilAmount() ||
            m_petrolAmount != m_oilRefinery->getPetrolAmount() || m_storedEnergy != m_oilRefinery->getStoredEnergy())
        {
            isSwitchedOnPriv = m_oilRefinery->isSwitchedOn();
            m_oilAmount = m_oilRefinery->getOilAmount();
            m_petrolAmount = m_oilRefinery->getPetrolAmount();
            m_storedEnergy = m_oilRefinery->getStoredEnergy();
            
            updateRefineryState(isSwitchedOnPriv, m_oilAmount, m_petrolAmount, m_storedEnergy);
        }
    }
}

glm::vec4 OilRefineryController::getSlotHighlight(const std::string &id, int slot, ItemStack &stack)
{
    IFluidContainingItem *movingFluidItem = player->movingStack.getItemAs<IFluidContainingItem>();
    IEnergyContainingItem *movingEnergyItem = player->movingStack.getItemAs<IEnergyContainingItem>();

    if (movingFluidItem != nullptr)
    {
        std::string fluidName = movingFluidItem->getFluidName(player->movingStack);
        if (fluidName == "main:oil" && id == "machine" && slot == 0)
        {
            return Color::oilHighlight;
        }

        if ((fluidName == "" || fluidName == "main:petrol") && id == "machine" && slot == 1)
        {
            return Color::petrolHighlight;
        }
    }

    if (movingEnergyItem != nullptr && id == "machine" && slot == 2)
    {
        return Color::rechargeHighlight;
    }

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

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

    IFluidContainingItem *fluidContainer = stack.getItemAs<IFluidContainingItem>();
    if (fluidContainer != nullptr && id != "machine")
    {
        std::string fluidName = fluidContainer->getFluidName(stack);
        if (fluidName == "main:oil")
        {
            return Color::oilHighlight;
        }

        if (fluidName == "" || fluidName == "main:petrol")
        {
            return Color::petrolHighlight;
        }
    }

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