001package co.aikar.commands;
002
003import com.google.common.collect.Iterables;
004import org.jetbrains.annotations.NotNull;
005import org.spongepowered.api.Sponge;
006import org.spongepowered.api.command.CommandSource;
007import org.spongepowered.api.entity.living.player.Player;
008
009import java.util.ArrayList;
010import java.util.Iterator;
011import java.util.List;
012import java.util.stream.Collectors;
013
014@SuppressWarnings("WeakerAccess")
015public class ACFSpongeUtil {
016    public static Player findPlayerSmart(CommandIssuer issuer, String search) {
017        CommandSource requester = issuer.getIssuer();
018        if (search == null) {
019            return null;
020        }
021        String name = ACFUtil.replace(search, ":confirm", "");
022        if (name.length() < 3) {
023            issuer.sendError(MinecraftMessageKeys.USERNAME_TOO_SHORT);
024            return null;
025        }
026        if (!isValidName(name)) {
027            issuer.sendError(MinecraftMessageKeys.IS_NOT_A_VALID_NAME, "{name}", name);
028            return null;
029        }
030
031        List<Player> matches = matchPlayer(name);
032        List<Player> confirmList = new ArrayList<>();
033        findMatches(search, requester, matches, confirmList);
034
035
036        if (matches.size() > 1 || confirmList.size() > 1) {
037            String allMatches = matches.stream().map(Player::getName).collect(Collectors.joining(", "));
038            issuer.sendError(MinecraftMessageKeys.MULTIPLE_PLAYERS_MATCH,
039                    "{search}", name, "{all}", allMatches);
040            return null;
041        }
042
043        if (matches.isEmpty()) {
044            if (confirmList.isEmpty()) {
045                issuer.sendError(MinecraftMessageKeys.NO_PLAYER_FOUND_SERVER,
046                        "{search}", name);
047                return null;
048            } else {
049                Player player = Iterables.getOnlyElement(confirmList);
050                issuer.sendInfo(MinecraftMessageKeys.PLAYER_IS_VANISHED_CONFIRM, "{vanished}", player.getName());
051                return null;
052            }
053        }
054
055        return matches.get(0);
056    }
057
058    private static void findMatches(String search, CommandSource requester, List<Player> matches, List<Player> confirmList) {
059        // Remove vanished players from smart matching.
060        Iterator<Player> iter = matches.iterator();
061        //noinspection Duplicates
062        while (iter.hasNext()) {
063            Player player = iter.next();
064            if (requester instanceof Player && !((Player) requester).canSee(player)) {
065                if (requester.hasPermission("acf.seevanish")) {
066                    if (!search.endsWith(":confirm")) {
067                        confirmList.add(player);
068                        iter.remove();
069                    }
070                } else {
071                    iter.remove();
072                }
073            }
074        }
075    }
076
077    public static List<Player> matchPlayer(String partialName) {
078        List<Player> matchedPlayers = new ArrayList<Player>();
079
080        for (Player iterPlayer : Sponge.getServer().getOnlinePlayers()) {
081            String iterPlayerName = iterPlayer.getName();
082
083            if (partialName.equalsIgnoreCase(iterPlayerName)) {
084                // Exact match
085                matchedPlayers.clear();
086                matchedPlayers.add(iterPlayer);
087                break;
088            }
089            if (iterPlayerName.toLowerCase(java.util.Locale.ENGLISH).contains(partialName.toLowerCase(java.util.Locale.ENGLISH))) {
090                // Partial match
091                matchedPlayers.add(iterPlayer);
092            }
093        }
094
095        return matchedPlayers;
096    }
097
098    public static boolean isValidName(String name) {
099        return name != null && !name.isEmpty() && ACFPatterns.VALID_NAME_PATTERN.matcher(name).matches();
100    }
101
102}