content: /*
   * Copyright (c) 2016-2017 Daniel Ennis (Aikar) - MIT License
   *
   *  Permission is hereby granted, free of charge, to any person obtaining
   *  a copy of this software and associated documentation files (the
   *  "Software"), to deal in the Software without restriction, including
   *  without limitation the rights to use, copy, modify, merge, publish,
   *  distribute, sublicense, and/or sell copies of the Software, and to
   *  permit persons to whom the Software is furnished to do so, subject to
   *  the following conditions:
   *
   *  The above copyright notice and this permission notice shall be
   *  included in all copies or substantial portions of the Software.
   *
   *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
   *  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
   *  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
   *  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
   *  LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
   *  OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
   *  WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
   */
  
  package co.aikar.commands;
  
  import co.aikar.commands.contexts.CommandResultSupplier;
  import co.aikar.commands.sponge.contexts.OnlinePlayer;
  import net.kyori.adventure.text.format.NamedTextColor;
  import net.kyori.adventure.text.format.TextColor;
  import net.kyori.adventure.text.format.TextDecoration;
  import org.jetbrains.annotations.Contract;
  import org.spongepowered.api.ResourceKey;
  import org.spongepowered.api.Sponge;
  import org.spongepowered.api.command.CommandCause;
  import org.spongepowered.api.entity.living.player.Player;
  import org.spongepowered.api.entity.living.player.User;
  import org.spongepowered.api.entity.living.player.server.ServerPlayer;
  import org.spongepowered.api.user.UserManager;
  import org.spongepowered.api.world.World;
  import org.spongepowered.api.world.server.ServerWorld;
  
  import java.util.Arrays;
  import java.util.HashSet;
  import java.util.Locale;
  import java.util.Optional;
  import java.util.Set;
  import java.util.concurrent.CompletableFuture;
  import java.util.concurrent.ExecutionException;
  import java.util.regex.Pattern;
  import java.util.stream.Collectors;
  
  @SuppressWarnings("WeakerAccess")
  public class SpongeCommandContexts extends CommandContexts<SpongeCommandExecutionContext> {
  
      public SpongeCommandContexts(final SpongeCommandManager manager) {
          super(manager);
  
          registerIssuerOnlyContext(CommandResultSupplier.class, c -> new CommandResultSupplier());
          registerContext(OnlinePlayer.class, c -> getOnlinePlayer(c.getIssuer(), c.popFirstArg(), false));
          registerContext(co.aikar.commands.contexts.OnlinePlayer.class, c -> {
              OnlinePlayer onlinePlayer = getOnlinePlayer(c.getIssuer(), c.popFirstArg(), false);
              return new co.aikar.commands.contexts.OnlinePlayer(onlinePlayer.getPlayer());
          });
          registerContext(User.class, c -> {
              String name = c.popFirstArg();
              UserManager userNamanger = Sponge.server().userManager();
              CompletableFuture<Optional<User>> optionalUser = userNamanger.load(name);
              // Fetch the player's profile
              CompletableFuture<User> completableFuture = optionalUser.thenCompose(user -> {
                  if (user.isPresent()) {
                      return CompletableFuture.completedFuture(user.get());
                  } else {
                      throw new InvalidCommandArgument(MinecraftMessageKeys.NO_PLAYER_FOUND, false, "{search}", name);
                  }
              });
  
              try {
                  return completableFuture.get();
              } catch (InterruptedException | ExecutionException e) {
                  throw new RuntimeException(e);
              }
          });
  
          registerContext(TextColor.class, c -> {
              String arg = c.getFirstArg().toLowerCase();
              TextColor color = NamedTextColor.NAMES.value(arg);
              if (color == null) {
                  color = arg.startsWith("#") ? TextColor.fromHexString(arg) : TextColor.fromHexString("#" + arg);
                  if (color == null) {
                      throw new InvalidCommandArgument("Unknown TextColor syntax");
                  }
  
                  NamedTextColor closest = NamedTextColor.nearestTo(color);
                  if (color.compareTo(closest) == 0) {
                      color = closest;
                  }
              }
  
              String filter = c.getFlagValue("filter", (String) null);
              if (filter != null) {
                  Set<NamedTextColor> colors = Arrays.stream(
                          ACFPatterns.COLON.split(filter))
                          .map(NamedTextColor.NAMES::value)
                          .collect(Collectors.toSet());
                  if (!(color instanceof NamedTextColor) || !colors.contains(color)) {
                      throw new InvalidCommandArgument("Not a valid color");
                  }
              }
  
              return color;
          });
  
          registerContext(TextDecoration.class, c -> {
              String first = c.popFirstArg().toLowerCase(Locale.ROOT);
              TextDecoration decoration = TextDecoration.NAMES.value(first);
  
              if (decoration == null) {
                  throw new InvalidCommandArgument("Unknown TextDecoration syntax");
              }
  
              String filter = c.getFlagValue("filter", (String) null);
              if (filter != null) {
                  Set<TextDecoration> textDecorations = Arrays.stream(
                                  ACFPatterns.COLON.split(filter))
                          .map(TextDecoration.NAMES::value)
                          .collect(Collectors.toSet());
                  if (!textDecorations.contains(decoration)) {
                      throw new InvalidCommandArgument("Not a valid TextDecoration");
                  }
              }
  
              return decoration;
          });
  
          registerIssuerAwareContext(CommandCause.class, (context) -> context.getSource().commandCause());
          registerIssuerAwareContext(SpongeCommandSource.class, SpongeCommandExecutionContext::getSource);
          registerIssuerAwareContext(ServerPlayer.class, (c) -> {
              ServerPlayer serverPlayer = c.getIssuer().getServerPlayer();
              if (serverPlayer == null && !c.isOptional()) {
                  throw new InvalidCommandArgument(MessageKeys.NOT_ALLOWED_ON_CONSOLE, false);
              }
  
              return serverPlayer;
          });
          registerIssuerAwareContext(Player.class, (c) -> {
              Player player = c.getIssuer().getPlayer();
              if (player == null && !c.isOptional()) {
                  throw new InvalidCommandArgument(MessageKeys.NOT_ALLOWED_ON_CONSOLE, false);
              }
              /*PlayerInventory inventory = player != null ? player.getInventory() : null;
              if (inventory != null && c.hasFlag("itemheld") && !ACFBukkitUtil.isValidItem(inventory.getItem(inventory.getHeldItemSlot()))) {
                  throw new InvalidCommandArgument(MinecraftMessageKeys.YOU_MUST_BE_HOLDING_ITEM, false);
              }*/
              return player;
          });
          registerContext(OnlinePlayer[].class, (c) -> {
              SpongeCommandIssuer issuer = c.getIssuer();
              final String search = c.popFirstArg();
              boolean allowMissing = c.hasFlag("allowmissing");
              Set<OnlinePlayer> players = new HashSet<>();
              Pattern split = ACFPatterns.COMMA;
              String splitter = c.getFlagValue("splitter", (String) null);
              if (splitter != null) {
                  split = Pattern.compile(Pattern.quote(splitter));
              }
              for (String lookup : split.split(search)) {
                  OnlinePlayer player = getOnlinePlayer(issuer, lookup, allowMissing);
                  if (player != null) {
                      players.add(player);
                  }
              }
              if (players.isEmpty() && !c.hasFlag("allowempty")) {
                  issuer.sendError(MinecraftMessageKeys.NO_PLAYER_FOUND_SERVER,
                          "{search}", search);
  
                  throw new InvalidCommandArgument(false);
              }
              return players.toArray(new OnlinePlayer[players.size()]);
          });
          registerIssuerAwareContext(World.class, (c) -> {
              String firstArg = c.getFirstArg();
  
              if (firstArg == null) {
                  if (c.isOptional()) {
                      return null;
                  } else {
                      throw new InvalidCommandArgument(MinecraftMessageKeys.INVALID_WORLD);
                  }
              }
  
              ResourceKey worldKey = ResourceKey.of("minecraft", firstArg);
              Optional<ServerWorld> serverWorld = Sponge.server().worldManager().world(worldKey);
  
              Optional<World> world = Optional.empty();
              if (serverWorld.isPresent()) {
                  world = Optional.of( serverWorld.get() );
              }
  
              if (world.isPresent()) {
                  c.popFirstArg();
              } else {
                  if (c.getSource() instanceof Player) {
                      world = java.util.Optional.of(((Player) c.getSource()).world());
                  }
  
                  if (!world.isPresent()) {
                      throw new InvalidCommandArgument(MinecraftMessageKeys.INVALID_WORLD);
                  }
              }
              return world.get();
          });
      }
  
      @Contract("_,_,false -> !null")
      OnlinePlayer getOnlinePlayer(SpongeCommandIssuer issuer, String lookup, boolean allowMissing) throws InvalidCommandArgument {
          Player player = ACFSpongeUtil.findPlayerSmart(issuer, lookup);
          if (player == null) {
              if (allowMissing) {
                  return null;
              }
              throw new InvalidCommandArgument(false);
          }
          return new OnlinePlayer(player);
      }
  }
  