001//
002// MIT License
003//
004// Copyright (c) 2021 Alexander Söderberg & Contributors
005//
006// Permission is hereby granted, free of charge, to any person obtaining a copy
007// of this software and associated documentation files (the "Software"), to deal
008// in the Software without restriction, including without limitation the rights
009// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
010// copies of the Software, and to permit persons to whom the Software is
011// furnished to do so, subject to the following conditions:
012//
013// The above copyright notice and this permission notice shall be included in all
014// copies or substantial portions of the Software.
015//
016// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
017// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
018// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
019// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
020// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
021// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
022// SOFTWARE.
023//
024package cloud.commandframework.bukkit.parsers.selector;
025
026import cloud.commandframework.ArgumentDescription;
027import cloud.commandframework.arguments.CommandArgument;
028import cloud.commandframework.arguments.parser.ArgumentParseResult;
029import cloud.commandframework.arguments.parser.ArgumentParser;
030import cloud.commandframework.bukkit.CloudBukkitCapabilities;
031import cloud.commandframework.bukkit.arguments.selector.SinglePlayerSelector;
032import cloud.commandframework.bukkit.parsers.PlayerArgument;
033import cloud.commandframework.context.CommandContext;
034import cloud.commandframework.exceptions.parsing.NoInputProvidedException;
035import com.google.common.collect.ImmutableList;
036import org.bukkit.Bukkit;
037import org.bukkit.entity.Entity;
038import org.bukkit.entity.Player;
039import org.checkerframework.checker.nullness.qual.NonNull;
040import org.checkerframework.checker.nullness.qual.Nullable;
041
042import java.util.ArrayList;
043import java.util.List;
044import java.util.Queue;
045import java.util.Set;
046import java.util.function.BiFunction;
047
048public final class SinglePlayerSelectorArgument<C> extends CommandArgument<C, SinglePlayerSelector> {
049
050    private SinglePlayerSelectorArgument(
051            final boolean required,
052            final @NonNull String name,
053            final @NonNull String defaultValue,
054            final @Nullable BiFunction<@NonNull CommandContext<C>, @NonNull String,
055                    @NonNull List<@NonNull String>> suggestionsProvider,
056            final @NonNull ArgumentDescription defaultDescription
057    ) {
058        super(
059                required,
060                name,
061                new SinglePlayerSelectorParser<>(),
062                defaultValue,
063                SinglePlayerSelector.class,
064                suggestionsProvider,
065                defaultDescription
066        );
067    }
068
069    /**
070     * Create a new builder
071     *
072     * @param name Name of the argument
073     * @param <C>  Command sender type
074     * @return Created builder
075     */
076    public static <C> SinglePlayerSelectorArgument.@NonNull Builder<C> newBuilder(final @NonNull String name) {
077        return new SinglePlayerSelectorArgument.Builder<>(name);
078    }
079
080    /**
081     * Create a new required command argument
082     *
083     * @param name Argument name
084     * @param <C>  Command sender type
085     * @return Created argument
086     */
087    public static <C> @NonNull CommandArgument<C, SinglePlayerSelector> of(final @NonNull String name) {
088        return SinglePlayerSelectorArgument.<C>newBuilder(name).asRequired().build();
089    }
090
091    /**
092     * Create a new optional command argument
093     *
094     * @param name Argument name
095     * @param <C>  Command sender type
096     * @return Created argument
097     */
098    public static <C> @NonNull CommandArgument<C, SinglePlayerSelector> optional(final @NonNull String name) {
099        return SinglePlayerSelectorArgument.<C>newBuilder(name).asOptional().build();
100    }
101
102    /**
103     * Create a new required command argument with a default value
104     *
105     * @param name                  Argument name
106     * @param defaultEntitySelector Default player
107     * @param <C>                   Command sender type
108     * @return Created argument
109     */
110    public static <C> @NonNull CommandArgument<C, SinglePlayerSelector> optional(
111            final @NonNull String name,
112            final @NonNull String defaultEntitySelector
113    ) {
114        return SinglePlayerSelectorArgument.<C>newBuilder(name).asOptionalWithDefault(defaultEntitySelector).build();
115    }
116
117
118    public static final class Builder<C> extends CommandArgument.Builder<C, SinglePlayerSelector> {
119
120        private Builder(final @NonNull String name) {
121            super(SinglePlayerSelector.class, name);
122        }
123
124        /**
125         * Builder a new argument
126         *
127         * @return Constructed argument
128         */
129        @Override
130        public @NonNull SinglePlayerSelectorArgument<C> build() {
131            return new SinglePlayerSelectorArgument<>(this.isRequired(), this.getName(), this.getDefaultValue(),
132                    this.getSuggestionsProvider(), this.getDefaultDescription()
133            );
134        }
135
136    }
137
138
139    public static final class SinglePlayerSelectorParser<C> implements ArgumentParser<C, SinglePlayerSelector> {
140
141        @Override
142        public @NonNull ArgumentParseResult<SinglePlayerSelector> parse(
143                final @NonNull CommandContext<C> commandContext,
144                final @NonNull Queue<@NonNull String> inputQueue
145        ) {
146            final String input = inputQueue.peek();
147            if (input == null) {
148                return ArgumentParseResult.failure(new NoInputProvidedException(
149                        SinglePlayerSelectorParser.class,
150                        commandContext
151                ));
152            }
153            inputQueue.remove();
154
155            if (!commandContext.<Set<CloudBukkitCapabilities>>get("CloudBukkitCapabilities").contains(
156                    CloudBukkitCapabilities.BRIGADIER)) {
157                @SuppressWarnings("deprecation")
158                Player player = Bukkit.getPlayer(input);
159
160                if (player == null) {
161                    return ArgumentParseResult.failure(new PlayerArgument.PlayerParseException(input, commandContext));
162                }
163                return ArgumentParseResult.success(new SinglePlayerSelector(input, ImmutableList.of(player)));
164            }
165
166            List<Entity> entities;
167            try {
168                entities = Bukkit.selectEntities(commandContext.get("BukkitCommandSender"), input);
169            } catch (IllegalArgumentException e) {
170                return ArgumentParseResult.failure(new SelectorParseException(
171                        input,
172                        commandContext,
173                        SelectorParseException.FailureReason.MALFORMED_SELECTOR,
174                        SinglePlayerSelectorParser.class
175                ));
176            }
177
178            for (Entity e : entities) {
179                if (!(e instanceof Player)) {
180                    return ArgumentParseResult.failure(new SelectorParseException(
181                            input,
182                            commandContext,
183                            SelectorParseException.FailureReason.NON_PLAYER_IN_PLAYER_SELECTOR,
184                            SinglePlayerSelectorParser.class
185                    ));
186                }
187            }
188            if (entities.size() > 1) {
189                return ArgumentParseResult.failure(new SelectorParseException(
190                        input,
191                        commandContext,
192                        SelectorParseException.FailureReason.TOO_MANY_PLAYERS,
193                        SinglePlayerSelectorParser.class
194                ));
195            }
196
197            return ArgumentParseResult.success(new SinglePlayerSelector(input, entities));
198        }
199
200        @Override
201        public @NonNull List<@NonNull String> suggestions(
202                final @NonNull CommandContext<C> commandContext,
203                final @NonNull String input
204        ) {
205            List<String> output = new ArrayList<>();
206
207            for (Player player : Bukkit.getOnlinePlayers()) {
208                output.add(player.getName());
209            }
210
211            return output;
212        }
213
214    }
215
216}