#include <BlockRegistry/BlockRegistry.hpp>
#include <Controller/InventoryController/SubstationController/SubstationController.hpp>
#include <Machine/MSubstation/MSubstation.hpp>
#include <Widget/EnumSelector/EnumSelector.hpp>
#include <Widget/IconLabel/IconLabel.hpp>
#include <Widget/Label/Label.hpp>
#include <Widget/Layout/CompoundWidget/Deck/Deck.hpp>
#include <Widget/Layout/CompoundWidget/IconText/IconText.hpp>
#include <Widget/Layout/CompoundWidget/ListView/ListView.hpp>
#include <Widget/Layout/CompoundWidget/MachineConfigPanel/MachineConfigPanel.hpp>
#include <Widget/Slider/Slider.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 <World/World.hpp>
#include <Widget/FlameStatusIndicator/FlameStatusIndicator.hpp>
#include <Widget/BatteryView/BatteryView.hpp>
#include <Widget/Panel/Panel.hpp>
#include <Widget/TabList/TabList.hpp>
#include <World/World.hpp>
#include <FluidRegistry/FluidRegistry.hpp>
#include <Widget/DownArrow/DownArrow.hpp>
#include <Item/IEnergyContainingItem.hpp>

Widget* SubstationController::makeConditionRow(DataLink::Channel channel, Material &icon)
{
    return new Row(
        new Center(new IconLabel(icon)),
        new Center(new Label(" when ")),
        new Center(
            new Builder<MSubstation::Operator>(
                channelConditions[channel].op,
                [this, channel](MSubstation::Operator op) -> Widget*
                {
                    return new Row(
                        new Center(
                            new EnumSelector<MSubstation::Operator>(
                                channelConditions[channel].op,
                                {
                                    {MSubstation::Operator::Never, "(Never)"},
                                    {MSubstation::Operator::EnergyLessThan, "Energy below..."},
                                    {MSubstation::Operator::EnergyMoreThan, "Energy above..."},
                                },
                                [this, channel]()
                                {
                                    setCondition(channel, channelConditions[channel].op, channelConditions[channel].energy);
                                }
                            )
                        ),

                        op == MSubstation::Operator::Never ?
                            (Widget*) new Panel() :
                            new Center(
                                new Slider<int>(
                                    channelConditions[channel].energy,
                                    0, MSubstation::capacity,
                                    "$+ J",
                                    [this, channel]()
                                    {
                                        setCondition(channel, channelConditions[channel].op, channelConditions[channel].energy);
                                    }
                                )
                            )
                    );
                }
            )
        )
    );
}

Widget* SubstationController::makeBatteryLevelPage()
{
    return new Center(
        new Column(
            new Center(new ItemSlot(this, "machine", 0)),
            new Center(new DownArrow()),
            new Center(new BatteryView(energy, MSubstation::capacity, 20000, 48)),
            new Center(new DownArrow()),
            new Center(new ItemSlot(this, "machine", 1))
        )
    );
}

Widget* SubstationController::makeDataOutputsPage()
{
    return new ListView(
        makeConditionRow(DataLink::Channel::Red, Material::redChannel),
        makeConditionRow(DataLink::Channel::Green, Material::greenChannel),
        makeConditionRow(DataLink::Channel::Blue, Material::blueChannel),
        makeConditionRow(DataLink::Channel::Magenta, Material::magentaChannel)
    );
}

Widget* SubstationController::makeStatRow(std::string label, Widget* value)
{
    return new Deck<Row, Center>(
        new Label(label),

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

        value
    );
}

Widget* SubstationController::makeStatsPage()
{
    return new ListView(
        makeStatRow(
            "Power in:",
            new Builder<int>(
                stats.powerIn,
                [](int power) { return new Label(formatString("$+ J/t", power)); }
            )
        ),
        makeStatRow(
            "Power out:",
            new Builder<int>(
                stats.powerOut,
                [](int power) { return new Label(formatString("$+ J/t", power)); }
            )
        ),
        makeStatRow(
            "Net power:",
            new Builder<int>(
                [this]() { return stats.powerIn - stats.powerOut; },
                [](int delta)
                {
                    return new Label(
                        formatString("$+ J/t", delta),
                        delta < 0 ? Color::negativeStat : Color::positiveStat
                    );
                }
            )
        )
    );
}

SubstationController::SubstationController(ControllerChannel &channel) :
    InventoryController(channel),
    updateSubstationState(this, RunsOn::Client,
        [this](int energy, std::unordered_map<DataLink::Channel, MSubstation::Condition> channelConditions) -> uint32_t
    {
        this->energy = energy;

        this->channelConditions[DataLink::Channel::Red] = channelConditions[DataLink::Channel::Red];
        this->channelConditions[DataLink::Channel::Green] = channelConditions[DataLink::Channel::Green];
        this->channelConditions[DataLink::Channel::Blue] = channelConditions[DataLink::Channel::Blue];
        this->channelConditions[DataLink::Channel::Magenta] = channelConditions[DataLink::Channel::Magenta];

        return 0;
    }),
    setCondition(this, RunsOn::Server,
        [this](DataLink::Channel channel, MSubstation::Operator op, int energy) -> uint32_t
    {
        if (substation == nullptr)
        {
            return 1;
        }

        substation->setCondition(channel, op, energy);

        return 0;
    }),
    updateStats(this, RunsOn::Client,
        [this](MSubstation::Stats stats) -> uint32_t
    {
        this->stats = stats;

        return 0;
    })
{}

void SubstationController::onConnect(std::shared_ptr<Machine> substationMachine)
{
    bindMachine(substationMachine);
    substation = std::dynamic_pointer_cast<MSubstation>(substationMachine);
    channelUpdatedCounter = substation->getChannelUpdateCounter() - 1;
}

Widget* SubstationController::getRightPanel()
{
    return new Column(
        new Center(new IconText(
            blockRegistry.getBlockByName("main:substation").iconMaterial,
            "Substation"
        )),

        Layout::WithProportion<1>(),
        new TabList(
            "/substation/tab-list",
            new TabList::Page(
                Material::battery,
                "Battery level",
                makeBatteryLevelPage()
            ),

            new TabList::Page(
                Material::dataOutputs,
                "Data outputs",
                makeDataOutputsPage()
            ),

            new TabList::Page(
                Material::statsIcon,
                "Stats",
                makeStatsPage()
            ),

            MachineConfigPanel::page(
                player,
                "main:substation",
                "/substation/config",
                blockRegistry.getBlockByName("main:substation").iconMaterial,
                *this
            )
        )
    );
}

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

    if (world.isLocal() && substation != nullptr)
    {
        if (energy != substation->getEnergy() || channelUpdatedCounter != substation->getChannelUpdateCounter())
        {
            energy = substation->getEnergy();
            channelUpdatedCounter = substation->getChannelUpdateCounter();
            
            updateSubstationState(energy, substation->getChannelConditions());
        }

        if (stats != substation->stats)
        {
            stats = substation->stats;
            updateStats(stats);
        }
    }
}

glm::vec4 SubstationController::getSlotHighlight(const std::string &id, int slot, ItemStack &stack)
{
    if (id == "machine")
    {
        return Color::rechargeHighlight;
    }

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

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

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