package com.gregtechceu.gtceu.common.machine.mui;

import com.gregtechceu.gtceu.api.blockentity.BlockEntityCreationInfo;
import com.gregtechceu.gtceu.api.machine.MetaMachine;
import com.gregtechceu.gtceu.api.machine.TickableSubscription;
import com.gregtechceu.gtceu.api.machine.feature.IMuiMachine;
import com.gregtechceu.gtceu.common.data.GTMaterials;
import com.gregtechceu.gtceu.common.mui.GTGuiTextures;

import net.minecraft.Util;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.network.chat.Component;
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.entity.animal.Fox;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.Items;
import net.minecraft.world.level.block.Blocks;
import net.minecraftforge.common.capabilities.ForgeCapabilities;
import net.minecraftforge.fluids.capability.templates.FluidTank;
import net.minecraftforge.items.IItemHandlerModifiable;
import net.minecraftforge.items.ItemStackHandler;
import net.minecraftforge.registries.ForgeRegistries;

import brachy.modularui.api.IPanelHandler;
import brachy.modularui.api.drawable.Text;
import brachy.modularui.api.widget.IWidget;
import brachy.modularui.drawable.*;
import brachy.modularui.drawable.progress.CircularProgressDrawable;
import brachy.modularui.drawable.progress.ProgressDrawable;
import brachy.modularui.drawable.schema.ArraySchema;
import brachy.modularui.drawable.schema.BlockHighlight;
import brachy.modularui.factory.GuiData;
import brachy.modularui.factory.PosGuiData;
import brachy.modularui.screen.ModularPanel;
import brachy.modularui.screen.RichTooltip;
import brachy.modularui.screen.UISettings;
import brachy.modularui.screen.viewport.ModularGuiContext;
import brachy.modularui.utils.Alignment;
import brachy.modularui.utils.Color;
import brachy.modularui.utils.Interpolation;
import brachy.modularui.value.BoolValue;
import brachy.modularui.value.IntValue;
import brachy.modularui.value.StringValue;
import brachy.modularui.value.sync.*;
import brachy.modularui.value.sync.ItemSlotSyncHandler;
import brachy.modularui.widget.EmptyWidget;
import brachy.modularui.widget.ParentWidget;
import brachy.modularui.widgets.*;
import brachy.modularui.widgets.dynamic.DynamicWidget;
import brachy.modularui.widgets.layout.Flow;
import brachy.modularui.widgets.slot.*;
import brachy.modularui.widgets.textfield.TextFieldWidget;
import it.unimi.dsi.fastutil.objects.Object2IntMap;
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
import org.jetbrains.annotations.NotNull;

import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.DoubleUnaryOperator;

import static brachy.modularui.drawable.GuiTextures.MUI_LOGO;

public class TestMuiMachine extends MetaMachine implements IMuiMachine {

    private static final Object2IntMap<Item> handlerSizeMap = Util.make(new Object2IntOpenHashMap<>(), map -> {
        map.put(Items.DIAMOND, 9);
        map.put(Items.EMERALD, 9);
        map.put(Items.GOLD_INGOT, 7);
        map.put(Items.IRON_INGOT, 6);
        map.put(Items.CLAY_BALL, 2);
        map.defaultReturnValue(3);
    });

    private final FluidTank fluidTank = new FluidTank(10000);
    private final FluidTank fluidTankPhantom = new FluidTank(500000);
    private long time = 0;
    private int val, val2 = 0;
    private String value = "";
    private int intValue = 1234567;
    private double doubleValue = 1;
    private final int duration = 80;
    private int progress = 0;
    private int cycleState = 0;
    private List<Integer> serverInts = new ArrayList<>();
    private ItemStack displayItem = new ItemStack(Items.DIAMOND);
    private final IItemHandlerModifiable inventory = new ItemStackHandler(2) {

        @Override
        public int getSlotLimit(int slot) {
            return slot == 0 ? Integer.MAX_VALUE : 64;
        }
    };
    private final ItemStackHandler bigInventory = new ItemStackHandler(9);

    private final ItemStackHandler mixerItems = new ItemStackHandler(4);
    private final ItemStackHandler smallInv = new ItemStackHandler(4);
    private final FluidTank mixerFluids1 = new FluidTank(16000);
    private final FluidTank mixerFluids2 = new FluidTank(16000);
    private final ItemStackHandler craftingInventory = new ItemStackHandler(10);
    private final ItemStackHandler storageInventory0 = new ItemStackHandler(1);
    private final Map<Item, ItemStackHandler> stackHandlerMap = new Object2ObjectOpenHashMap<>();

    private int num = 2;

    private TickableSubscription sub;

    public TestMuiMachine(BlockEntityCreationInfo info) {
        super(info);
        sub = subscribeServerTick(this::tick);
    }

    @Override
    public ModularPanel<?> buildUI(PosGuiData guiData, PanelSyncManager syncManager, UISettings settings) {
        // settings.customContainer(() -> new CraftingModularContainer(3, 3, this.craftingInventory));
        // settings.customGui(() -> TestGuiContainer::new);

        syncManager.registerSlotGroup("item_inv", 3);
        syncManager.registerSlotGroup("mixer_items", 2);

        syncManager.syncValue("mixer_fluids", 0, SyncHandlers.fluidSlot(this.mixerFluids1));
        syncManager.syncValue("mixer_fluids", 1, SyncHandlers.fluidSlot(this.mixerFluids2));
        IntSyncValue cycleStateValue = new IntSyncValue(() -> this.cycleState, val -> this.cycleState = val);
        syncManager.getHyperVisor().syncValue("cycle_state", cycleStateValue);
        syncManager.syncValue("display_item", GenericSyncValue.forItem(() -> this.displayItem, null));
        GenericListSyncHandler<Integer> numberListSyncHandler = GenericListSyncHandler.<Integer>builder()
                .getter(() -> this.serverInts)
                .setter(v -> this.serverInts = v)
                .serializer(FriendlyByteBuf::writeInt)
                .deserializer(FriendlyByteBuf::readInt)
                .immutableCopy()
                .build();
        syncManager.syncValue("number_list", numberListSyncHandler);
        syncManager.bindPlayerInventory(guiData.getPlayer());

        DynamicSyncHandler dynamicSyncHandler = new DynamicSyncHandler()
                .widgetProvider((syncManager1, packet) -> {
                    ItemStack itemStack = packet.readItem();
                    if (itemStack.isEmpty()) return new EmptyWidget();
                    Item item = itemStack.getItem();
                    ItemStackHandler handler = stackHandlerMap.computeIfAbsent(item,
                            k -> new ItemStackHandler(handlerSizeMap.getInt(k)));
                    String name = item.getName(itemStack).toString();
                    Flow flow = Flow.row();
                    for (int i = 0; i < handler.getSlots(); i++) {
                        final int index = i;
                        flow.child(new ItemSlot()
                                .syncHandler(syncManager1.getOrCreateSyncHandler(name, i, ItemSlotSyncHandler.class,
                                        () -> new ItemSlotSyncHandler(new ModularSlot(handler, index)))));
                    }
                    return flow;
                });

        DynamicLinkedSyncHandler<GenericListSyncHandler<Integer>> dynamicLinkedSyncHandler = new DynamicLinkedSyncHandler<>(
                numberListSyncHandler)
                .widgetProvider((syncManager1, value1) -> {
                    List<Integer> vals = value1.getValue();
                    return Flow.col()
                            .widthRel(1f)
                            .coverChildrenHeight()
                            .children(vals.size(), i -> Text.str(String.valueOf(vals.get(i))).asWidget().padding(2))
                            .name("synced number col");
                });

        // disable spotless on the menu layout code so it won't insert random line breaks
        // spotless:off
        Rectangle colorPickerBackground = new Rectangle().color(Color.RED.main);
        ModularPanel<?> panel = new ModularPanel<>("test_tile");
        IPanelHandler panelSyncHandler = syncManager.syncedPanel("other_panel", true, this::openSecondWindow);
        IPanelHandler colorPicker = IPanelHandler.simple(panel,
                (mainPanel, player) -> new ColorPickerDialog("color_picker", colorPickerBackground.getColor(), true)
                        .draggable(true)
                        .relative(panel)
                        .top(0)
                        .rightRel(1f),
                true);
        PagedWidget.Controller tabController = new PagedWidget.Controller();
        panel.resizer()                        // returns object which is responsible for sizing
                .size(176, 220);       // set a static size for the main panel

        DoubleSyncValue progressPercent = syncManager.getOrCreateSyncHandler("progressPercent", DoubleSyncValue.class, () ->
                new DoubleSyncValue(() -> (this.progress / (double) this.duration)));

        var babyFop = new Fox(EntityType.FOX, guiData.getLevel());
        babyFop.setAge(-1);
        panel.child(Flow.row()
                        .name("Tab row")
                        .coverChildren()
                        .topRel(0f, 4, 1f)
                        .child(new PageButton(0, tabController)
                                .tab(GuiTextures.TAB_TOP, -1)
                                .overlay(new EntityDrawable<>(babyFop).followMouse().asIcon().size(8)))
                        .child(new PageButton(1, tabController)
                                .tab(GuiTextures.TAB_TOP, 0)
                                .overlay(new ItemDrawable(Items.OAK_SAPLING).asIcon()))
                        .child(new PageButton(2, tabController)
                                .tab(GuiTextures.TAB_TOP, 0)
                                .overlay(new ItemDrawable(Items.COMPASS).asIcon()))
                        .child(new PageButton(3, tabController)
                                .tab(GuiTextures.TAB_TOP, 0)
                                .overlay(new ItemDrawable(Blocks.CHEST).asIcon()))
                        .child(new PageButton(4, tabController)
                                .tab(GuiTextures.TAB_TOP, 0)
                                .overlay(new ItemDrawable(Items.ENDER_EYE).asIcon())))

                .child(new Expandable()
                        .name("expandable")
                        .top(0)
                        .leftRelOffset(1f, 1)
                        .background(GTGuiTextures.BACKGROUND)
                        .excludeAreaInRecipeViewer()
                        .stencilTransform((r, expanded) -> {
                            r.width = Math.max(20, r.width - 5);
                            r.height = Math.max(20, r.height - 5);
                        })
                        .animationDuration(500)
                        .interpolation(Interpolation.BOUNCE_OUT)
                        .collapsedView(new ItemDrawable(Blocks.CRAFTING_TABLE).asIcon().asWidget().size(20).pos(0, 0))
                        .expandedView(new ParentWidget<>()
                                .name("crafting tab")
                                .coverChildren()
                                .child(new ItemDrawable(Blocks.CRAFTING_TABLE).asIcon().asWidget().size(20).pos(0, 0))
                                .child(SlotGroupWidget.builder()
                                        .row("III  D")
                                        .row("III  O")
                                        .row("III   ")
                                        .key('I', i -> new ItemSlot().slot(new ModularSlot(this.craftingInventory, i))
                                                .addTooltipLine("This slot is empty"))
                                        .key('O', new ItemSlot().slot(new ModularCraftingSlot(this.craftingInventory, 9)))
                                        .key('D', new ItemDisplayWidget().syncHandler("display_item"))
                                        .build()
                                        .margin(5, 5, 20, 5).name("crafting"))))

                .child(Flow.column()
                        .name("main col")
                        .sizeRel(1f)
                        .paddingBottom(7)
                        .child(new ParentWidget<>()
                                .expanded()
                                .widthRel(1f)
                                .child(new PagedWidget<>()
                                        .name("root parent")
                                        .sizeRel(1f)
                                        .controller(tabController)
                                        .addPage(new ParentWidget<>()
                                                .name("page 1 parent")
                                                .sizeRel(1f, 1f)
                                                .padding(7, 0)
                                                .child(Flow.row()
                                                        .name("buttons, slots and more tests")
                                                        .height(137)
                                                        .coverChildrenWidth()
                                                        .posRel(Alignment.CenterLeft)
                                                        // .padding(7)
                                                        .child(Flow.col()
                                                                        .name("buttons and slots test")
                                                                        .coverChildren()
                                                                        .marginRight(8)
                                                                        // .flex(flex -> flex.height(0.5f))
                                                                        // .widthRel(0.5f)
                                                                        .crossAxisAlignment(Alignment.CrossAxis.CENTER)
                                                                        .child(new ButtonWidget<>()
                                                                                .size(60, 18)
                                                                                .overlay(Text.dynamic(() -> Component.literal("Button " + this.val))))
                                                                        .child(new FluidSlot()
                                                                                .margin(2)
                                                                                .syncHandler(SyncHandlers.fluidSlot(this.fluidTank)))
                                                                        .child(new ButtonWidget<>()
                                                                                .size(60, 18)
                                                                                .tooltip(tooltip -> {
                                                                                    tooltip.showUpTimer(10);
                                                                                    tooltip.addLine(Text.str("Test Line g"));
                                                                                    tooltip.addLine(Text.str("An image inside of a tooltip:"));
                                                                                    tooltip.addDrawableLine(MUI_LOGO
                                                                                            .asIcon().size(50)
                                                                                            .alignment(Alignment.TopCenter));
                                                                                    tooltip.addLine(Text.str("And here a circle:"));
                                                                                    tooltip.addDrawableLine(new Circle()
                                                                                                    .color(Color.RED.darker(2), Color.RED.brighter(2))
                                                                                                    .asIcon()
                                                                                                    .size(20))
                                                                                            .addDrawableLine(new ItemDrawable(Items.DIAMOND).asIcon())
                                                                                            .pos(RichTooltip.Pos.LEFT);
                                                                                })
                                                                                .onMousePressed((context, mouseButton) -> {
                                                                                    // panel.getScreen().close(true);
                                                                                    // panel.getScreen().openDialog("dialog",
                                                                                    // this::buildDialog,
                                                                                    // ModularUI.LOGGER::info);
                                                                                    // openSecondWindow(context).openIn(panel.getScreen());
                                                                                    panelSyncHandler.openPanel();
                                                                                    return true;
                                                                                })
                                                                                // .flex(flex -> flex.left(3)) // ?
                                                                                .overlay(Text.str("Button 2")))
                                                                        .child(new TextFieldWidget()
                                                                                .size(60, 18)
                                                                                .setTextAlignment(Alignment.CenterRight)
                                                                                .value(SyncHandlers.string(() -> this.value,
                                                                                        val -> this.value = val))
                                                                                .margin(0, 2)
                                                                                .hintText(Component.literal("hint")))
                                                                        .child(new TextFieldWidget()
                                                                                .size(60, 18)
                                                                                .paddingTop(1)
                                                                                .value(SyncHandlers.doubleNumber(
                                                                                        () -> this.doubleValue,
                                                                                        val -> this.doubleValue = val))
                                                                                .setNumbersDouble(DoubleUnaryOperator.identity())
                                                                                .hintText(Component.literal("number")))
                                                                        // .child(Text.str("Test
                                                                        // string").asWidget().padding(2).name("test
                                                                        // string"))
                                                                        .child(new ScrollingTextWidget(
                                                                                Text.str("Very very long test string"))
                                                                                .widthRel(1f).height(16))
                                                                // .child(Text.EMPTY.asWidget().name("Empty Text"))
                                                        )
                                                        .child(Flow.col()
                                                                .name("button and slots test 2")
                                                                .coverChildren()
                                                                // .widthRel(0.5f)
                                                                .crossAxisAlignment(Alignment.CrossAxis.CENTER)
                                                                .child(new ProgressWidget()
                                                                        .value(progressPercent)
                                                                        .texture(GTGuiTextures.PROGRESS_ARROW.main(), ProgressDrawable.Direction.RIGHT))
                                                                .child(new CircularProgressDrawable()
                                                                        .emptyTexture(GTGuiTextures.PROGRESS_MIXER[0])
                                                                        .filledTexture(GTGuiTextures.PROGRESS_MIXER[1])
                                                                        .clockwise()
                                                                        .asWidget()
                                                                        .value(progressPercent))
                                                                .child(Flow.row().coverChildrenWidth().height(22)
                                                                        .child(new ToggleButton()
                                                                                .value(new BoolValue.Dynamic(
                                                                                        () -> cycleStateValue.getIntValue() == 0,
                                                                                        val -> cycleStateValue.setIntValue(0)))
                                                                                .overlay(GTGuiTextures.CYCLE_BUTTON
                                                                                        .getSubArea(0, 0, 1, 1 / 3f)))
                                                                        .child(new ToggleButton()
                                                                                .value(new BoolValue.Dynamic(
                                                                                        () -> cycleStateValue.getIntValue() == 1,
                                                                                        val -> cycleStateValue.setIntValue(1)))
                                                                                .overlay(GTGuiTextures.CYCLE_BUTTON
                                                                                        .getSubArea(0, 1 / 3f, 1,
                                                                                                2 / 3f)))
                                                                        .child(new ToggleButton()
                                                                                .value(new BoolValue.Dynamic(
                                                                                        () -> cycleStateValue.getIntValue() == 2,
                                                                                        val -> cycleStateValue.setIntValue(2)))
                                                                                .overlay(GTGuiTextures.CYCLE_BUTTON
                                                                                        .getSubArea(0, 2 / 3f, 1, 1))))
                                                                /*
                                                                 * .child(new CycleButtonWidget()
                                                                 * .length(3)
                                                                 * .texture(GTGuiTextures.CYCLE_BUTTON)
                                                                 * .addTooltip(0, "State 1")
                                                                 * .addTooltip(1, "State 2")
                                                                 * .addTooltip(2, "State 3")
                                                                 * .background(GTGuiTextures.BUTTON)
                                                                 * .value(SyncHandlers.intNumber(() -> this.cycleState,
                                                                 * val -> this.cycleState = val)))
                                                                 */
                                                                .child(new ItemSlot()
                                                                        .slot(SyncHandlers.itemSlot(this.inventory, 0)
                                                                                .ignoreMaxStackSize(true)
                                                                                .singletonSlotGroup()))
                                                                .child(new FluidSlot()
                                                                        .margin(2)
                                                                        .width(30)
                                                                        .alwaysShowFull(false)
                                                                        .syncHandler(SyncHandlers
                                                                                .fluidSlot(this.fluidTankPhantom)
                                                                                .phantom(true)))
                                                                .child(Flow.col()
                                                                        .name("button and slots test 3")
                                                                        .coverChildren()
                                                                        .child(new TextFieldWidget()
                                                                                .size(60, 20)
                                                                                .value(SyncHandlers.intNumber(
                                                                                        () -> this.intValue,
                                                                                        val -> this.intValue = val))
                                                                                .setNumbers(0, 9999999)
                                                                                .hintText(Component
                                                                                        .literal("integer")))))))
                                        .addPage(Flow.col()
                                                        .name("Slots test page")
                                                        .coverChildren()
                                                        // .height(120)
                                                        .padding(7)
                                                        .leftRel(0.5f)
                                                        .mainAxisAlignment(Alignment.MainAxis.START)
                                                        .childPadding(2)
                                                        // .child(SlotGroupWidget.playerInventory().left(0))
                                                        .child(SlotGroupWidget.builder()
                                                                        .matrix("III", "III", "III")
                                                                        .key('I', index -> {
                                                                            // 4 is the middle slot with a negative priority -> shift
                                                                            // click prioritises middle slot
                                                                            if (index == 4) {
                                                                                return new ItemSlot().slot(
                                                                                        SyncHandlers.itemSlot(this.bigInventory, index)
                                                                                                .singletonSlotGroup(-100));
                                                                            }
                                                                            return new ItemSlot().slot(
                                                                                    SyncHandlers.itemSlot(this.bigInventory, index)
                                                                                            .slotGroup("item_inv"));
                                                                        })
                                                                        .build().name("9 slot inv")
                                                                        .placeSortButtonsTopRightVertical()
                                                                // .marginBottom(2)
                                                        )
                                                        .child(SlotGroupWidget.builder()
                                                                .row("FII")
                                                                .row("FII")
                                                                .key('F',
                                                                        index -> new FluidSlot().syncHandler("mixer_fluids",
                                                                                index))
                                                                .key('I',
                                                                        index -> ItemSlot.create(index >= 2)
                                                                                .slot(new ModularSlot(this.mixerItems, index)
                                                                                        .slotGroup("mixer_items")
                                                                                        .filter(stack -> !stack.getCapability(
                                                                                                        ForgeCapabilities.ITEM_HANDLER)
                                                                                                .isPresent())))
                                                                .build().name("mixer inv")
                                                                .disableSortButtons())
                                                        .child(Flow.row()
                                                                .coverChildrenHeight()
                                                                .child(new CycleButtonWidget()
                                                                        .size(20, 20)
                                                                        .stateCount(3)
                                                                        .stateOverlay(GTGuiTextures.CYCLE_BUTTON)
                                                                        .value(new IntSyncValue(() -> this.val2, val -> this.val2 = val))
                                                                        .margin(8, 0))
                                                                .child(Text.str("Hello World").asWidget().height(18)))
                                                        /*.child(*//*new SpecialButton(
                                                                Text.str("A very long string that looks cool when animated")
                                                                        *//**//*.withAnimation()*//**//*)
                                                                .height(14)
                                                                .widthRel(1f)*//*)*/
                                                /*
                                                 * GuiTextures.LOGO.asIcon()
                                                 * .size(80, 80)
                                                 * .asWidget()
                                                 * .flex(flex -> flex.width(1f).height(1f))
                                                 */)
                                        .addPage(new ParentWidget<>()
                                                .name("page 3 parent")
                                                .sizeRel(1f, 1f)
                                                .padding(7)
                                                // .child(SlotGroupWidget.playerInventory())
                                                .child(new SliderWidget()
                                                        .widthRel(1f).bottom(50).height(16) // test overwriting of units
                                                        .top(7)
                                                        .stopper(0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100)
                                                        .background(GTGuiTextures.FLUID_SLOT))
                                                .child(new ButtonWidget<>()
                                                        .name("color picker button")
                                                        .top(25)
                                                        .background(colorPickerBackground)
                                                        .disableHoverBackground()
                                                        .onMousePressed((context, mouseButton) -> {
                                                            colorPicker.openPanel();
                                                            return true;
                                                        }))
                                                .child(new ListWidget<>()
                                                        .name("test config list")
                                                        .widthRel(1f).top(50).bottom(2)
                                                        /*
                                                         * .child(new Rectangle().setColor(0xFF606060).asWidget()
                                                         * .top(1)
                                                         * .left(32)
                                                         * .size(1, 40))
                                                         */
                                                        .child(Flow.row()
                                                                .name("test config 1")
                                                                .widthRel(1f).coverChildrenHeight()
                                                                .crossAxisAlignment(Alignment.CrossAxis.CENTER)
                                                                .childPadding(2)
                                                                .child(new CycleButtonWidget()
                                                                        .value(new BoolValue(false))
                                                                        .stateOverlay(GuiTextures.CHECK_BOX)
                                                                        .size(14, 14)
                                                                        .margin(8, 4))
                                                                .child(Text.str("Boolean config").asWidget().height(14)))
                                                        .child(Flow.row()
                                                                .name("test config 2")
                                                                .widthRel(1f).height(14)
                                                                .childPadding(2)
                                                                .child(new TextFieldWidget()
                                                                        .value(new IntValue.Dynamic(() -> this.num, val -> this.num = val))
                                                                        .disableHoverBackground()
                                                                        .setNumbers(1, Short.MAX_VALUE)
                                                                        .setTextAlignment(Alignment.Center)
                                                                        .background(new Rectangle().color(0xFFb1b1b1))
                                                                        .setTextColor(Text.TEXT_COLOR)
                                                                        .size(20, 14))
                                                                .child(Text.str("Number config").asWidget()
                                                                        .height(14)))
                                                        .child(Text.str("Config title").asWidget()
                                                                .color(0xFF404040)
                                                                .alignment(Alignment.CenterLeft)
                                                                .left(5).height(14)
                                                                .tooltip(tooltip -> tooltip.showUpTimer(10)
                                                                        .addLine(Text.str("Config title tooltip"))))
                                                        .child(Flow.row()
                                                                .name("test config 3")
                                                                .widthRel(1f).height(14)
                                                                .childPadding(2)
                                                                .child(new CycleButtonWidget()
                                                                        .value(new BoolValue(false))
                                                                        .stateOverlay(GuiTextures.CHECK_BOX)
                                                                        .size(14, 14))
                                                                .child(Text.str("Boolean config 3").asWidget().height(14)))))
                                        .addPage(new ParentWidget<>()
                                                .name("page 4 storage")
                                                .sizeRel(1f)
                                                .child(Flow.col()
                                                                .name("page 4 col, dynamic widgets")
                                                                .padding(7)
                                                                .child(new ItemSlot()
                                                                        .slot(new ModularSlot(this.storageInventory0, 0)
                                                                                .changeListener(((oldItem, newItem, client, init) -> {
                                                                                    if (client && !ItemStack.isSameItem(oldItem, newItem)) {
                                                                                        dynamicSyncHandler.notifyUpdate(
                                                                                                packet -> packet.writeItem(newItem));
                                                                                    }
                                                                                }))))
                                                                .child(new DynamicWidget<>()
                                                                        .widthRel(1f)
                                                                        .syncHandler(dynamicSyncHandler))
                                                /*.child(new DynamicSyncedWidget<>()
                                                        .widthRel(1f)
                                                        .coverChildrenHeight()
                                                        .syncHandler(dynamicLinkedSyncHandler))*/))
                                        .addPage(createSchemaPage(guiData))))
                        .child(SlotGroupWidget.playerInventory(false)));
        /*
         * panel.child(new ButtonWidget<>()
         * .flex(flex -> flex.size(60, 20)
         * .top(7)
         * .left(0.5f))
         * .background(GuiTextures.BUTTON, Text.dynamic(() -> "Button " + this.val)))
         * .child(SlotGroup.playerInventory())
         * .child(new FluidSlot().flex(flex -> flex
         * .top(30)
         * .left(0.5f))
         * .setSynced("fluid_slot"));
         */
        // spotless:on

        return panel;
    }

    private IWidget createSchemaPage(GuiData data) {
        ParentWidget<?> page = new ParentWidget<>();
        page.name("Page 5 schema");
        page.sizeRel(1f);
        page.child(Text.str("Schema").asWidget());

        if (getLevel().isClientSide()) {
            page.child(new SchemaWidget(
                    new SchemaRenderer(ArraySchema.of(data.getPlayer(), 20))
                            .highlightRenderer(
                                    new BlockHighlight(Color.withAlpha(Color.GREEN.brighter(1), 0.9f), 1 / 32f))
            /* .isometric(true) */)
                    .enableAllInteraction(true)
                    .pos(20, 20)
                    .size(100, 100));
        }

        return page;
    }

    public ModularPanel<?> openSecondWindow(PanelSyncManager syncManager, IPanelHandler syncHandler) {
        ModularPanel<?> panel = new Dialog<>("second_window")
                .disablePanelsBelow(false)
                .closeOnOutOfBoundsClick(false)
                .draggable(true)
                .size(100, 100)
                .resizeableOnDrag(true);
        SlotGroup slotGroup = new SlotGroup("small_inv", 2);
        IntSyncValue timeSync = new IntSyncValue(() -> (int) java.lang.System.currentTimeMillis());
        syncManager.syncValue(123456, timeSync);
        syncManager.registerSlotGroup(slotGroup);
        AtomicInteger number = new AtomicInteger(0);
        syncManager.syncValue("int_value", new IntSyncValue(number::get, number::set));
        IPanelHandler panelSyncHandler = syncManager.syncedPanel("other_panel_2", true,
                (syncManager1, syncHandler1) -> openThirdWindow(syncManager1, syncHandler1, number));
        IntSyncValue num = syncManager.getHyperVisor().findSyncHandler("cycle_state", IntSyncValue.class);
        panel.child(ButtonWidget.panelCloseButton())
                .child(new ButtonWidget<>()
                        .size(10).top(14).right(4)
                        .overlay((new FluidDrawable().fluid(GTMaterials.Iron.getFluid(200))), Text.str("3"))
                        .size(50, 50)
                        .onMousePressed((context, mouseButton) -> {
                            panelSyncHandler.openPanel();
                            return true;
                        }))
                .child(Text.str("2nd Panel")
                        .asWidget()
                        .pos(5, 5))
                .child(SlotGroupWidget.builder()
                        .row("II")
                        .row("II")
                        .key('I', i -> new ItemSlot().slot(new ModularSlot(smallInv, i).slotGroup(slotGroup)))
                        .build()
                        .center())
                .child(new CycleButtonWidget()
                        .size(16).pos(5, 5 + 11)
                        .value(num)
                        .stateOverlay(0, Text.str("1"))
                        .stateOverlay(1, Text.str("2"))
                        .stateOverlay(2, Text.str("3"))
                        .addTooltipLine(Text.str("Hyper Visor test")))
                .child(new ButtonWidget<>()
                        .bottom(5)
                        .right(5)
                        .tooltip(richTooltip -> richTooltip.textColor(Color.RED.main).add("WARNING! Very Dangerous"))
                        .onMousePressed((context, mouseButton) -> {
                            if (!panelSyncHandler.isPanelOpen()) {
                                panelSyncHandler.deleteCachedPanel();
                                number.incrementAndGet();
                            }
                            return true;
                        }));
        return panel;
    }

    public ModularPanel<?> openThirdWindow(PanelSyncManager syncManager, IPanelHandler syncHandler,
                                           AtomicInteger integer) {
        ModularPanel<?> panel = new Dialog<>("third_window")
                .disablePanelsBelow(false)
                .closeOnOutOfBoundsClick(false)
                .draggable(true)
                .size(50, 50);
        panel.child(ButtonWidget.panelCloseButton())
                .child(Text.str("3rd Panel: " + integer.get())
                        .asWidget()
                        .pos(5, 17));
        return panel;
    }

    public void buildDialog(Dialog<String, ?> dialog) {
        AtomicReference<String> value = new AtomicReference<>("");
        dialog.draggable(true);
        dialog.child(new TextFieldWidget()
                .resizer(flex -> flex.size(100, 20).center())
                .value(new StringValue.Dynamic(value::get, value::set)))
                .child(new ButtonWidget<>()
                        .resizer(flex -> flex.size(8, 8).top(5).right(5))
                        .overlay(Text.str("x"))
                        .onMousePressed((context, mouseButton) -> {
                            dialog.closeWith(value.get());
                            return true;
                        }));
    }

    @Override
    public void clientTick() {
        if (this.time++ % 20 == 0) {
            this.val++;
        }
        if (++this.progress == this.duration) {
            this.progress = 0;
        }
    }

    public @NotNull ModularPanel<?> buildSearchTest(ModularGuiContext context) {
        List<String> items = Arrays.asList("Chicken", "Jockey", "Flint", "Steel", "Steve", "Diamond", "Ingot", "Iron",
                "Armor", "Greg");
        StringValue searchValue = new StringValue("");
        return ModularPanel.defaultPanel("search", 100, 150)
                .child(Flow.column()
                        .padding(5)
                        .child(new TextFieldWidget()
                                .value(searchValue)
                                .height(16)
                                .widthRel(1f))
                        .child(new ListWidget<>()
                                .collapseDisabledChildren()
                                .expanded()
                                .widthRel(1f)
                                .children(items.size(), i -> new TextWidget<>(Text.str(items.get(i)))
                                        .alignment(Alignment.Center)
                                        .color(Color.WHITE.main)
                                        .widthRel(1f)
                                        .height(16)
                                        .background(GuiTextures.MC_BUTTON)
                                        .setEnabledIf(w -> items.get(i).toLowerCase()
                                                .contains(searchValue.getStringValue())))));
    }

    public void tick() {
        if (this.time++ % 20 == 0) {
            if (++this.val2 == 3) this.val2 = 0;
            Collection<Item> vals = ForgeRegistries.ITEMS.getValues();
            Item item = vals.stream().skip(new Random().nextInt(vals.size())).findFirst().orElse(Items.DIAMOND);
            this.displayItem = new ItemStack(item, 26735987);

            Random rnd = new Random();
            this.serverInts.clear();
            for (int i = 0; i < 5; i++) {
                this.serverInts.add(rnd.nextInt(100));
            }
        }
        if (++this.progress == this.duration) {
            this.progress = 0;
        }
    }

    /*
     * private static class SpecialButton extends ButtonWidget<SpecialButton> {
     * 
     * private final AnimatedText animatedKey;
     * 
     * private SpecialButton(AnimatedText animatedKey) {
     * this.animatedKey = animatedKey.stopAnimation().forward(true);
     * this.animatedKey.reset();
     * }
     * 
     * @Override
     * public void draw(ModularGuiContext context, WidgetThemeEntry<?> widgetTheme) {
     * this.animatedKey.draw(context, 0, 0, getArea().w(), getArea().h(),
     * getActiveWidgetTheme(widgetTheme, isHovering()));
     * }
     * 
     * @Override
     * public void onMouseStartHover() {
     * super.onMouseStartHover();
     * this.animatedKey.startAnimation().forward(true);
     * }
     * 
     * @Override
     * public void onMouseEndHover() {
     * super.onMouseEndHover();
     * this.animatedKey.forward(false);
     * }
     * }
     */
}
