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

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

import java.lang.invoke.CallSite;
import java.lang.reflect.UndeclaredThrowableException;
import java.lang.invoke.MethodHandle;
import java.lang.runtime.SwitchBootstraps;
import java.lang.invoke.MethodType;
import java.lang.invoke.MethodHandles;
import com.hypixel.hytale.server.core.command.system.arguments.types.ListArgumentType;
import com.hypixel.hytale.server.core.command.system.arguments.system.WrappedArg;
import com.hypixel.hytale.server.core.command.system.arguments.system.ArgWrapper;
import com.hypixel.hytale.server.core.command.system.arguments.types.ArgumentType;
import com.hypixel.hytale.server.core.command.system.arguments.system.OptionalArg;
import java.util.Objects;
import it.unimi.dsi.fastutil.objects.ObjectBidirectionalIterator;
import com.hypixel.hytale.server.core.command.system.arguments.system.Argument;
import it.unimi.dsi.fastutil.objects.ObjectBooleanPair;
import com.hypixel.hytale.server.core.Constants;
import com.hypixel.hytale.common.util.StringUtil;
import java.util.Collection;
import com.hypixel.hytale.server.core.command.system.basecommands.AbstractCommandCollection;
import java.util.concurrent.CompletableFuture;
import com.hypixel.hytale.server.core.command.system.arguments.system.DefaultArg;
import com.hypixel.hytale.server.core.command.system.exceptions.GeneralCommandException;
import com.hypixel.hytale.server.core.modules.i18n.I18nModule;
import com.hypixel.hytale.protocol.GameMode;
import java.util.Arrays;
import com.hypixel.hytale.server.core.plugin.PluginBase;
import it.unimi.dsi.fastutil.objects.ObjectIterator;
import java.util.Iterator;
import com.hypixel.hytale.server.core.command.system.arguments.system.FlagArg;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import java.util.LinkedHashMap;
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
import java.util.HashSet;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import com.hypixel.hytale.server.core.command.system.arguments.system.AbstractOptionalArg;
import java.util.Map;
import com.hypixel.hytale.server.core.command.system.arguments.system.RequiredArg;
import java.util.List;
import java.util.Set;
import javax.annotation.Nullable;
import com.hypixel.hytale.server.core.Message;
import javax.annotation.Nonnull;
import com.hypixel.hytale.logger.HytaleLogger;

public abstract class AbstractCommand
{
    @Nonnull
    public static final HytaleLogger LOGGER;
    public static final String[] EMPTY_STRING_ARRAY;
    @Nonnull
    private static final Message MESSAGE_COMMANDS_HELP_NO_PERMISSIBLE_SUB_COMMAND;
    @Nonnull
    private static final Message MESSAGE_COMMANDS_PARSING_ERROR_NO_PERMISSION_FOR_COMMAND;
    @Nonnull
    private static final Message MESSAGE_COMMANDS_PARSING_ERROR_ATTEMPTED_UNSAFE;
    @Nonnull
    private static final Message MESSAGE_COMMANDS_PARSING_USAGE_REQUIRES_CONFIRMATION;
    @Nonnull
    private static final Message MESSAGE_COMMAND_SINGLEPLAYER;
    @Nonnull
    static final String CONFIRM_ARG_TAG = "confirm";
    @Nonnull
    private static final String COLOR_STRING_ARG_REQUIRED = "#C1E0FF";
    @Nonnull
    private static final String COLOR_STRING_ARG_OPTIONAL = "#7E9EBC";
    private AbstractCommand parentCommand;
    @Nullable
    private final String name;
    @Nonnull
    private final Set<String> aliases;
    private final String description;
    @Nonnull
    private final List<RequiredArg<?>> requiredArguments;
    @Nonnull
    private final Map<String, AbstractOptionalArg<?, ?>> optionalArguments;
    private AbbreviationMap<AbstractOptionalArg<?, ?>> argumentAbbreviationMap;
    @Nonnull
    private final Map<String, AbstractCommand> subCommands;
    @Nonnull
    private final Map<String, String> subCommandsAliases;
    @Nonnull
    private final Int2ObjectMap<AbstractCommand> variantCommands;
    @Nullable
    private CommandOwner owner;
    @Nullable
    private String permission;
    @Nullable
    private List<String> permissionGroups;
    private int totalNumRequiredParameters;
    private final boolean requiresConfirmation;
    private boolean unavailableInSingleplayer;
    private boolean allowsExtraArguments;
    private boolean hasBeenRegistered;
    
    protected AbstractCommand(@Nullable final String name, @Nullable final String description, final boolean requiresConfirmation) {
        this.aliases = new HashSet<String>();
        this.requiredArguments = new ObjectArrayList<RequiredArg<?>>();
        this.optionalArguments = new Object2ObjectOpenHashMap<String, AbstractOptionalArg<?, ?>>();
        this.subCommands = new LinkedHashMap<String, AbstractCommand>();
        this.subCommandsAliases = new LinkedHashMap<String, String>();
        this.variantCommands = new Int2ObjectOpenHashMap<AbstractCommand>();
        this.name = ((name == null) ? null : name.toLowerCase());
        this.description = description;
        this.requiresConfirmation = requiresConfirmation;
        if (requiresConfirmation) {
            this.registerOptionalArg(new FlagArg(this, "confirm", ""));
        }
    }
    
    protected AbstractCommand(@Nullable final String name, @Nullable final String description) {
        this(name, description, false);
    }
    
    protected AbstractCommand(@Nullable final String description) {
        this(null, description);
    }
    
    public void setOwner(@Nonnull final CommandOwner owner) {
        this.owner = owner;
        if (this.permission == null && this.canGeneratePermission()) {
            this.permission = this.generatePermission();
        }
        for (final AbstractCommand subCommand : this.subCommands.values()) {
            subCommand.setOwner(owner);
        }
        for (final AbstractCommand variantCommand : this.variantCommands.values()) {
            variantCommand.setOwner(owner);
        }
    }
    
    protected boolean canGeneratePermission() {
        return true;
    }
    
    @Nullable
    protected String generatePermissionNode() {
        return (this.name == null) ? null : this.name.toLowerCase();
    }
    
    @Nonnull
    private String generatePermission() {
        final String selfNode = this.generatePermissionNode();
        if (this.parentCommand != null) {
            final String parentPermission = (this.parentCommand.permission == null) ? this.parentCommand.generatePermission() : this.parentCommand.permission;
            String generatedPermission;
            if (selfNode == null || selfNode.isEmpty()) {
                generatedPermission = parentPermission;
            }
            else {
                generatedPermission = parentPermission + "." + selfNode;
            }
            AbstractCommand.LOGGER.atFine().log("Generated missing permission '" + generatedPermission + "'.");
            return generatedPermission;
        }
        final CommandOwner owner = this.owner;
        if (owner instanceof final PluginBase plugin) {
            return plugin.getBasePermission() + ".command." + selfNode;
        }
        if (this.owner instanceof CommandManager) {
            return "hytale.system.command." + selfNode;
        }
        throw new IllegalArgumentException("Unknown owner type, please use PluginBase or CommandManager");
    }
    
    @Nullable
    public List<String> getPermissionGroups() {
        return this.permissionGroups;
    }
    
    protected void setPermissionGroups(@Nonnull final String... groups) {
        this.permissionGroups = Arrays.asList(groups);
    }
    
    protected void setPermissionGroup(@Nullable final GameMode gameMode) {
        this.setPermissionGroups((gameMode == null) ? null : gameMode.toString());
    }
    
    @Nonnull
    public Map<String, Set<String>> getPermissionGroupsRecursive() {
        final Map<String, Set<String>> permissionsByGroup = new Object2ObjectOpenHashMap<String, Set<String>>();
        this.putRecursivePermissionGroups(permissionsByGroup);
        return permissionsByGroup;
    }
    
    public void putRecursivePermissionGroups(@Nonnull final Map<String, Set<String>> permissionsByGroup) {
        List<String> permissionGroups = this.permissionGroups;
        if (permissionGroups == null && this.parentCommand != null) {
            permissionGroups = this.parentCommand.permissionGroups;
        }
        if (permissionGroups != null && this.permission != null) {
            for (final String group : permissionGroups) {
                if (group == null) {
                    continue;
                }
                permissionsByGroup.computeIfAbsent(group, k -> new HashSet()).add(this.permission);
            }
        }
        for (final AbstractCommand subCommand : this.subCommands.values()) {
            subCommand.putRecursivePermissionGroups(permissionsByGroup);
        }
    }
    
    protected void setUnavailableInSingleplayer(final boolean unavailableInSingleplayer) {
        this.unavailableInSingleplayer = unavailableInSingleplayer;
    }
    
    public void setAllowsExtraArguments(final boolean allowsExtraArguments) {
        this.allowsExtraArguments = allowsExtraArguments;
    }
    
    @Nonnull
    public MatchResult matches(@Nonnull final String language, @Nonnull final String search, final int termDepth) {
        return this.matches(language, search, termDepth, 0);
    }
    
    @Nonnull
    private MatchResult matches(@Nonnull final String language, @Nonnull final String search, final int termDepth, final int depth) {
        if (this.name != null && this.name.contains(search)) {
            return MatchResult.of(termDepth, depth, 0, this.name, search);
        }
        for (final String alias : this.aliases) {
            if (alias.contains(search)) {
                return MatchResult.of(termDepth, depth, 1, alias, search);
            }
        }
        for (final AbstractOptionalArg<?, ?> opt : this.optionalArguments.values()) {
            if (opt.getName().contains(search)) {
                return MatchResult.of(termDepth, depth, 3, opt.getName(), search);
            }
            for (final String alias2 : opt.getAliases()) {
                if (alias2.contains(search)) {
                    return MatchResult.of(termDepth, depth, 3, alias2, search);
                }
            }
        }
        for (final RequiredArg<?> opt2 : this.requiredArguments) {
            if (opt2.getName().contains(search)) {
                return MatchResult.of(termDepth, depth, 3, opt2.getName(), search);
            }
        }
        if (this.description != null) {
            final String descriptionMessage = I18nModule.get().getMessage(language, this.description);
            if (descriptionMessage != null && descriptionMessage.contains(search)) {
                return MatchResult.of(termDepth, depth, 4, descriptionMessage, search);
            }
        }
        for (final AbstractOptionalArg<?, ?> opt : this.optionalArguments.values()) {
            final String description = opt.getDescription();
            if (description != null) {
                final String usageDescription = I18nModule.get().getMessage(language, description);
                if (usageDescription != null && usageDescription.contains(search)) {
                    return MatchResult.of(termDepth, depth, 5, usageDescription, search);
                }
                continue;
            }
        }
        for (final AbstractCommand subCommand : this.subCommands.values()) {
            final MatchResult result = subCommand.matches(language, search, termDepth, depth + 1);
            if (result != MatchResult.NONE) {
                return result;
            }
        }
        for (final AbstractCommand variantCommand : this.variantCommands.values()) {
            final MatchResult result = variantCommand.matches(language, search, termDepth, depth + 1);
            if (result != MatchResult.NONE) {
                return result;
            }
        }
        return MatchResult.NONE;
    }
    
    public void completeRegistration() throws GeneralCommandException {
        this.hasBeenRegistered = true;
        for (final AbstractCommand command : this.subCommands.values()) {
            command.completeRegistration();
        }
        for (final AbstractCommand command : this.variantCommands.values()) {
            command.completeRegistration();
        }
        this.validateVariantNumberOfRequiredParameters(new ParseResult(true));
        this.validateDefaultArguments(new ParseResult(true));
        this.createOptionalArgumentAbbreviationMap();
    }
    
    private void createOptionalArgumentAbbreviationMap() {
        final AbbreviationMap.AbbreviationMapBuilder<AbstractOptionalArg<?, ?>> abbreviationMapBuilder = AbbreviationMap.create();
        for (final AbstractOptionalArg<?, ?> abstractOptionalArg : this.optionalArguments.values()) {
            abbreviationMapBuilder.put(abstractOptionalArg.getName(), abstractOptionalArg);
            for (final String alias : abstractOptionalArg.getAliases()) {
                abbreviationMapBuilder.put(alias, abstractOptionalArg);
            }
        }
        this.argumentAbbreviationMap = abbreviationMapBuilder.build();
    }
    
    private void validateVariantNumberOfRequiredParameters(@Nonnull final ParseResult result) {
        for (Int2ObjectMap.Entry<AbstractCommand> entry : this.variantCommands.int2ObjectEntrySet()) {
            if (this.totalNumRequiredParameters == entry.getValue().totalNumRequiredParameters) {
                result.fail(Message.raw("Command '" + this.getFullyQualifiedName() + "' and its variant '" + entry.getValue().toString() + "' both have " + this.totalNumRequiredParameters + " required parameters. Variants must have different numbers of required parameters."));
            }
        }
    }
    
    private void validateDefaultArguments(@Nonnull final ParseResult parseResult) {
        for (final AbstractOptionalArg<?, ?> value : this.optionalArguments.values()) {
            if (value instanceof final DefaultArg defaultArg2) {
                final DefaultArg<?> defaultArg = defaultArg2;
                defaultArg.validateDefaultValue(parseResult);
            }
        }
    }
    
    public void requirePermission(@Nonnull final String permission) {
        this.permission = permission;
    }
    
    @Nullable
    public String getFullyQualifiedName() {
        if (this.parentCommand == null) {
            return this.name;
        }
        if (this.isVariant()) {
            return this.parentCommand.getFullyQualifiedName();
        }
        return this.parentCommand.getFullyQualifiedName() + " " + this.name;
    }
    
    public int countParents() {
        if (this.parentCommand == null) {
            return 0;
        }
        return this.parentCommand.countParents() + 1;
    }
    
    public void addAliases(@Nonnull final String... aliases) {
        if (this.hasBeenRegistered) {
            throw new IllegalStateException("Cannot add aliases when a command has already completed registration");
        }
        if (this.name == null) {
            throw new IllegalStateException("Cannot add aliases to a command with no name");
        }
        for (final String alias : aliases) {
            this.aliases.add(alias.toLowerCase());
        }
    }
    
    public void addSubCommand(@Nonnull final AbstractCommand command) {
        if (this.hasBeenRegistered) {
            throw new IllegalStateException("Cannot add new subcommands when a command has already completed registration");
        }
        if (this.isVariant()) {
            throw new IllegalStateException("Cannot add a subcommand to a variant command, can only add subcommands to named commands");
        }
        if (command.name == null) {
            throw new IllegalArgumentException("Cannot add a subcommand with no name");
        }
        if (command.parentCommand != null) {
            throw new IllegalArgumentException("Cannot re-use subcommands. Only one parent command allowed for each subcommand");
        }
        command.parentCommand = this;
        if (this.subCommands.containsKey(command.name)) {
            throw new IllegalArgumentException("Cannot have multiple subcommands with the same name");
        }
        if (this.subCommandsAliases.containsKey(command.name)) {
            throw new IllegalArgumentException("Command has same name as existing command alias for command: " + command.name);
        }
        this.subCommands.put(command.name, command);
        for (final String alias : command.aliases) {
            if (this.subCommandsAliases.containsKey(alias) || this.subCommands.containsKey(alias)) {
                throw new IllegalArgumentException("Cannot specify a subcommand alias with the same name as an existing command or alias: " + alias);
            }
            this.subCommandsAliases.put(alias, command.name);
        }
        command.hasBeenRegistered = true;
    }
    
    public void addUsageVariant(@Nonnull final AbstractCommand command) {
        if (this.hasBeenRegistered) {
            throw new IllegalStateException("Cannot add new variants when a command has already completed registration");
        }
        if (this.isVariant()) {
            throw new IllegalStateException("Cannot add a command variant to a variant command, can only add variants to named commands");
        }
        if (command.name != null) {
            throw new IllegalArgumentException("Cannot add a variant command with a name, use the description-only constructor");
        }
        if (command.parentCommand != null) {
            throw new IllegalArgumentException("Cannot re-use variant commands. Only one parent command allowed for each variant command");
        }
        final AbstractCommand variantWithSameNumRequiredParameters = this.variantCommands.put(command.totalNumRequiredParameters, command);
        if (variantWithSameNumRequiredParameters != null) {
            throw new IllegalArgumentException("You have already registered a variant command with " + command.totalNumRequiredParameters + " required parameters. Command's class name: " + variantWithSameNumRequiredParameters.getClass().getName());
        }
        command.parentCommand = this;
        command.hasBeenRegistered = true;
    }
    
    @Nullable
    public CompletableFuture<Void> acceptCall(@Nonnull final CommandSender sender, @Nonnull final ParserContext parserContext, @Nonnull final ParseResult parseResult) {
        parserContext.convertToSubCommand();
        return this.acceptCall0(sender, parserContext, parseResult);
    }
    
    @Nullable
    private CompletableFuture<Void> acceptCall0(@Nonnull final CommandSender sender, @Nonnull final ParserContext parserContext, @Nonnull final ParseResult parseResult) {
        final int numberOfPreOptionalTokens = parserContext.getNumPreOptionalTokens();
        final ObjectBooleanPair<CompletableFuture<Void>> completableFutureBooleanPair = this.checkForExecutingSubcommands(sender, parserContext, parseResult, numberOfPreOptionalTokens);
        if (parseResult.failed()) {
            return null;
        }
        if (completableFutureBooleanPair.rightBoolean()) {
            return completableFutureBooleanPair.left();
        }
        if (!this.hasPermission(sender)) {
            parseResult.fail(AbstractCommand.MESSAGE_COMMANDS_PARSING_ERROR_NO_PERMISSION_FOR_COMMAND);
            return null;
        }
        if (this instanceof AbstractCommandCollection && numberOfPreOptionalTokens != 0) {
            final HashSet<String> commandNames = new HashSet<String>(this.subCommands.keySet());
            commandNames.addAll((Collection<?>)this.subCommandsAliases.keySet());
            final String firstToken = parserContext.getFirstToken();
            final String commandSuggestionPrefix = "\n/" + this.getFullyQualifiedName();
            final String suggestedCommands = commandSuggestionPrefix + String.join(commandSuggestionPrefix, StringUtil.sortByFuzzyDistance(firstToken, commandNames, 5));
            parseResult.fail(Message.translation("server.commands.parsing.error.commandCollectionSubcommandNotFound").param("subcommand", firstToken).param("suggestions", suggestedCommands));
            return null;
        }
        if (parserContext.isHelpSpecified()) {
            sender.sendMessage(this.getUsageString(sender));
            return null;
        }
        if (this.unavailableInSingleplayer && Constants.SINGLEPLAYER) {
            parseResult.fail(AbstractCommand.MESSAGE_COMMAND_SINGLEPLAYER);
            return null;
        }
        if (this.requiresConfirmation && !parserContext.isConfirmationSpecified()) {
            parseResult.fail(AbstractCommand.MESSAGE_COMMANDS_PARSING_ERROR_ATTEMPTED_UNSAFE);
            return null;
        }
        if (this.allowsExtraArguments) {
            if (numberOfPreOptionalTokens < this.totalNumRequiredParameters) {
                parseResult.fail(Message.translation("server.commands.parsing.error.wrongNumberRequiredParameters").param("expected", this.totalNumRequiredParameters).param("actual", numberOfPreOptionalTokens).insert("\n").insert(Message.translation("server.commands.help.usagecolon").param("usage", this.getUsageShort(sender, true))).insert("\n").insert(Message.translation("server.commands.help.useHelpToLearnMore").param("command", this.getFullyQualifiedName())));
                return null;
            }
        }
        else if (this.totalNumRequiredParameters != numberOfPreOptionalTokens) {
            parseResult.fail(Message.translation("server.commands.parsing.error.wrongNumberRequiredParameters").param("expected", this.totalNumRequiredParameters).param("actual", numberOfPreOptionalTokens).insert("\n").insert(Message.translation("server.commands.help.usagecolon").param("usage", this.getUsageShort(sender, true))).insert("\n").insert(Message.translation("server.commands.help.useHelpToLearnMore").param("command", this.getFullyQualifiedName())));
            return null;
        }
        final CommandContext commandContext = new CommandContext(this, sender, parserContext.getInputString());
        this.processRequiredArguments(parserContext, parseResult, commandContext);
        if (parseResult.failed()) {
            return null;
        }
        this.processOptionalArguments(parserContext, parseResult, commandContext);
        if (parseResult.failed()) {
            return null;
        }
        return this.execute(commandContext);
    }
    
    public boolean hasPermission(@Nonnull final CommandSender sender) {
        final String permission = this.getPermission();
        return permission == null || (sender.hasPermission(permission) && (this.parentCommand == null || this.parentCommand.hasPermission(sender)));
    }
    
    @Nonnull
    private ObjectBooleanPair<CompletableFuture<Void>> checkForExecutingSubcommands(@Nonnull final CommandSender sender, @Nonnull final ParserContext parserContext, @Nonnull final ParseResult parseResult, final int numberOfPreOptionalTokens) {
        if (parserContext.getNumPreOptSingleValueTokensBeforeListTokens() >= 0) {
            if (!this.subCommands.isEmpty()) {
                String subCommandName = parserContext.getPreOptionalSingleValueToken(0);
                if (subCommandName != null) {
                    subCommandName = subCommandName.toLowerCase();
                }
                final AbstractCommand subCommand = this.subCommands.get(subCommandName);
                if (subCommand != null) {
                    parserContext.convertToSubCommand();
                    return ObjectBooleanPair.of(subCommand.acceptCall0(sender, parserContext, parseResult), true);
                }
                final String alias = this.subCommandsAliases.get(subCommandName);
                if (alias != null) {
                    parserContext.convertToSubCommand();
                    return ObjectBooleanPair.of(this.subCommands.get(alias).acceptCall0(sender, parserContext, parseResult), true);
                }
            }
            final AbstractCommand commandVariant = this.variantCommands.get(numberOfPreOptionalTokens);
            if (this.totalNumRequiredParameters != numberOfPreOptionalTokens && commandVariant != null) {
                return ObjectBooleanPair.of(commandVariant.acceptCall0(sender, parserContext, parseResult), true);
            }
        }
        return ObjectBooleanPair.of((CompletableFuture<Void>)null, false);
    }
    
    private void processRequiredArguments(@Nonnull final ParserContext parserContext, @Nonnull final ParseResult parseResult, @Nonnull final CommandContext commandContext) {
        int currentReqArgIndex = 0;
        for (final RequiredArg<?> requiredArgument : this.requiredArguments) {
            if (requiredArgument.getArgumentType().isListArgument() && parserContext.isListToken(currentReqArgIndex)) {
                final ParserContext.PreOptionalListContext preOptionalTokenContext = parserContext.getPreOptionalListToken(currentReqArgIndex);
                ++currentReqArgIndex;
                commandContext.appendArgumentData((Argument<?, Object>)requiredArgument, preOptionalTokenContext.getTokens(), true, parseResult);
            }
            else {
                final String[] argParameters = new String[requiredArgument.getArgumentType().getNumberOfParameters()];
                for (int i = 0; i < requiredArgument.getArgumentType().getNumberOfParameters(); ++i) {
                    if (parserContext.isListToken(currentReqArgIndex)) {
                        parseResult.fail(Message.translation("server.commands.parsing.error.notAList").param("name", requiredArgument.getName()));
                        return;
                    }
                    argParameters[i] = parserContext.getPreOptionalSingleValueToken(currentReqArgIndex);
                    ++currentReqArgIndex;
                }
                commandContext.appendArgumentData((Argument<?, Object>)requiredArgument, argParameters, false, parseResult);
                if (parseResult.failed()) {
                    return;
                }
                continue;
            }
        }
    }
    
    private void processOptionalArguments(@Nonnull final ParserContext parserContext, @Nonnull final ParseResult parseResult, @Nonnull final CommandContext commandContext) {
        for (final Map.Entry<String, List<List<String>>> optionalArgContext : parserContext.getOptionalArgs()) {
            final AbstractOptionalArg<? extends Argument<?, ?>, ?> optionalArg = this.argumentAbbreviationMap.get(optionalArgContext.getKey());
            if (optionalArg == null) {
                parseResult.fail(Message.translation("server.commands.parsing.error.couldNotFindOptionalArgName").param("input", optionalArgContext.getKey()));
                return;
            }
            if (optionalArg.getPermission() != null && !commandContext.sender().hasPermission(optionalArg.getPermission())) {
                parseResult.fail(Message.translation("server.commands.parsing.error.noPermissionForOptional").param("argument", optionalArgContext.getKey()));
                return;
            }
            final List<List<String>> optionalArgValues = optionalArgContext.getValue();
            if (optionalArg.getArgumentType().isListArgument() && optionalArgValues.size() > 1) {
                final String[] optionalArgParseValues = new String[optionalArgValues.size() * optionalArgValues.getFirst().size()];
                for (int i = 0; i < optionalArgValues.size(); ++i) {
                    final List<String> values = optionalArgValues.get(i);
                    for (int k = 0; k < values.size(); ++k) {
                        optionalArgParseValues[i * values.size() + k] = values.get(k);
                    }
                }
                commandContext.appendArgumentData(optionalArg, optionalArgParseValues, true, parseResult);
            }
            else {
                commandContext.appendArgumentData(optionalArg, optionalArgValues.isEmpty() ? AbstractCommand.EMPTY_STRING_ARRAY : ((String[])optionalArgValues.getFirst().toArray(AbstractCommand.EMPTY_STRING_ARRAY)), false, parseResult);
            }
        }
        for (final AbstractOptionalArg<?, ?> optionalArg2 : this.optionalArguments.values()) {
            optionalArg2.verifyArgumentDependencies(commandContext, parseResult);
        }
    }
    
    @Nullable
    protected abstract CompletableFuture<Void> execute(@Nonnull final CommandContext p0);
    
    @Nonnull
    public Message getUsageString(@Nonnull final CommandSender sender) {
        final Message requiredArgsMessage = Message.raw("");
        for (final RequiredArg<?> requiredArgument : this.requiredArguments) {
            requiredArgsMessage.insert(" ").insert(requiredArgument.getUsageMessageWithoutDescription());
            if (requiredArgument.getArgumentType().isListArgument()) {
                requiredArgsMessage.insert("/...");
            }
        }
        final Message requiresConfirmationMessage = this.requiresConfirmation ? AbstractCommand.MESSAGE_COMMANDS_PARSING_USAGE_REQUIRES_CONFIRMATION : Message.raw("");
        Message requiredArgs = Message.raw("");
        boolean requiredArgsShown = false;
        for (final RequiredArg<?> requiredArgument2 : this.requiredArguments) {
            requiredArgsShown = true;
            requiredArgs.insert("\n    ").insert(requiredArgument2.getUsageMessage());
        }
        Message optionalArgs = Message.raw("");
        boolean optionalArgsShown = false;
        Message defaultArgs = Message.raw("");
        boolean defaultArgsShown = false;
        Message flagArgs = Message.raw("");
        boolean flagArgsShown = false;
        for (Map.Entry<String, AbstractOptionalArg<?, ?>> entry : this.optionalArguments.entrySet()) {
            final AbstractOptionalArg<? extends Argument<?, ?>, ?> arg = entry.getValue();
            if (arg.getPermission() != null && !sender.hasPermission(arg.getPermission())) {
                continue;
            }
            final AbstractOptionalArg<? extends Argument<?, ?>, ?> obj = arg;
            Objects.requireNonNull(obj);
            final FlagArg flagArg = (FlagArg)obj;
            switch (/* invokedynamic(!) */ProcyonInvokeDynamicHelper_8.invoke(flagArg, false)) {
                case 0: {
                    final OptionalArg ignored = (OptionalArg)flagArg;
                    optionalArgsShown = true;
                    optionalArgs.insert("\n    ").insert(arg.getUsageMessage());
                    continue;
                }
                case 1: {
                    final DefaultArg ignored2 = (DefaultArg)flagArg;
                    defaultArgsShown = true;
                    defaultArgs.insert("\n    ").insert(arg.getUsageMessage());
                    continue;
                }
                case 2: {
                    final FlagArg ignored3 = flagArg;
                    flagArgsShown = true;
                    flagArgs.insert("\n    ").insert(arg.getUsageMessage());
                    continue;
                }
            }
        }
        if (requiredArgsShown) {
            requiredArgs = Message.translation("server.commands.parsing.usage.requiredArgs").param("args", requiredArgs);
        }
        if (optionalArgsShown) {
            optionalArgs = Message.translation("server.commands.parsing.usage.optionalArgs").param("args", optionalArgs);
        }
        if (flagArgsShown) {
            flagArgs = Message.translation("server.commands.parsing.usage.flagArgs").param("args", flagArgs);
        }
        if (defaultArgsShown) {
            defaultArgs = Message.translation("server.commands.parsing.usage.defaultArgs").param("args", defaultArgs);
        }
        final Message variantsMessage = Message.raw("");
        for (final AbstractCommand value : this.variantCommands.values()) {
            if (value.hasPermission(sender)) {
                variantsMessage.insert(Message.translation("server.commands.parsing.usage.subcommands").param("separator", Message.translation("server.commands.parsing.usage.subcommandSeparator")).param("usage", value.getUsageString(sender)));
            }
        }
        final Message subcommandsMessage = Message.raw("");
        for (final AbstractCommand value2 : this.subCommands.values()) {
            if (value2.hasPermission(sender)) {
                subcommandsMessage.insert(Message.translation("server.commands.parsing.usage.subcommands").param("separator", Message.translation("server.commands.parsing.usage.subcommandSeparator")).param("usage", value2.getUsageString(sender)));
            }
        }
        final Message argTypesMessage = Message.raw("\nArgument Types:");
        final HashSet<ArgumentType<?>> allArgumentTypes = new HashSet<ArgumentType<?>>();
        for (final RequiredArg<?> requiredArgument3 : this.requiredArguments) {
            allArgumentTypes.add(requiredArgument3.getArgumentType());
        }
        for (final AbstractOptionalArg<?, ?> optionalArgument : this.optionalArguments.values()) {
            allArgumentTypes.add(optionalArgument.getArgumentType());
        }
        for (final ArgumentType<?> argumentType : allArgumentTypes) {
            argTypesMessage.insert("\n    ").insert(argumentType.getName()).insert(": ").insert(argumentType.getArgumentUsage()).insert("\n        Examples: ").insert("'").insert(String.join("', '", (CharSequence[])argumentType.getExamples())).insert("'.");
        }
        return Message.translation("server.commands.parsing.usage.header").param("fullyQualifiedName", this.getFullyQualifiedName()).param("description", Message.translation(this.description)).param("listOfRequiredArgs", requiredArgsMessage).param("requiresConfirmation", requiresConfirmationMessage).param("requiredArgs", requiredArgs).param("optionalArgs", optionalArgs).param("defaultArgs", defaultArgs).param("flagArgs", flagArgs).param("argTypes", argTypesMessage).param("variants", variantsMessage).param("subcommands", subcommandsMessage);
    }
    
    @Nonnull
    public Message getUsageShort(@Nonnull final CommandSender sender, final boolean fullyQualify) {
        final String indent = " ".repeat(this.countParents());
        if (!this.subCommands.isEmpty() || !this.variantCommands.isEmpty()) {
            final String prefix = (this.parentCommand == null) ? "/" : indent;
            final Message message = Message.raw(prefix).insert(this.name);
            boolean anyPermissible = false;
            for (final AbstractCommand variantCommand : this.variantCommands.values()) {
                if (variantCommand.hasPermission(sender)) {
                    message.insert("\n ").insert(indent).insert(variantCommand.getUsageShort(sender, fullyQualify));
                    anyPermissible = true;
                }
            }
            for (final AbstractCommand subCommand : this.subCommands.values()) {
                if (subCommand.hasPermission(sender)) {
                    message.insert("\n ").insert(indent).insert(subCommand.getUsageShort(sender, fullyQualify));
                    anyPermissible = true;
                }
            }
            if (!anyPermissible) {
                message.insert("\n ").insert(AbstractCommand.MESSAGE_COMMANDS_HELP_NO_PERMISSIBLE_SUB_COMMAND);
            }
            return message;
        }
        final String fullyQualifiedName = this.getFullyQualifiedName();
        final Message message = Message.raw(indent).insert(fullyQualify ? ((fullyQualifiedName != null) ? fullyQualifiedName : "???") : ((this.name != null) ? this.name : ""));
        for (final RequiredArg<?> requiredArgument : this.requiredArguments) {
            message.insert(" ").insert(requiredArgument.getUsageOneLiner().color("#C1E0FF"));
        }
        for (final AbstractOptionalArg<?, ?> optionalArgument : this.optionalArguments.values()) {
            if (optionalArgument.hasPermission(sender)) {
                message.insert(" ").insert(optionalArgument.getUsageOneLiner().color("#7E9EBC"));
            }
        }
        return message;
    }
    
    @Nonnull
    private <R extends RequiredArg<D>, D> R registerRequiredArg(@Nonnull final R requiredArgument) {
        if (this.hasBeenRegistered) {
            throw new IllegalStateException("Cannot add new arguments when a command has already completed registration");
        }
        if (!requiredArgument.getCommandRegisteredTo().equals(this) || this.requiredArguments.contains(requiredArgument)) {
            throw new IllegalArgumentException("Cannot re-use arguments");
        }
        this.totalNumRequiredParameters += ((Argument<Arg, DataType>)requiredArgument).getArgumentType().getNumberOfParameters();
        this.requiredArguments.add(requiredArgument);
        return requiredArgument;
    }
    
    @Nonnull
    private <R extends AbstractOptionalArg<?, D>, D> R registerOptionalArg(@Nonnull final R optionalArgument) {
        if (this.hasBeenRegistered) {
            throw new IllegalStateException("Cannot add new arguments when a command has already completed registration");
        }
        if (!optionalArgument.getCommandRegisteredTo().equals(this) || this.optionalArguments.containsKey(optionalArgument.getName().toLowerCase())) {
            throw new IllegalArgumentException("Cannot re-use arguments");
        }
        this.optionalArguments.put(optionalArgument.getName().toLowerCase(), optionalArgument);
        return optionalArgument;
    }
    
    @Nonnull
    public <D> RequiredArg<D> withRequiredArg(@Nonnull final String name, @Nonnull final String description, @Nonnull final ArgumentType<D> argType) {
        return this.registerRequiredArg(new RequiredArg<D>(this, name, description, argType));
    }
    
    public <W extends WrappedArg<D>, D> W withRequiredArg(@Nonnull final String name, @Nonnull final String description, @Nonnull final ArgWrapper<W, D> wrapper) {
        return wrapper.wrapArg(this.registerRequiredArg((Argument<?, D>)new RequiredArg(this, name, description, (ArgumentType<Object>)wrapper.argumentType())));
    }
    
    @Nonnull
    public <D> RequiredArg<List<D>> withListRequiredArg(@Nonnull final String name, @Nonnull final String description, @Nonnull final ArgumentType<D> argType) {
        return this.registerRequiredArg(new RequiredArg<List<D>>(this, name, description, (ArgumentType<List<D>>)new ListArgumentType((ArgumentType<Object>)argType)));
    }
    
    @Nonnull
    public <D> DefaultArg<D> withDefaultArg(final String name, final String description, final ArgumentType<D> argType, @Nullable final D defaultValue, final String defaultValueDescription) {
        return this.registerOptionalArg(new DefaultArg<D>(this, name, description, argType, defaultValue, defaultValueDescription));
    }
    
    public <W extends WrappedArg<D>, D> W withDefaultArg(@Nonnull final String name, @Nonnull final String description, @Nonnull final ArgWrapper<W, D> wrapper, final D defaultValue, @Nonnull final String defaultValueDescription) {
        return wrapper.wrapArg(this.registerOptionalArg(new DefaultArg(this, name, description, wrapper.argumentType(), defaultValue, defaultValueDescription)));
    }
    
    @Nonnull
    public <D> DefaultArg<List<D>> withListDefaultArg(@Nonnull final String name, @Nonnull final String description, @Nonnull final ArgumentType<D> argType, final List<D> defaultValue, @Nonnull final String defaultValueDescription) {
        return this.registerOptionalArg(new DefaultArg<List<D>>(this, name, description, (ArgumentType<List<D>>)new ListArgumentType((ArgumentType<Object>)argType), defaultValue, defaultValueDescription));
    }
    
    @Nonnull
    public <D> OptionalArg<D> withOptionalArg(@Nonnull final String name, @Nonnull final String description, @Nonnull final ArgumentType<D> argType) {
        return this.registerOptionalArg(new OptionalArg<D>(this, name, description, argType));
    }
    
    public <W extends WrappedArg<D>, D> W withOptionalArg(@Nonnull final String name, @Nonnull final String description, @Nonnull final ArgWrapper<W, D> wrapper) {
        return wrapper.wrapArg(this.registerOptionalArg(new OptionalArg(this, name, description, wrapper.argumentType())));
    }
    
    @Nonnull
    public <D> OptionalArg<List<D>> withListOptionalArg(@Nonnull final String name, @Nonnull final String description, @Nonnull final ArgumentType<D> argType) {
        return this.registerOptionalArg(new OptionalArg<List<D>>(this, name, description, (ArgumentType<List<D>>)new ListArgumentType((ArgumentType<Object>)argType)));
    }
    
    @Nonnull
    public FlagArg withFlagArg(@Nonnull final String name, @Nonnull final String description) {
        return this.registerOptionalArg(new FlagArg(this, name, description));
    }
    
    public boolean isVariant() {
        return this.name == null;
    }
    
    @Nullable
    public String getName() {
        return this.name;
    }
    
    @Nonnull
    public Set<String> getAliases() {
        return this.aliases;
    }
    
    public String getDescription() {
        return this.description;
    }
    
    @Nullable
    public CommandOwner getOwner() {
        return this.owner;
    }
    
    @Nullable
    public String getPermission() {
        return this.permission;
    }
    
    @Nonnull
    public Map<String, AbstractCommand> getSubCommands() {
        return this.subCommands;
    }
    
    @Nonnull
    public List<RequiredArg<?>> getRequiredArguments() {
        return this.requiredArguments;
    }
    
    public boolean hasBeenRegistered() {
        return this.hasBeenRegistered;
    }
    
    static {
        LOGGER = HytaleLogger.forEnclosingClass();
        EMPTY_STRING_ARRAY = new String[0];
        MESSAGE_COMMANDS_HELP_NO_PERMISSIBLE_SUB_COMMAND = Message.translation("server.commands.help.noPermissibleSubCommand");
        MESSAGE_COMMANDS_PARSING_ERROR_NO_PERMISSION_FOR_COMMAND = Message.translation("server.commands.parsing.error.noPermissionForCommand");
        MESSAGE_COMMANDS_PARSING_ERROR_ATTEMPTED_UNSAFE = Message.translation("server.commands.parsing.error.attemptedUnsafe");
        MESSAGE_COMMANDS_PARSING_USAGE_REQUIRES_CONFIRMATION = Message.translation("server.commands.parsing.usage.requiresConfirmation");
        MESSAGE_COMMAND_SINGLEPLAYER = Message.translation("server.commands.parsing.error.unavailableInSingleplayer");
    }
    
    // This helper class was generated by Procyon to approximate the behavior of an
    // 'invokedynamic' instruction that it doesn't know how to interpret.
    private static final class ProcyonInvokeDynamicHelper_8
    {
        private static final MethodHandles.Lookup LOOKUP = MethodHandles.lookup();
        private static MethodHandle handle;
        private static volatile int fence;
        
        private static MethodHandle handle() {
            final MethodHandle handle = ProcyonInvokeDynamicHelper_8.handle;
            if (handle != null)
                return handle;
            return ProcyonInvokeDynamicHelper_8.ensureHandle();
        }
        
        private static MethodHandle ensureHandle() {
            ProcyonInvokeDynamicHelper_8.fence = 0;
            MethodHandle handle = ProcyonInvokeDynamicHelper_8.handle;
            if (handle == null) {
                MethodHandles.Lookup lookup = ProcyonInvokeDynamicHelper_8.LOOKUP;
                try {
                    handle = ((CallSite)SwitchBootstraps.typeSwitch(lookup, "typeSwitch", MethodType.methodType(int.class, Object.class, int.class), OptionalArg.class, DefaultArg.class, FlagArg.class)).dynamicInvoker();
                }
                catch (Throwable t) {
                    throw new UndeclaredThrowableException(t);
                }
                ProcyonInvokeDynamicHelper_8.fence = 1;
                ProcyonInvokeDynamicHelper_8.handle = handle;
                ProcyonInvokeDynamicHelper_8.fence = 0;
            }
            return handle;
        }
        
        private static int invoke(Object p0, int p1) {
            try {
                return ProcyonInvokeDynamicHelper_8.handle().invokeExact(p0, p1);
            }
            catch (Throwable t) {
                throw new UndeclaredThrowableException(t);
            }
        }
    }
}
