#include <Controller/InventoryController/QuartzClockController/QuartzClockController.hpp>
#include <Machine/MQuartzClock/MQuartzClock.hpp>
#include <Widget/IconLabel/IconLabel.hpp>
#include <Widget/Label/Label.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 <Utilities/Utilities.hpp>
#include <Entity/PhysicsEntity/CameraEntity/Player/Player.hpp>
#include <Widget/TabList/TabList.hpp>
#include <World/World.hpp>
#include <Widget/BatteryView/BatteryView.hpp>
#include <Widget/Panel/Panel.hpp>
#include <World/World.hpp>
#include <Item/IEnergyContainingItem.hpp>

Row* QuartzClockController::makePeriodSetter(Material &icon, DataLink::Channel channel)
{
    return new Row(
        new Center(new IconLabel(icon)),
        Layout::WithProportion<1>(),
        new Slider<uint64_t>(
            periods[static_cast<size_t>(channel)],
            0,
            maxPeriod,
            "Every $+ ticks",
            [this, channel]()
            {
                setPeriod(static_cast<int>(channel), periods[static_cast<size_t>(channel)]);
            }
        )
    );
}

QuartzClockController::QuartzClockController(ControllerChannel &channel) :
    InventoryController(channel),
    updateEnergy(this, RunsOn::Client,
        [this](int energy) -> uint32_t
    {
        this->energy = energy;

        return 0;
    }),
    updatePeriod(this, RunsOn::Client,
        [this](int index, uint64_t period) -> uint32_t
    {
        periods[index] = period;

        return 0;
    }),
    setPeriod(this, RunsOn::Server,
        [this](int index, uint64_t period) -> uint32_t
    {
        periods[index] = period;
        quartzClock->periods[index] = period;

        return 0;
    })
{}

void QuartzClockController::onConnect(std::shared_ptr<Machine> quartzClockMachine)
{
    bindMachine(quartzClockMachine);
    quartzClock = std::dynamic_pointer_cast<MQuartzClock>(quartzClockMachine);
}

Widget* QuartzClockController::getRightPanel()
{
    return new Column(
        new Center(new Label("Quartz Clock")),

        Layout::WithProportion<1>(),
        new TabList(
            new TabList::Page(
                Material::quartzFork,
                "Timing",
                new Center(
                    new Column(
                        new Label("Channels will flip..."),
                        new Panel(glm::ivec2(0, 2)),
                        makePeriodSetter(Material::redChannel, DataLink::Channel::Red),
                        new Panel(glm::ivec2(0, 2)),
                        makePeriodSetter(Material::greenChannel, DataLink::Channel::Green),
                        new Panel(glm::ivec2(0, 2)),
                        makePeriodSetter(Material::blueChannel, DataLink::Channel::Blue),
                        new Panel(glm::ivec2(0, 2)),
                        makePeriodSetter(Material::magentaChannel, DataLink::Channel::Magenta),
                        new Panel(glm::ivec2(0, 5)),
                        new Label("0 means always off.", Color::faded)
                    )
                )
            ),

            MachineConfigPanel::page(
                player,
                "main:quartzClock",
                "/quartz-clock/config",
                Material::quartzFork,
                *this
            )
        ),

        new Center(
            new Row(
                new Center(new ItemSlot(this, "machine", 0)),
                new Panel(glm::ivec2(2, 0)),
                new Center(new IconLabel(Material::rightArrow)),
                new Panel(glm::ivec2(2, 0)),
                new Center(new BatteryView(energy, MQuartzClock::energyCapacity))
            )
        )
    );
}

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

    if (world.isLocal() && quartzClock != nullptr)
    {
        if (energy != quartzClock->getEnergy())
        {
            energy = quartzClock->getEnergy();
            
            updateEnergy(energy);
        }

        for (int i = 0; i < 4; i++)
        {
            if (periods[i] != quartzClock->periods[i])
            {
                periods[i] = quartzClock->periods[i];

                updatePeriod(i, periods[i]);
            }
        }
    }
}

glm::vec4 QuartzClockController::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);
}