package net.minecraft.commands.arguments.selector;

import net.minecraft.network.chat.ComponentUtils;
import net.minecraft.network.chat.Component;
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 cvf, final BiConsumer<Vec3, List<? extends Entity>> biConsumer, final boolean boolean9, @Nullable final String string, @Nullable final UUID uUID, @Nullable final EntityType<?> aku, final boolean boolean13) {
        this.maxResults = integer;
        this.includesEntities = boolean2;
        this.worldLimited = boolean3;
        this.predicate = predicate;
        this.range = c;
        this.position = function;
        this.aabb = cvf;
        this.order = biConsumer;
        this.currentEntity = boolean9;
        this.playerName = string;
        this.entityUUID = uUID;
        this.type = aku;
        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 cq) throws CommandSyntaxException {
        if (this.usesSelector && !cq.hasPermission(2)) {
            throw EntityArgument.ERROR_SELECTORS_NOT_ALLOWED.create();
        }
    }
    
    public Entity findSingleEntity(final CommandSourceStack cq) throws CommandSyntaxException {
        this.checkPermissions(cq);
        final List<? extends Entity> list3 = this.findEntities(cq);
        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 cq) throws CommandSyntaxException {
        this.checkPermissions(cq);
        if (!this.includesEntities) {
            return this.findPlayers(cq);
        }
        if (this.playerName != null) {
            final ServerPlayer xe3 = cq.getServer().getPlayerList().getPlayerByName(this.playerName);
            if (xe3 == null) {
                return Collections.emptyList();
            }
            return Lists.<ServerPlayer>newArrayList(xe3);
        }
        else {
            if (this.entityUUID != null) {
                for (final ServerLevel xd4 : cq.getServer().getAllLevels()) {
                    final Entity akq5 = xd4.getEntity(this.entityUUID);
                    if (akq5 != null) {
                        return Lists.<Entity>newArrayList(akq5);
                    }
                }
                return Collections.emptyList();
            }
            final Vec3 cvl3 = this.position.apply(cq.getPosition());
            final Predicate<Entity> predicate4 = this.getPredicate(cvl3);
            if (!this.currentEntity) {
                final List<Entity> list5 = Lists.newArrayList();
                if (this.isWorldLimited()) {
                    this.addEntities(list5, cq.getLevel(), cvl3, predicate4);
                }
                else {
                    for (final ServerLevel xd5 : cq.getServer().getAllLevels()) {
                        this.addEntities(list5, xd5, cvl3, predicate4);
                    }
                }
                return this.sortAndLimit(cvl3, list5);
            }
            if (cq.getEntity() != null && predicate4.test(cq.getEntity())) {
                return Lists.<Entity>newArrayList(cq.getEntity());
            }
            return Collections.emptyList();
        }
    }
    
    private void addEntities(final List<Entity> list, final ServerLevel xd, final Vec3 cvl, final Predicate<Entity> predicate) {
        if (this.aabb != null) {
            list.addAll(xd.getEntities(this.type, this.aabb.move(cvl), predicate));
        }
        else {
            list.addAll(xd.getEntities(this.type, predicate));
        }
    }
    
    public ServerPlayer findSinglePlayer(final CommandSourceStack cq) throws CommandSyntaxException {
        this.checkPermissions(cq);
        final List<ServerPlayer> list3 = this.findPlayers(cq);
        if (list3.size() != 1) {
            throw EntityArgument.NO_PLAYERS_FOUND.create();
        }
        return list3.get(0);
    }
    
    public List<ServerPlayer> findPlayers(final CommandSourceStack cq) throws CommandSyntaxException {
        this.checkPermissions(cq);
        if (this.playerName != null) {
            final ServerPlayer xe3 = cq.getServer().getPlayerList().getPlayerByName(this.playerName);
            if (xe3 == null) {
                return Collections.<ServerPlayer>emptyList();
            }
            return Lists.<ServerPlayer>newArrayList(xe3);
        }
        else if (this.entityUUID != null) {
            final ServerPlayer xe3 = cq.getServer().getPlayerList().getPlayer(this.entityUUID);
            if (xe3 == null) {
                return Collections.<ServerPlayer>emptyList();
            }
            return Lists.<ServerPlayer>newArrayList(xe3);
        }
        else {
            final Vec3 cvl3 = this.position.apply(cq.getPosition());
            final Predicate<Entity> predicate4 = this.getPredicate(cvl3);
            if (this.currentEntity) {
                if (cq.getEntity() instanceof ServerPlayer) {
                    final ServerPlayer xe4 = (ServerPlayer)cq.getEntity();
                    if (predicate4.test(xe4)) {
                        return Lists.<ServerPlayer>newArrayList(xe4);
                    }
                }
                return Collections.<ServerPlayer>emptyList();
            }
            List<ServerPlayer> list5;
            if (this.isWorldLimited()) {
                list5 = cq.getLevel().getPlayers(predicate4::test);
            }
            else {
                list5 = Lists.newArrayList();
                for (final ServerPlayer xe5 : cq.getServer().getPlayerList().getPlayers()) {
                    if (predicate4.test(xe5)) {
                        list5.add(xe5);
                    }
                }
            }
            return this.<ServerPlayer>sortAndLimit(cvl3, list5);
        }
    }
    
    private Predicate<Entity> getPredicate(final Vec3 cvl) {
        Predicate<Entity> predicate3 = this.predicate;
        if (this.aabb != null) {
            final AABB cvf4 = this.aabb.move(cvl);
            predicate3 = predicate3.and(akq -> cvf4.intersects(akq.getBoundingBox()));
        }
        if (!this.range.isAny()) {
            predicate3 = predicate3.and(akq -> this.range.matchesSqr(akq.distanceToSqr(cvl)));
        }
        return predicate3;
    }
    
    private <T extends Entity> List<T> sortAndLimit(final Vec3 cvl, final List<T> list) {
        if (list.size() > 1) {
            this.order.accept(cvl, list);
        }
        return list.subList(0, Math.min(this.maxResults, list.size()));
    }
    
    public static Component joinNames(final List<? extends Entity> list) {
        return ComponentUtils.formatList(list, Entity::getDisplayName);
    }
}
