// 
// Decompiled by Procyon v0.6.0
// 

package com.hypixel.hytale.server.core.command.system.pages;

import com.hypixel.hytale.codec.builder.BuilderCodec;
import com.hypixel.hytale.server.core.command.system.arguments.types.ArgumentType;
import java.util.HashSet;
import com.hypixel.hytale.server.core.command.system.arguments.system.FlagArg;
import com.hypixel.hytale.server.core.command.system.arguments.system.DefaultArg;
import com.hypixel.hytale.server.core.command.system.arguments.system.OptionalArg;
import it.unimi.dsi.fastutil.objects.ObjectIterator;
import java.util.Set;
import java.lang.reflect.Field;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import com.hypixel.hytale.server.core.Message;
import java.util.Comparator;
import com.hypixel.hytale.server.core.command.system.MatchResult;
import java.util.Collections;
import java.util.Collection;
import com.hypixel.hytale.server.core.command.system.CommandSender;
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
import java.util.Map;
import java.util.Iterator;
import com.hypixel.hytale.server.core.command.system.arguments.system.RequiredArg;
import com.hypixel.hytale.server.core.command.system.CommandManager;
import com.hypixel.hytale.server.core.command.system.AbstractCommand;
import com.hypixel.hytale.protocol.Packet;
import com.hypixel.hytale.protocol.packets.interface_.OpenChatWithCommand;
import com.hypixel.hytale.protocol.packets.interface_.Page;
import com.hypixel.hytale.server.core.entity.entities.Player;
import com.hypixel.hytale.component.ComponentAccessor;
import com.hypixel.hytale.server.core.ui.builder.EventData;
import com.hypixel.hytale.protocol.packets.interface_.CustomUIEventBindingType;
import com.hypixel.hytale.component.Store;
import com.hypixel.hytale.server.core.ui.builder.UIEventBuilder;
import com.hypixel.hytale.server.core.ui.builder.UICommandBuilder;
import com.hypixel.hytale.server.core.universe.world.storage.EntityStore;
import com.hypixel.hytale.component.Ref;
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
import com.hypixel.hytale.protocol.packets.interface_.CustomPageLifetime;
import com.hypixel.hytale.server.core.universe.PlayerRef;
import javax.annotation.Nullable;
import javax.annotation.Nonnull;
import java.util.List;
import com.hypixel.hytale.server.core.ui.Value;
import com.hypixel.hytale.server.core.entity.entities.player.pages.InteractiveCustomUIPage;

public class CommandListPage extends InteractiveCustomUIPage<CommandListPageEventData>
{
    private static final Value<String> BUTTON_LABEL_STYLE;
    private static final Value<String> BUTTON_LABEL_STYLE_SELECTED;
    private final List<String> visibleCommands;
    @Nonnull
    private String searchQuery;
    private String selectedCommand;
    private String selectedSubcommand;
    private Integer selectedVariantIndex;
    private final List<String> subcommandBreadcrumb;
    @Nullable
    private final String initialCommand;
    
    public CommandListPage(@Nonnull final PlayerRef playerRef) {
        this(playerRef, (String)null);
    }
    
    public CommandListPage(@Nonnull final PlayerRef playerRef, @Nullable final String initialCommand) {
        super(playerRef, CustomPageLifetime.CanDismiss, CommandListPageEventData.CODEC);
        this.visibleCommands = new ObjectArrayList<String>();
        this.searchQuery = "";
        this.subcommandBreadcrumb = new ObjectArrayList<String>();
        this.initialCommand = initialCommand;
    }
    
    @Override
    public void build(@Nonnull final Ref<EntityStore> ref, @Nonnull final UICommandBuilder commandBuilder, @Nonnull final UIEventBuilder eventBuilder, @Nonnull final Store<EntityStore> store) {
        commandBuilder.append("Pages/CommandListPage.ui");
        eventBuilder.addEventBinding(CustomUIEventBindingType.ValueChanged, "#SearchInput", EventData.of("@SearchQuery", "#SearchInput.Value"), false);
        eventBuilder.addEventBinding(CustomUIEventBindingType.Activating, "#BackButton", EventData.of("NavigateUp", "true"));
        eventBuilder.addEventBinding(CustomUIEventBindingType.Activating, "#SendToChatButton", EventData.of("SendToChat", "true"));
        this.buildCommandList(ref, commandBuilder, eventBuilder, store);
        String commandToSelect = this.visibleCommands.getFirst();
        if (this.initialCommand != null && this.visibleCommands.contains(this.initialCommand)) {
            commandToSelect = this.initialCommand;
        }
        this.selectCommand(ref, commandToSelect, commandBuilder, eventBuilder, store);
    }
    
    @Override
    public void handleDataEvent(@Nonnull final Ref<EntityStore> ref, @Nonnull final Store<EntityStore> store, @Nonnull final CommandListPageEventData data) {
        if (data.searchQuery != null) {
            this.searchQuery = data.searchQuery.trim().toLowerCase();
            final UICommandBuilder commandBuilder = new UICommandBuilder();
            final UIEventBuilder eventBuilder = new UIEventBuilder();
            this.buildCommandList(ref, commandBuilder, eventBuilder, store);
            this.sendUpdate(commandBuilder, eventBuilder, false);
        }
        else if (data.command != null) {
            final UICommandBuilder commandBuilder = new UICommandBuilder();
            final UIEventBuilder eventBuilder = new UIEventBuilder();
            this.selectCommand(ref, data.command, commandBuilder, eventBuilder, store);
            this.sendUpdate(commandBuilder, eventBuilder, false);
        }
        else if (data.navigateUp != null) {
            final UICommandBuilder commandBuilder = new UICommandBuilder();
            final UIEventBuilder eventBuilder = new UIEventBuilder();
            this.navigateUp(ref, commandBuilder, eventBuilder, store);
            this.sendUpdate(commandBuilder, eventBuilder, false);
        }
        else if (data.subcommand != null) {
            final UICommandBuilder commandBuilder = new UICommandBuilder();
            final UIEventBuilder eventBuilder = new UIEventBuilder();
            this.selectSubcommand(ref, data.subcommand, commandBuilder, eventBuilder, store);
            this.sendUpdate(commandBuilder, eventBuilder, false);
        }
        else if (data.variantIndex != null) {
            final UICommandBuilder commandBuilder = new UICommandBuilder();
            final UIEventBuilder eventBuilder = new UIEventBuilder();
            try {
                final int variantIdx = Integer.parseInt(data.variantIndex);
                this.selectVariant(ref, variantIdx, commandBuilder, eventBuilder, store);
                this.sendUpdate(commandBuilder, eventBuilder, false);
            }
            catch (final NumberFormatException ex) {}
        }
        else if (data.sendToChat != null) {
            this.handleSendToChat(ref, store);
        }
    }
    
    private void handleSendToChat(@Nonnull final Ref<EntityStore> ref, @Nonnull final Store<EntityStore> store) {
        final Player playerComponent = store.getComponent(ref, Player.getComponentType());
        if (playerComponent == null) {
            return;
        }
        final String command = this.buildCurrentCommandString();
        playerComponent.getPageManager().setPage(ref, store, Page.None);
        this.playerRef.getPacketHandler().write(new OpenChatWithCommand(command));
    }
    
    @Nonnull
    private String buildCurrentCommandString() {
        final StringBuilder sb = new StringBuilder("/");
        sb.append(this.selectedCommand);
        for (final String part : this.subcommandBreadcrumb) {
            sb.append(" ").append(part);
        }
        AbstractCommand currentContext = CommandManager.get().getCommandRegistration().get(this.selectedCommand);
        if (currentContext != null) {
            for (final String part2 : this.subcommandBreadcrumb) {
                final Map<String, AbstractCommand> subcommands = currentContext.getSubCommands();
                currentContext = subcommands.get(part2);
                if (currentContext == null) {
                    break;
                }
            }
            if (currentContext != null) {
                for (final RequiredArg<?> arg : currentContext.getRequiredArguments()) {
                    sb.append(" <").append(arg.getName()).append(">");
                }
            }
        }
        return sb.toString();
    }
    
    private void buildCommandList(@Nonnull final Ref<EntityStore> ref, @Nonnull final UICommandBuilder commandBuilder, @Nonnull final UIEventBuilder eventBuilder, @Nonnull final ComponentAccessor<EntityStore> componentAccessor) {
        commandBuilder.clear("#CommandList");
        final Map<String, AbstractCommand> commands = new Object2ObjectOpenHashMap<String, AbstractCommand>(CommandManager.get().getCommandRegistration());
        final Player playerComponent = componentAccessor.getComponent(ref, Player.getComponentType());
        assert playerComponent != null;
        AbstractCommand command = null;
        commands.values().removeIf(command -> !command.hasPermission(playerComponent));
        if (this.searchQuery.isEmpty()) {
            this.visibleCommands.clear();
            this.visibleCommands.addAll(commands.keySet());
            Collections.sort(this.visibleCommands);
        }
        else {
            final ObjectArrayList<SearchResult> results = new ObjectArrayList<SearchResult>();
            for (final Map.Entry<String, AbstractCommand> entry : commands.entrySet()) {
                if (entry.getValue() == null) {
                    continue;
                }
                results.add(new SearchResult(entry.getKey(), MatchResult.EXACT));
            }
            final String[] terms = this.searchQuery.split(" ");
            for (int termIndex = 0; termIndex < terms.length; ++termIndex) {
                final String term = terms[termIndex];
                for (int cmdIndex = results.size() - 1; cmdIndex >= 0; --cmdIndex) {
                    final SearchResult result = results.get(cmdIndex);
                    command = commands.get(result.name);
                    MatchResult match;
                    if (command != null) {
                        match = command.matches(this.playerRef.getLanguage(), term, termIndex);
                    }
                    else {
                        match = MatchResult.NONE;
                    }
                    if (match == MatchResult.NONE) {
                        results.remove(cmdIndex);
                    }
                    else {
                        result.match = result.match.min(match);
                    }
                }
            }
            results.sort(SearchResult.COMPARATOR);
            this.visibleCommands.clear();
            for (int i = 0; i < results.size(); ++i) {
                this.visibleCommands.add(results.get(i).name);
            }
        }
        for (int j = 0; j < this.visibleCommands.size(); ++j) {
            final String name = this.visibleCommands.get(j);
            commandBuilder.append("#CommandList", "Pages/BasicTextButton.ui");
            commandBuilder.set("#CommandList[" + j + "].TextSpans", Message.raw(name));
            eventBuilder.addEventBinding(CustomUIEventBindingType.Activating, "#CommandList[" + j, EventData.of("Command", name));
            if (name.equals(this.selectedCommand)) {
                commandBuilder.set("#CommandList[" + j + "].Style", CommandListPage.BUTTON_LABEL_STYLE_SELECTED);
            }
        }
    }
    
    private void selectCommand(@Nonnull final Ref<EntityStore> ref, @Nonnull final String commandName, @Nonnull final UICommandBuilder commandBuilder, @Nonnull final UIEventBuilder eventBuilder, @Nonnull final ComponentAccessor<EntityStore> componentAccessor) {
        final AbstractCommand command = CommandManager.get().getCommandRegistration().get(commandName);
        if (command == null) {
            throw new IllegalArgumentException("Unknown command: " + commandName);
        }
        commandBuilder.set("#CommandName.TextSpans", Message.raw(commandName));
        final Player playerComponent = componentAccessor.getComponent(ref, Player.getComponentType());
        commandBuilder.set("#CommandDescription.TextSpans", Message.translation(command.getDescription()));
        this.selectedSubcommand = null;
        this.selectedVariantIndex = null;
        this.subcommandBreadcrumb.clear();
        this.buildSubcommandTabs(command, playerComponent, commandBuilder, eventBuilder);
        this.displayCommandInfo(command, playerComponent, commandBuilder, eventBuilder);
        if (this.selectedCommand != null && this.visibleCommands.contains(this.selectedCommand)) {
            commandBuilder.set("#CommandList[" + this.visibleCommands.indexOf(this.selectedCommand) + "].Style", CommandListPage.BUTTON_LABEL_STYLE);
        }
        commandBuilder.set("#CommandList[" + this.visibleCommands.indexOf(commandName) + "].Style", CommandListPage.BUTTON_LABEL_STYLE_SELECTED);
        this.selectedCommand = commandName;
    }
    
    private void selectSubcommand(@Nonnull final Ref<EntityStore> ref, @Nonnull final String subcommandName, @Nonnull final UICommandBuilder commandBuilder, @Nonnull final UIEventBuilder eventBuilder, @Nonnull final ComponentAccessor<EntityStore> componentAccessor) {
        AbstractCommand currentContext = CommandManager.get().getCommandRegistration().get(this.selectedCommand);
        if (currentContext == null) {
            return;
        }
        for (final String breadcrumbPart : this.subcommandBreadcrumb) {
            final Map<String, AbstractCommand> subcommands = currentContext.getSubCommands();
            currentContext = subcommands.get(breadcrumbPart);
            if (currentContext == null) {
                return;
            }
        }
        final Player playerComponent = componentAccessor.getComponent(ref, Player.getComponentType());
        final Map<String, AbstractCommand> subcommands2 = currentContext.getSubCommands();
        final AbstractCommand subcommand = subcommands2.get(subcommandName);
        if (subcommand == null) {
            return;
        }
        this.subcommandBreadcrumb.add(subcommandName);
        this.selectedSubcommand = subcommandName;
        this.selectedVariantIndex = null;
        this.updateTitleWithBreadcrumb(commandBuilder);
        commandBuilder.set("#CommandDescription.TextSpans", Message.translation(subcommand.getDescription()));
        this.buildSubcommandTabs(subcommand, playerComponent, commandBuilder, eventBuilder);
        commandBuilder.set("#BackButton.Visible", true);
        this.displayCommandInfo(subcommand, playerComponent, commandBuilder, eventBuilder);
    }
    
    private void selectVariant(@Nonnull final Ref<EntityStore> ref, final int variantIndex, @Nonnull final UICommandBuilder commandBuilder, @Nonnull final UIEventBuilder eventBuilder, @Nonnull final ComponentAccessor<EntityStore> componentAccessor) {
        AbstractCommand currentContext = CommandManager.get().getCommandRegistration().get(this.selectedCommand);
        if (currentContext == null) {
            return;
        }
        for (final String breadcrumbPart : this.subcommandBreadcrumb) {
            final Map<String, AbstractCommand> subcommands = currentContext.getSubCommands();
            currentContext = subcommands.get(breadcrumbPart);
            if (currentContext == null) {
                return;
            }
        }
        final Player playerComponent = componentAccessor.getComponent(ref, Player.getComponentType());
        try {
            final Field variantsField = AbstractCommand.class.getDeclaredField("variantCommands");
            variantsField.setAccessible(true);
            final Int2ObjectMap<AbstractCommand> variants = (Int2ObjectMap<AbstractCommand>)variantsField.get(currentContext);
            final AbstractCommand variant = variants.get(variantIndex);
            if (variant == null || !variant.hasPermission(playerComponent)) {
                return;
            }
            this.selectedVariantIndex = variantIndex;
            this.updateTitleWithVariantSuffix(commandBuilder);
            commandBuilder.set("#VariantsSection.Visible", false);
            commandBuilder.set("#BackButton.Visible", true);
            commandBuilder.set("#CommandUsageLabel.TextSpans", this.getSimplifiedUsage(variant, playerComponent));
            this.buildParametersSection(variant, playerComponent, commandBuilder);
            this.buildArgumentTypesSection(variant, playerComponent, commandBuilder);
        }
        catch (final Exception ex) {}
    }
    
    private void navigateUp(@Nonnull final Ref<EntityStore> ref, @Nonnull final UICommandBuilder commandBuilder, @Nonnull final UIEventBuilder eventBuilder, @Nonnull final ComponentAccessor<EntityStore> componentAccessor) {
        if (this.selectedVariantIndex != null) {
            this.selectedVariantIndex = null;
            AbstractCommand currentContext = CommandManager.get().getCommandRegistration().get(this.selectedCommand);
            if (currentContext == null) {
                return;
            }
            for (final String breadcrumbPart : this.subcommandBreadcrumb) {
                final Map<String, AbstractCommand> subcommands = currentContext.getSubCommands();
                currentContext = subcommands.get(breadcrumbPart);
                if (currentContext == null) {
                    return;
                }
            }
            final Player playerComponent = componentAccessor.getComponent(ref, Player.getComponentType());
            this.updateTitleWithBreadcrumb(commandBuilder);
            this.displayCommandInfo(currentContext, playerComponent, commandBuilder, eventBuilder);
            commandBuilder.set("#BackButton.Visible", !this.subcommandBreadcrumb.isEmpty());
        }
        else {
            if (this.subcommandBreadcrumb.isEmpty()) {
                return;
            }
            this.subcommandBreadcrumb.remove(this.subcommandBreadcrumb.size() - 1);
            AbstractCommand currentContext = CommandManager.get().getCommandRegistration().get(this.selectedCommand);
            if (currentContext == null) {
                return;
            }
            for (final String breadcrumbPart : this.subcommandBreadcrumb) {
                final Map<String, AbstractCommand> subcommands = currentContext.getSubCommands();
                currentContext = subcommands.get(breadcrumbPart);
                if (currentContext == null) {
                    return;
                }
            }
            final Player playerComponent = componentAccessor.getComponent(ref, Player.getComponentType());
            this.selectedSubcommand = (this.subcommandBreadcrumb.isEmpty() ? null : this.subcommandBreadcrumb.get(this.subcommandBreadcrumb.size() - 1));
            this.updateTitleWithBreadcrumb(commandBuilder);
            this.buildSubcommandTabs(currentContext, playerComponent, commandBuilder, eventBuilder);
            this.displayCommandInfo(currentContext, playerComponent, commandBuilder, eventBuilder);
        }
    }
    
    private void buildSubcommandTabs(@Nonnull final AbstractCommand command, @Nonnull final Player playerComponent, @Nonnull final UICommandBuilder commandBuilder, @Nonnull final UIEventBuilder eventBuilder) {
        commandBuilder.clear("#SubcommandCards");
        final Map<String, AbstractCommand> subcommands = command.getSubCommands();
        if (subcommands.isEmpty()) {
            commandBuilder.set("#SubcommandSection.Visible", false);
        }
        else {
            commandBuilder.set("#SubcommandSection.Visible", true);
            int cardIndex = 0;
            int rowIndex = 0;
            int cardsInCurrentRow = 0;
            for (Map.Entry<String, AbstractCommand> entry : subcommands.entrySet()) {
                final AbstractCommand subcommand = entry.getValue();
                if (!subcommand.hasPermission(playerComponent)) {
                    continue;
                }
                if (cardsInCurrentRow == 0) {
                    commandBuilder.appendInline("#SubcommandCards", "Group { LayoutMode: Left; Anchor: (Bottom: 0); }");
                }
                commandBuilder.append("#SubcommandCards[" + rowIndex, "Pages/SubcommandCard.ui");
                commandBuilder.set("#SubcommandCards[" + rowIndex + "][" + cardsInCurrentRow + "] #SubcommandName.TextSpans", Message.raw(entry.getKey()));
                commandBuilder.set("#SubcommandCards[" + rowIndex + "][" + cardsInCurrentRow + "] #SubcommandUsage.TextSpans", this.getSimplifiedUsage(subcommand, playerComponent));
                commandBuilder.set("#SubcommandCards[" + rowIndex + "][" + cardsInCurrentRow + "] #SubcommandDescription.TextSpans", Message.translation(subcommand.getDescription()));
                eventBuilder.addEventBinding(CustomUIEventBindingType.Activating, "#SubcommandCards[" + rowIndex + "][" + cardsInCurrentRow, EventData.of("Subcommand", entry.getKey()));
                ++cardsInCurrentRow;
                ++cardIndex;
                if (cardsInCurrentRow < 3) {
                    continue;
                }
                cardsInCurrentRow = 0;
                ++rowIndex;
            }
        }
        commandBuilder.set("#BackButton.Visible", !this.subcommandBreadcrumb.isEmpty());
    }
    
    private void updateTitleWithBreadcrumb(@Nonnull final UICommandBuilder commandBuilder) {
        final StringBuilder titleText = new StringBuilder(this.selectedCommand);
        for (final String part : this.subcommandBreadcrumb) {
            titleText.append(" > ").append(part);
        }
        commandBuilder.set("#CommandName.TextSpans", Message.raw(titleText.toString()));
    }
    
    private void updateTitleWithVariantSuffix(@Nonnull final UICommandBuilder commandBuilder) {
        final StringBuilder titleText = new StringBuilder(this.selectedCommand);
        for (final String part : this.subcommandBreadcrumb) {
            titleText.append(" > ").append(part);
        }
        titleText.append(" [Variant]");
        commandBuilder.set("#CommandName.TextSpans", Message.raw(titleText.toString()));
    }
    
    private void buildAliasesSection(@Nonnull final AbstractCommand command, @Nonnull final UICommandBuilder commandBuilder) {
        final Set<String> aliases = command.getAliases();
        if (aliases == null || aliases.isEmpty()) {
            commandBuilder.set("#AliasesSection.Visible", false);
            return;
        }
        commandBuilder.set("#AliasesSection.Visible", true);
        commandBuilder.set("#AliasesList.TextSpans", Message.raw(String.join(", ", aliases)));
    }
    
    private void buildPermissionSection(@Nonnull final AbstractCommand command, @Nonnull final UICommandBuilder commandBuilder) {
        final String permission = command.getPermission();
        if (permission == null || permission.isEmpty()) {
            commandBuilder.set("#PermissionSection.Visible", false);
            return;
        }
        commandBuilder.set("#PermissionSection.Visible", true);
        commandBuilder.set("#PermissionLabel.TextSpans", Message.raw(permission));
    }
    
    private void buildVariantsSection(@Nonnull final AbstractCommand command, @Nonnull final Player playerComponent, @Nonnull final UICommandBuilder commandBuilder, @Nonnull final UIEventBuilder eventBuilder) {
        commandBuilder.clear("#VariantsList");
        try {
            final Field variantsField = AbstractCommand.class.getDeclaredField("variantCommands");
            variantsField.setAccessible(true);
            final Int2ObjectMap<AbstractCommand> variants = (Int2ObjectMap<AbstractCommand>)variantsField.get(command);
            if (variants.isEmpty()) {
                commandBuilder.set("#VariantsSection.Visible", false);
                return;
            }
            commandBuilder.set("#VariantsSection.Visible", true);
            int displayIndex = 0;
            for (Int2ObjectMap.Entry<AbstractCommand> entry : variants.int2ObjectEntrySet()) {
                final AbstractCommand variant = entry.getValue();
                final int variantIndex = entry.getIntKey();
                if (!variant.hasPermission(playerComponent)) {
                    continue;
                }
                commandBuilder.append("#VariantsList", "Pages/VariantCard.ui");
                commandBuilder.set("#VariantsList[" + displayIndex + "] #VariantUsage.TextSpans", this.getSimplifiedUsage(variant, playerComponent));
                eventBuilder.addEventBinding(CustomUIEventBindingType.Activating, "#VariantsList[" + displayIndex, EventData.of("Variant", String.valueOf(variantIndex)));
                ++displayIndex;
            }
            if (displayIndex == 0) {
                commandBuilder.set("#VariantsSection.Visible", false);
            }
        }
        catch (final Exception e) {
            commandBuilder.set("#VariantsSection.Visible", false);
        }
    }
    
    private void displayCommandInfo(@Nonnull final AbstractCommand command, @Nonnull final Player playerComponent, @Nonnull final UICommandBuilder commandBuilder, @Nonnull final UIEventBuilder eventBuilder) {
        this.buildVariantsSection(command, playerComponent, commandBuilder, eventBuilder);
        this.buildAliasesSection(command, commandBuilder);
        this.buildPermissionSection(command, commandBuilder);
        commandBuilder.set("#CommandUsageLabel.TextSpans", this.getSimplifiedUsage(command, playerComponent));
        this.buildParametersSection(command, playerComponent, commandBuilder);
        this.buildArgumentTypesSection(command, playerComponent, commandBuilder);
    }
    
    private Message getSimplifiedUsage(@Nonnull final AbstractCommand command, @Nonnull final Player playerComponent) {
        final Message message = Message.raw("/").insert(command.getFullyQualifiedName());
        try {
            final Field requiredArgsField = AbstractCommand.class.getDeclaredField("requiredArguments");
            requiredArgsField.setAccessible(true);
            final List<RequiredArg<?>> requiredArgs = (List<RequiredArg<?>>)requiredArgsField.get(command);
            for (final RequiredArg<?> arg : requiredArgs) {
                message.insert(" <").insert(Message.translation(arg.getName())).insert(">");
            }
        }
        catch (final Exception ex) {}
        try {
            final Field optionalArgsField = AbstractCommand.class.getDeclaredField("optionalArguments");
            optionalArgsField.setAccessible(true);
            final Map<String, ?> optionalArgs = (Map<String, ?>)optionalArgsField.get(command);
            if (!optionalArgs.isEmpty()) {
                message.insert(" [").insert(Message.translation("server.customUI.commandListPage.optionsIndicator")).insert("]");
            }
        }
        catch (final Exception ex2) {}
        return message;
    }
    
    private void buildParametersSection(@Nonnull final AbstractCommand command, @Nonnull final Player playerComponent, @Nonnull final UICommandBuilder commandBuilder) {
        commandBuilder.clear("#RequiredArgumentsList");
        commandBuilder.clear("#OptionalArgumentsList");
        commandBuilder.clear("#DefaultArgumentsList");
        commandBuilder.clear("#FlagArgumentsList");
        boolean hasAnyParameters = false;
        try {
            final Field requiredArgsField = AbstractCommand.class.getDeclaredField("requiredArguments");
            requiredArgsField.setAccessible(true);
            final List<RequiredArg<?>> requiredArgs = (List<RequiredArg<?>>)requiredArgsField.get(command);
            if (!requiredArgs.isEmpty()) {
                hasAnyParameters = true;
                for (int i = 0; i < requiredArgs.size(); ++i) {
                    final RequiredArg<?> arg = requiredArgs.get(i);
                    commandBuilder.append("#RequiredArgumentsList", "Pages/ParameterItem.ui");
                    commandBuilder.set("#RequiredArgumentsList[" + i + "] #ParamName.TextSpans", Message.raw(arg.getName()));
                    commandBuilder.set("#RequiredArgumentsList[" + i + "] #ParamTag.TextSpans", Message.raw("[Required]"));
                    commandBuilder.set("#RequiredArgumentsList[" + i + "] #ParamType.TextSpans", Message.translation("server.customUI.commandListPage.paramType").param("type", arg.getArgumentType().getName()));
                    commandBuilder.set("#RequiredArgumentsList[" + i + "] #ParamDescription.TextSpans", (arg.getDescription() != null) ? Message.translation(arg.getDescription()) : Message.translation("server.customUI.commandListPage.noDescription"));
                }
            }
        }
        catch (final Exception ex) {}
        try {
            final Field optionalArgsField = AbstractCommand.class.getDeclaredField("optionalArguments");
            optionalArgsField.setAccessible(true);
            final Map<String, ?> optionalArgs = (Map<String, ?>)optionalArgsField.get(command);
            if (!optionalArgs.isEmpty()) {
                hasAnyParameters = true;
            }
            int optIndex = 0;
            int defIndex = 0;
            int flagIndex = 0;
            for (Map.Entry<String, ?> entry : optionalArgs.entrySet()) {
                final Object arg2 = entry.getValue();
                if (arg2 instanceof OptionalArg) {
                    final OptionalArg<?> optArg = (OptionalArg<?>)arg2;
                    if (optArg.getPermission() != null && !playerComponent.hasPermission(optArg.getPermission())) {
                        continue;
                    }
                    commandBuilder.append("#OptionalArgumentsList", "Pages/ParameterItem.ui");
                    commandBuilder.set("#OptionalArgumentsList[" + optIndex + "] #ParamName.TextSpans", Message.raw("--" + optArg.getName() + " <" + optArg.getName()));
                    commandBuilder.set("#OptionalArgumentsList[" + optIndex + "] #ParamTag.TextSpans", Message.raw("[Optional]"));
                    commandBuilder.set("#OptionalArgumentsList[" + optIndex + "] #ParamType.TextSpans", Message.translation("server.customUI.commandListPage.paramType").param("type", optArg.getArgumentType().getName()));
                    commandBuilder.set("#OptionalArgumentsList[" + optIndex + "] #ParamDescription.TextSpans", (optArg.getDescription() != null) ? Message.translation(optArg.getDescription()) : Message.translation("server.customUI.commandListPage.noDescription"));
                    ++optIndex;
                }
                else if (arg2 instanceof DefaultArg) {
                    final DefaultArg<?> defArg = (DefaultArg<?>)arg2;
                    if (defArg.getPermission() != null && !playerComponent.hasPermission(defArg.getPermission())) {
                        continue;
                    }
                    commandBuilder.append("#DefaultArgumentsList", "Pages/ParameterItem.ui");
                    commandBuilder.set("#DefaultArgumentsList[" + defIndex + "] #ParamName.TextSpans", Message.raw("--" + defArg.getName() + " <" + defArg.getName()));
                    commandBuilder.set("#DefaultArgumentsList[" + defIndex + "] #ParamTag.TextSpans", Message.raw("[Default]"));
                    commandBuilder.set("#DefaultArgumentsList[" + defIndex + "] #ParamType.TextSpans", Message.translation("server.customUI.commandListPage.paramTypeDefault").param("type", defArg.getArgumentType().getName()).param("default", defArg.getDefaultValueDescription()));
                    commandBuilder.set("#DefaultArgumentsList[" + defIndex + "] #ParamDescription.TextSpans", (defArg.getDescription() != null) ? Message.translation(defArg.getDescription()) : Message.translation("server.customUI.commandListPage.noDescription"));
                    ++defIndex;
                }
                else {
                    if (!(arg2 instanceof FlagArg)) {
                        continue;
                    }
                    final FlagArg flagArg = (FlagArg)arg2;
                    if (flagArg.getPermission() != null && !playerComponent.hasPermission(flagArg.getPermission())) {
                        continue;
                    }
                    commandBuilder.append("#FlagArgumentsList", "Pages/ParameterItem.ui");
                    commandBuilder.set("#FlagArgumentsList[" + flagIndex + "] #ParamName.TextSpans", Message.raw("--" + flagArg.getName()));
                    commandBuilder.set("#FlagArgumentsList[" + flagIndex + "] #ParamTag.TextSpans", Message.raw("[Flag]"));
                    commandBuilder.set("#FlagArgumentsList[" + flagIndex + "] #ParamType.TextSpans", Message.translation("server.customUI.commandListPage.paramTypeFlag"));
                    commandBuilder.set("#FlagArgumentsList[" + flagIndex + "] #ParamDescription.TextSpans", (flagArg.getDescription() != null) ? Message.translation(flagArg.getDescription()) : Message.translation("server.customUI.commandListPage.noDescription"));
                    ++flagIndex;
                }
            }
        }
        catch (final Exception ex2) {}
        commandBuilder.set("#ParametersSection.Visible", hasAnyParameters);
    }
    
    private void buildArgumentTypesSection(@Nonnull final AbstractCommand command, @Nonnull final Player playerComponent, @Nonnull final UICommandBuilder commandBuilder) {
        commandBuilder.clear("#ArgumentTypesList");
        final HashSet<ArgumentType<?>> allArgumentTypes = new HashSet<ArgumentType<?>>();
        try {
            final Field requiredArgsField = AbstractCommand.class.getDeclaredField("requiredArguments");
            requiredArgsField.setAccessible(true);
            final List<RequiredArg<?>> requiredArgs = (List<RequiredArg<?>>)requiredArgsField.get(command);
            for (final RequiredArg<?> arg : requiredArgs) {
                allArgumentTypes.add(arg.getArgumentType());
            }
            final Field optionalArgsField = AbstractCommand.class.getDeclaredField("optionalArguments");
            optionalArgsField.setAccessible(true);
            final Map<String, ?> optionalArgs = (Map<String, ?>)optionalArgsField.get(command);
            for (final Object entry : optionalArgs.values()) {
                if (entry instanceof final OptionalArg optionalArg) {
                    allArgumentTypes.add(optionalArg.getArgumentType());
                }
                else {
                    if (!(entry instanceof DefaultArg)) {
                        continue;
                    }
                    allArgumentTypes.add(((DefaultArg)entry).getArgumentType());
                }
            }
        }
        catch (final Exception ex) {}
        if (allArgumentTypes.isEmpty()) {
            commandBuilder.set("#ArgumentTypesSection.Visible", false);
            return;
        }
        commandBuilder.set("#ArgumentTypesSection.Visible", true);
        int index = 0;
        for (ArgumentType<?> argType : allArgumentTypes) {
            commandBuilder.append("#ArgumentTypesList", "Pages/ArgumentTypeItem.ui");
            commandBuilder.set("#ArgumentTypesList[" + index + "] #TypeName.TextSpans", argType.getName());
            commandBuilder.set("#ArgumentTypesList[" + index + "] #TypeDescription.TextSpans", argType.getArgumentUsage());
            final String[] examples = argType.getExamples();
            if (examples != null && examples.length > 0) {
                commandBuilder.set("#ArgumentTypesList[" + index + "] #TypeExamples.TextSpans", Message.translation("server.customUI.commandListPage.examples").param("examples", String.join("', '", (CharSequence[])examples)));
            }
            else {
                commandBuilder.set("#ArgumentTypesList[" + index + "] #TypeExamples.Visible", false);
            }
            ++index;
        }
    }
    
    static {
        BUTTON_LABEL_STYLE = Value.ref("Pages/BasicTextButton.ui", "LabelStyle");
        BUTTON_LABEL_STYLE_SELECTED = Value.ref("Pages/BasicTextButton.ui", "SelectedLabelStyle");
    }
    
    public static class CommandListPageEventData
    {
        static final String KEY_COMMAND = "Command";
        static final String KEY_SUBCOMMAND = "Subcommand";
        static final String KEY_SEARCH_QUERY = "@SearchQuery";
        static final String KEY_NAVIGATE_UP = "NavigateUp";
        static final String KEY_VARIANT = "Variant";
        static final String KEY_SEND_TO_CHAT = "SendToChat";
        public static final BuilderCodec<CommandListPageEventData> CODEC;
        private String command;
        private String subcommand;
        private String searchQuery;
        private String navigateUp;
        private String variantIndex;
        private String sendToChat;
        
        static {
            // 
            // This method could not be decompiled.
            // 
            // Original Bytecode:
            // 
            //     2: invokedynamic   BootstrapMethod #0, get:()Ljava/util/function/Supplier;
            //     7: invokestatic    com/hypixel/hytale/codec/builder/BuilderCodec.builder:(Ljava/lang/Class;Ljava/util/function/Supplier;)Lcom/hypixel/hytale/codec/builder/BuilderCodec$Builder;
            //    10: new             Lcom/hypixel/hytale/codec/KeyedCodec;
            //    13: dup            
            //    14: ldc             "Command"
            //    16: getstatic       com/hypixel/hytale/codec/Codec.STRING:Lcom/hypixel/hytale/codec/codecs/simple/StringCodec;
            //    19: invokespecial   com/hypixel/hytale/codec/KeyedCodec.<init>:(Ljava/lang/String;Lcom/hypixel/hytale/codec/Codec;)V
            //    22: invokedynamic   BootstrapMethod #1, accept:()Ljava/util/function/BiConsumer;
            //    27: invokedynamic   BootstrapMethod #2, apply:()Ljava/util/function/Function;
            //    32: invokevirtual   com/hypixel/hytale/codec/builder/BuilderCodec$Builder.addField:(Lcom/hypixel/hytale/codec/KeyedCodec;Ljava/util/function/BiConsumer;Ljava/util/function/Function;)Lcom/hypixel/hytale/codec/builder/BuilderCodec$BuilderBase;
            //    35: checkcast       Lcom/hypixel/hytale/codec/builder/BuilderCodec$Builder;
            //    38: new             Lcom/hypixel/hytale/codec/KeyedCodec;
            //    41: dup            
            //    42: ldc             "Subcommand"
            //    44: getstatic       com/hypixel/hytale/codec/Codec.STRING:Lcom/hypixel/hytale/codec/codecs/simple/StringCodec;
            //    47: invokespecial   com/hypixel/hytale/codec/KeyedCodec.<init>:(Ljava/lang/String;Lcom/hypixel/hytale/codec/Codec;)V
            //    50: invokedynamic   BootstrapMethod #3, accept:()Ljava/util/function/BiConsumer;
            //    55: invokedynamic   BootstrapMethod #4, apply:()Ljava/util/function/Function;
            //    60: invokevirtual   com/hypixel/hytale/codec/builder/BuilderCodec$Builder.addField:(Lcom/hypixel/hytale/codec/KeyedCodec;Ljava/util/function/BiConsumer;Ljava/util/function/Function;)Lcom/hypixel/hytale/codec/builder/BuilderCodec$BuilderBase;
            //    63: checkcast       Lcom/hypixel/hytale/codec/builder/BuilderCodec$Builder;
            //    66: new             Lcom/hypixel/hytale/codec/KeyedCodec;
            //    69: dup            
            //    70: ldc             "@SearchQuery"
            //    72: getstatic       com/hypixel/hytale/codec/Codec.STRING:Lcom/hypixel/hytale/codec/codecs/simple/StringCodec;
            //    75: invokespecial   com/hypixel/hytale/codec/KeyedCodec.<init>:(Ljava/lang/String;Lcom/hypixel/hytale/codec/Codec;)V
            //    78: invokedynamic   BootstrapMethod #5, accept:()Ljava/util/function/BiConsumer;
            //    83: invokedynamic   BootstrapMethod #6, apply:()Ljava/util/function/Function;
            //    88: invokevirtual   com/hypixel/hytale/codec/builder/BuilderCodec$Builder.addField:(Lcom/hypixel/hytale/codec/KeyedCodec;Ljava/util/function/BiConsumer;Ljava/util/function/Function;)Lcom/hypixel/hytale/codec/builder/BuilderCodec$BuilderBase;
            //    91: checkcast       Lcom/hypixel/hytale/codec/builder/BuilderCodec$Builder;
            //    94: new             Lcom/hypixel/hytale/codec/KeyedCodec;
            //    97: dup            
            //    98: ldc             "NavigateUp"
            //   100: getstatic       com/hypixel/hytale/codec/Codec.STRING:Lcom/hypixel/hytale/codec/codecs/simple/StringCodec;
            //   103: invokespecial   com/hypixel/hytale/codec/KeyedCodec.<init>:(Ljava/lang/String;Lcom/hypixel/hytale/codec/Codec;)V
            //   106: invokedynamic   BootstrapMethod #7, accept:()Ljava/util/function/BiConsumer;
            //   111: invokedynamic   BootstrapMethod #8, apply:()Ljava/util/function/Function;
            //   116: invokevirtual   com/hypixel/hytale/codec/builder/BuilderCodec$Builder.addField:(Lcom/hypixel/hytale/codec/KeyedCodec;Ljava/util/function/BiConsumer;Ljava/util/function/Function;)Lcom/hypixel/hytale/codec/builder/BuilderCodec$BuilderBase;
            //   119: checkcast       Lcom/hypixel/hytale/codec/builder/BuilderCodec$Builder;
            //   122: new             Lcom/hypixel/hytale/codec/KeyedCodec;
            //   125: dup            
            //   126: ldc             "Variant"
            //   128: getstatic       com/hypixel/hytale/codec/Codec.STRING:Lcom/hypixel/hytale/codec/codecs/simple/StringCodec;
            //   131: invokespecial   com/hypixel/hytale/codec/KeyedCodec.<init>:(Ljava/lang/String;Lcom/hypixel/hytale/codec/Codec;)V
            //   134: invokedynamic   BootstrapMethod #9, accept:()Ljava/util/function/BiConsumer;
            //   139: invokedynamic   BootstrapMethod #10, apply:()Ljava/util/function/Function;
            //   144: invokevirtual   com/hypixel/hytale/codec/builder/BuilderCodec$Builder.addField:(Lcom/hypixel/hytale/codec/KeyedCodec;Ljava/util/function/BiConsumer;Ljava/util/function/Function;)Lcom/hypixel/hytale/codec/builder/BuilderCodec$BuilderBase;
            //   147: checkcast       Lcom/hypixel/hytale/codec/builder/BuilderCodec$Builder;
            //   150: new             Lcom/hypixel/hytale/codec/KeyedCodec;
            //   153: dup            
            //   154: ldc             "SendToChat"
            //   156: getstatic       com/hypixel/hytale/codec/Codec.STRING:Lcom/hypixel/hytale/codec/codecs/simple/StringCodec;
            //   159: invokespecial   com/hypixel/hytale/codec/KeyedCodec.<init>:(Ljava/lang/String;Lcom/hypixel/hytale/codec/Codec;)V
            //   162: invokedynamic   BootstrapMethod #11, accept:()Ljava/util/function/BiConsumer;
            //   167: invokedynamic   BootstrapMethod #12, apply:()Ljava/util/function/Function;
            //   172: invokevirtual   com/hypixel/hytale/codec/builder/BuilderCodec$Builder.addField:(Lcom/hypixel/hytale/codec/KeyedCodec;Ljava/util/function/BiConsumer;Ljava/util/function/Function;)Lcom/hypixel/hytale/codec/builder/BuilderCodec$BuilderBase;
            //   175: checkcast       Lcom/hypixel/hytale/codec/builder/BuilderCodec$Builder;
            //   178: invokevirtual   com/hypixel/hytale/codec/builder/BuilderCodec$Builder.build:()Lcom/hypixel/hytale/codec/builder/BuilderCodec;
            //   181: putstatic       com/hypixel/hytale/server/core/command/system/pages/CommandListPage$CommandListPageEventData.CODEC:Lcom/hypixel/hytale/codec/builder/BuilderCodec;
            //   184: return         
            // 
            // The error that occurred was:
            // 
            // java.lang.UnsupportedOperationException: The requested operation is not supported.
            //     at com.strobel.util.ContractUtils.unsupported(ContractUtils.java:27)
            //     at com.strobel.assembler.metadata.TypeReference.getRawType(TypeReference.java:284)
            //     at com.strobel.assembler.metadata.TypeReference.getRawType(TypeReference.java:279)
            //     at com.strobel.assembler.metadata.TypeReference.makeGenericType(TypeReference.java:154)
            //     at com.strobel.assembler.metadata.TypeSubstitutionVisitor.visitParameterizedType(TypeSubstitutionVisitor.java:225)
            //     at com.strobel.assembler.metadata.TypeSubstitutionVisitor.visitParameterizedType(TypeSubstitutionVisitor.java:25)
            //     at com.strobel.assembler.metadata.ParameterizedType.accept(ParameterizedType.java:103)
            //     at com.strobel.assembler.metadata.TypeSubstitutionVisitor.visit(TypeSubstitutionVisitor.java:40)
            //     at com.strobel.assembler.metadata.TypeSubstitutionVisitor.visitMethod(TypeSubstitutionVisitor.java:314)
            //     at com.strobel.decompiler.ast.TypeAnalysis.inferCall(TypeAnalysis.java:2611)
            //     at com.strobel.decompiler.ast.TypeAnalysis.doInferTypeForExpression(TypeAnalysis.java:1040)
            //     at com.strobel.decompiler.ast.TypeAnalysis.inferTypeForExpression(TypeAnalysis.java:815)
            //     at com.strobel.decompiler.ast.TypeAnalysis.inferTypeForExpression(TypeAnalysis.java:782)
            //     at com.strobel.decompiler.ast.TypeAnalysis.inferTypeForExpression(TypeAnalysis.java:778)
            //     at com.strobel.decompiler.ast.TypeAnalysis.doInferTypeForExpression(TypeAnalysis.java:1510)
            //     at com.strobel.decompiler.ast.TypeAnalysis.inferTypeForExpression(TypeAnalysis.java:815)
            //     at com.strobel.decompiler.ast.TypeAnalysis.inferTypeForExpression(TypeAnalysis.java:790)
            //     at com.strobel.decompiler.ast.TypeAnalysis.inferCall(TypeAnalysis.java:2689)
            //     at com.strobel.decompiler.ast.TypeAnalysis.doInferTypeForExpression(TypeAnalysis.java:1040)
            //     at com.strobel.decompiler.ast.TypeAnalysis.inferTypeForExpression(TypeAnalysis.java:815)
            //     at com.strobel.decompiler.ast.TypeAnalysis.inferTypeForExpression(TypeAnalysis.java:782)
            //     at com.strobel.decompiler.ast.TypeAnalysis.inferTypeForExpression(TypeAnalysis.java:778)
            //     at com.strobel.decompiler.ast.TypeAnalysis.doInferTypeForExpression(TypeAnalysis.java:1510)
            //     at com.strobel.decompiler.ast.TypeAnalysis.inferTypeForExpression(TypeAnalysis.java:815)
            //     at com.strobel.decompiler.ast.TypeAnalysis.inferTypeForExpression(TypeAnalysis.java:790)
            //     at com.strobel.decompiler.ast.TypeAnalysis.inferCall(TypeAnalysis.java:2689)
            //     at com.strobel.decompiler.ast.TypeAnalysis.doInferTypeForExpression(TypeAnalysis.java:1040)
            //     at com.strobel.decompiler.ast.TypeAnalysis.inferTypeForExpression(TypeAnalysis.java:815)
            //     at com.strobel.decompiler.ast.TypeAnalysis.inferTypeForExpression(TypeAnalysis.java:782)
            //     at com.strobel.decompiler.ast.TypeAnalysis.inferTypeForExpression(TypeAnalysis.java:778)
            //     at com.strobel.decompiler.ast.TypeAnalysis.doInferTypeForExpression(TypeAnalysis.java:1083)
            //     at com.strobel.decompiler.ast.TypeAnalysis.inferTypeForExpression(TypeAnalysis.java:815)
            //     at com.strobel.decompiler.ast.TypeAnalysis.runInference(TypeAnalysis.java:684)
            //     at com.strobel.decompiler.ast.TypeAnalysis.runInference(TypeAnalysis.java:667)
            //     at com.strobel.decompiler.ast.TypeAnalysis.runInference(TypeAnalysis.java:373)
            //     at com.strobel.decompiler.ast.TypeAnalysis.run(TypeAnalysis.java:95)
            //     at com.strobel.decompiler.ast.AstOptimizer.optimize(AstOptimizer.java:344)
            //     at com.strobel.decompiler.ast.AstOptimizer.optimize(AstOptimizer.java:42)
            //     at com.strobel.decompiler.languages.java.ast.AstMethodBodyBuilder.createMethodBody(AstMethodBodyBuilder.java:206)
            //     at com.strobel.decompiler.languages.java.ast.AstMethodBodyBuilder.createMethodBody(AstMethodBodyBuilder.java:93)
            //     at com.strobel.decompiler.languages.java.ast.AstBuilder.createMethodBody(AstBuilder.java:868)
            //     at com.strobel.decompiler.languages.java.ast.AstBuilder.createMethod(AstBuilder.java:761)
            //     at com.strobel.decompiler.languages.java.ast.AstBuilder.addTypeMembers(AstBuilder.java:638)
            //     at com.strobel.decompiler.languages.java.ast.AstBuilder.createTypeCore(AstBuilder.java:605)
            //     at com.strobel.decompiler.languages.java.ast.AstBuilder.createTypeNoCache(AstBuilder.java:195)
            //     at com.strobel.decompiler.languages.java.ast.AstBuilder.addTypeMembers(AstBuilder.java:662)
            //     at com.strobel.decompiler.languages.java.ast.AstBuilder.createTypeCore(AstBuilder.java:605)
            //     at com.strobel.decompiler.languages.java.ast.AstBuilder.createTypeNoCache(AstBuilder.java:195)
            //     at com.strobel.decompiler.languages.java.ast.AstBuilder.createType(AstBuilder.java:162)
            //     at com.strobel.decompiler.languages.java.ast.AstBuilder.addType(AstBuilder.java:137)
            //     at com.strobel.decompiler.languages.java.JavaLanguage.buildAst(JavaLanguage.java:71)
            //     at com.strobel.decompiler.languages.java.JavaLanguage.decompileType(JavaLanguage.java:59)
            //     at com.strobel.decompiler.DecompilerDriver.decompileType(DecompilerDriver.java:333)
            //     at com.strobel.decompiler.DecompilerDriver.decompileJar(DecompilerDriver.java:254)
            //     at com.strobel.decompiler.DecompilerDriver.main(DecompilerDriver.java:129)
            // 
            throw new IllegalStateException("An error occurred while decompiling this method.");
        }
    }
    
    private static class SearchResult
    {
        public static final Comparator<SearchResult> COMPARATOR;
        private final String name;
        private MatchResult match;
        
        public SearchResult(final String name, final MatchResult match) {
            this.name = name;
            this.match = match;
        }
        
        static {
            COMPARATOR = Comparator.comparing(o -> o.match);
        }
    }
}
