#include <Item/StatefulItem/MemoryChip/MemoryChip.hpp>
#include <Material/Material.hpp>
#include <RuntimeStateCache/RuntimeStateCache.hpp>
#include <Widget/ColorIndicator/ColorIndicator.hpp>
#include <Widget/IconLabel/IconLabel.hpp>
#include <Widget/Layout/Center/Center.hpp>
#include <Widget/Layout/Column/Column.hpp>
#include <Widget/ColorSelector/ColorSelector.hpp>
#include <Widget/Layout/CompoundWidget/Deck/Deck.hpp>
#include <Widget/Layout/CompoundWidget/Form/Form.hpp>
#include <Widget/Layout/CompoundWidget/FormSection/FormSection.hpp>
#include <Widget/Layout/CompoundWidget/MachineConfigPanel/MachineConfigPanel.hpp>
#include <Widget/Layout/Margin/Margin.hpp>
#include <Widget/Layout/Row/Row.hpp>
#include <Widget/Panel/Panel.hpp>
#include <Widget/StaticItemSlot/ItemSlotButton/ItemSlotButton.hpp>
#include <Entity/PhysicsEntity/CameraEntity/Player/Player.hpp>

MachineConfigPanel::State::State()
{
    chipColor.alpha = 255;
    std::mt19937 rng(std::random_device{}());
    std::uniform_int_distribution<int> dist(0, 255);

    for (int i = 0; i < 3; i++)
    {
        chipColor.channels[i] = static_cast<uint8_t>(dist(rng));
    }
}

MachineConfigPanel::MachineConfigPanel(
    Player *player,
    std::string machineBlockName,
    std::string stateUrl,
    Material &machineIcon,
    Controller &ctrl
) : CompoundWidget(
        new Margin<1>(
            new Column(
                new Form(
                    "Save Configuration",
                    new Center(
                        new Row(
                            new Column(
                                new Panel(glm::ivec2(0, 9)),
                                new Row(
                                    new IconLabel(machineIcon),
                                    new Panel(glm::ivec2(4, 0)),
                                    new ColorSelector(RuntimeStateCache::get<State>(stateUrl)->chipColor),
                                    new Panel(glm::ivec2(4, 0)),
                                    new ColorIndicator(
                                        Material::coloredRightArrow,
                                        RuntimeStateCache::get<State>(stateUrl)->chipColor
                                    )
                                )
                            ),
                            new ItemSlotButton(
                                ItemStack(),
                                [player, stateUrl, &ctrl]()
                                {
                                    ItemStack &movingStack = player->movingStack;
                                    MemoryChip *chip = movingStack.getItemAs<MemoryChip>();
                                    if (chip == nullptr)
                                    {
                                        Sound::itemClick.play();
                                        return;
                                    }

                                    auto state = RuntimeStateCache::get<State>(stateUrl);
                                    ctrl.saveConfig(0)->then([&movingStack, state](MachineConfig config)
                                    {
                                        MemoryChip *chip = movingStack.getItemAs<MemoryChip>();
                                        if (chip == nullptr)
                                        {
                                            Sound::itemClick.play();
                                            return;
                                        }

                                        chip->save(movingStack, config, state->chipColor);
                                        Sound::accepted.play();
                                    });
                                }
                            )
                        )
                    )
                ),
                Layout::WithProportion<1>(),
                new Panel(),
                new Form(
                    "Load Configuration",
                    new Center(
                        new Row(
                            new ItemSlotButton(
                                ItemStack(),
                                [player, stateUrl, &ctrl, machineBlockName]()
                                {
                                    ItemStack &movingStack = player->movingStack;
                                    MemoryChip *chip = movingStack.getItemAs<MemoryChip>();
                                    if (chip == nullptr)
                                    {
                                        Sound::itemClick.play();
                                        return;
                                    }

                                    const MachineConfig &config = chip->getConfig(movingStack);
                                    if (config.blockName != machineBlockName)
                                    {
                                        Sound::itemClick.play();
                                        return;
                                    }
                                    
                                    ctrl.loadConfig(config)->then([](uint32_t)
                                    {
                                        Sound::accepted.play();
                                    });
                                }
                            ),
                            new Column(
                                new Panel(glm::ivec2(0, 9)),
                                new Row(
                                    new IconLabel(Material::rightArrow),
                                    new Panel(glm::ivec2(4, 0)),
                                    new IconLabel(machineIcon)
                                )
                            )
                        )
                    )
                )
            )
        )
    ),
    player(player),
    ctrl(ctrl)
{}