package net.minecraft.commands.arguments.selector;

import net.minecraft.network.chat.ComponentUtils;
import net.minecraft.network.chat.MutableComponent;
import java.util.Collection;
import java.util.Iterator;
import net.minecraft.server.level.ServerLevel;
import com.google.common.collect.Lists;
import net.minecraft.server.level.ServerPlayer;
import java.util.Collections;
import com.mojang.brigadier.exceptions.CommandSyntaxException;
import net.minecraft.commands.arguments.EntityArgument;
import net.minecraft.commands.CommandSourceStack;
import net.minecraft.world.entity.EntityType;
import java.util.UUID;
import java.util.List;
import java.util.function.BiConsumer;
import javax.annotation.Nullable;
import net.minecraft.world.phys.AABB;
import net.minecraft.world.phys.Vec3;
import java.util.function.Function;
import net.minecraft.advancements.critereon.MinMaxBounds;
import net.minecraft.world.entity.Entity;
import java.util.function.Predicate;

public class EntitySelector {
    private final int maxResults;
    private final boolean includesEntities;
    private final boolean worldLimited;
    private final Predicate<Entity> predicate;
    private final MinMaxBounds.Floats range;
    private final Function<Vec3, Vec3> position;
    @Nullable
    private final AABB aabb;
    private final BiConsumer<Vec3, List<? extends Entity>> order;
    private final boolean currentEntity;
    @Nullable
    private final String playerName;
    @Nullable
    private final UUID entityUUID;
    @Nullable
    private final EntityType<?> type;
    private final boolean usesSelector;
    
    public EntitySelector(final int integer, final boolean boolean2, final boolean boolean3, final Predicate<Entity> predicate, final MinMaxBounds.Floats c, final Function<Vec3, Vec3> function, @Nullable final AABB deg, final BiConsumer<Vec3, List<? extends Entity>> biConsumer, final boolean boolean9, @Nullable final String string, @Nullable final UUID uUID, @Nullable final EntityType<?> aoq, final boolean boolean13) {
        this.maxResults = integer;
        this.includesEntities = boolean2;
        this.worldLimited = boolean3;
        this.predicate = predicate;
        this.range = c;
        this.position = function;
        this.aabb = deg;
        this.order = biConsumer;
        this.currentEntity = boolean9;
        this.playerName = string;
        this.entityUUID = uUID;
        this.type = aoq;
        this.usesSelector = boolean13;
    }
    
    public int getMaxResults() {
        return this.maxResults;
    }
    
    public boolean includesEntities() {
        return this.includesEntities;
    }
    
    public boolean isSelfSelector() {
        return this.currentEntity;
    }
    
    public boolean isWorldLimited() {
        return this.worldLimited;
    }
    
    private void checkPermissions(final CommandSourceStack cz) throws CommandSyntaxException {
        if (this.usesSelector && !cz.hasPermission(2)) {
            throw EntityArgument.ERROR_SELECTORS_NOT_ALLOWED.create();
        }
    }
    
    public Entity findSingleEntity(final CommandSourceStack cz) throws CommandSyntaxException {
        this.checkPermissions(cz);
        final List<? extends Entity> list3 = this.findEntities(cz);
        if (list3.isEmpty()) {
            throw EntityArgument.NO_ENTITIES_FOUND.create();
        }
        if (list3.size() > 1) {
            throw EntityArgument.ERROR_NOT_SINGLE_ENTITY.create();
        }
        return (Entity)list3.get(0);
    }
    
    public List<? extends Entity> findEntities(final CommandSourceStack cz) throws CommandSyntaxException {
        this.checkPermissions(cz);
        if (!this.includesEntities) {
            return this.findPlayers(cz);
        }
        if (this.playerName != null) {
            final ServerPlayer ze3 = cz.getServer().getPlayerList().getPlayerByName(this.playerName);
            if (ze3 == null) {
                return Collections.emptyList();
            }
            return Lists.<ServerPlayer>newArrayList(ze3);
        }
        else {
            if (this.entityUUID != null) {
                for (final ServerLevel zd4 : cz.getServer().getAllLevels()) {
                    final Entity aom5 = zd4.getEntity(this.entityUUID);
                    if (aom5 != null) {
                        return Lists.<Entity>newArrayList(aom5);
                    }
                }
                return Collections.emptyList();
            }
            final Vec3 dem3 = this.position.apply(cz.getPosition());
            final Predicate<Entity> predicate4 = this.getPredicate(dem3);
            if (!this.currentEntity) {
                final List<Entity> list5 = Lists.newArrayList();
                if (this.isWorldLimited()) {
                    this.addEntities(list5, cz.getLevel(), dem3, predicate4);
                }
                else {
                    for (final ServerLevel zd5 : cz.getServer().getAllLevels()) {
                        this.addEntities(list5, zd5, dem3, predicate4);
                    }
                }
                return this.sortAndLimit(dem3, list5);
            }
            if (cz.getEntity() != null && predicate4.test(cz.getEntity())) {
                return Lists.<Entity>newArrayList(cz.getEntity());
            }
            return Collections.emptyList();
        }
    }
    
    private void addEntities(final List<Entity> list, final ServerLevel zd, final Vec3 dem, final Predicate<Entity> predicate) {
        if (this.aabb != null) {
            list.addAll(zd.getEntities(this.type, this.aabb.move(dem), predicate));
        }
        else {
            list.addAll(zd.getEntities(this.type, predicate));
        }
    }
    
    public ServerPlayer findSinglePlayer(final CommandSourceStack cz) throws CommandSyntaxException {
        this.checkPermissions(cz);
        final List<ServerPlayer> list3 = this.findPlayers(cz);
        if (list3.size() != 1) {
            throw EntityArgument.NO_PLAYERS_FOUND.create();
        }
        return list3.get(0);
    }
    
    public List<ServerPlayer> findPlayers(final CommandSourceStack cz) throws CommandSyntaxException {
        this.checkPermissions(cz);
        if (this.playerName != null) {
            final ServerPlayer ze3 = cz.getServer().getPlayerList().getPlayerByName(this.playerName);
            if (ze3 == null) {
                return Collections.<ServerPlayer>emptyList();
            }
            return Lists.<ServerPlayer>newArrayList(ze3);
        }
        else if (this.entityUUID != null) {
            final ServerPlayer ze3 = cz.getServer().getPlayerList().getPlayer(this.entityUUID);
            if (ze3 == null) {
                return Collections.<ServerPlayer>emptyList();
            }
            return Lists.<ServerPlayer>newArrayList(ze3);
        }
        else {
            final Vec3 dem3 = this.position.apply(cz.getPosition());
            final Predicate<Entity> predicate4 = this.getPredicate(dem3);
            if (this.currentEntity) {
                if (cz.getEntity() instanceof ServerPlayer) {
                    final ServerPlayer ze4 = (ServerPlayer)cz.getEntity();
                    if (predicate4.test(ze4)) {
                        return Lists.<ServerPlayer>newArrayList(ze4);
                    }
                }
                return Collections.<ServerPlayer>emptyList();
            }
            List<ServerPlayer> list5;
            if (this.isWorldLimited()) {
                list5 = cz.getLevel().getPlayers(predicate4::test);
            }
            else {
                list5 = Lists.newArrayList();
                for (final ServerPlayer ze5 : cz.getServer().getPlayerList().getPlayers()) {
                    if (predicate4.test(ze5)) {
                        list5.add(ze5);
                    }
                }
            }
            return this.<ServerPlayer>sortAndLimit(dem3, list5);
        }
    }
    
    private Predicate<Entity> getPredicate(final Vec3 dem) {
        Predicate<Entity> predicate3 = this.predicate;
        if (this.aabb != null) {
            final AABB deg4 = this.aabb.move(dem);
            predicate3 = predicate3.and(aom -> deg4.intersects(aom.getBoundingBox()));
        }
        if (!this.range.isAny()) {
            predicate3 = predicate3.and(aom -> this.range.matchesSqr(aom.distanceToSqr(dem)));
        }
        return predicate3;
    }
    
    private <T extends Entity> List<T> sortAndLimit(final Vec3 dem, final List<T> list) {
        if (list.size() > 1) {
            this.order.accept(dem, list);
        }
        return list.subList(0, Math.min(this.maxResults, list.size()));
    }
    
    public static MutableComponent joinNames(final List<? extends Entity> list) {
        return ComponentUtils.formatList(list, Entity::getDisplayName);
    }
}
