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