package net.minecraft.world.level;

import java.util.UUID;
import com.google.common.collect.Lists;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.ai.targeting.TargetingConditions;
import java.util.Iterator;
import java.util.function.Function;
import java.util.Objects;
import java.util.stream.Stream;
import java.util.Set;
import net.minecraft.world.phys.shapes.BooleanOp;
import net.minecraft.world.phys.shapes.Shapes;
import net.minecraft.world.phys.shapes.VoxelShape;
import net.minecraft.world.entity.EntitySelector;
import net.minecraft.world.entity.player.Player;
import java.util.List;
import java.util.function.Predicate;
import net.minecraft.world.phys.AABB;
import javax.annotation.Nullable;
import net.minecraft.world.entity.Entity;

public interface EntityGetter {
    List<Entity> getEntities(@Nullable final Entity akq, final AABB cvf, @Nullable final Predicate<? super Entity> predicate);
    
     <T extends Entity> List<T> getEntitiesOfClass(final Class<? extends T> class1, final AABB cvf, @Nullable final Predicate<? super T> predicate);
    
    default <T extends Entity> List<T> getLoadedEntitiesOfClass(final Class<? extends T> class1, final AABB cvf, @Nullable final Predicate<? super T> predicate) {
        return this.getEntitiesOfClass(class1, cvf, predicate);
    }
    
    List<? extends Player> players();
    
    default List<Entity> getEntities(@Nullable final Entity akq, final AABB cvf) {
        return this.getEntities(akq, cvf, EntitySelector.NO_SPECTATORS);
    }
    
    default boolean isUnobstructed(@Nullable final Entity akq, final VoxelShape cwf) {
        return cwf.isEmpty() || this.getEntities(akq, cwf.bounds()).stream().filter(akq2 -> !akq2.removed && akq2.blocksBuilding && (akq == null || !akq2.isPassengerOfSameVehicle(akq))).noneMatch(akq -> Shapes.joinIsNotEmpty(cwf, Shapes.create(akq.getBoundingBox()), BooleanOp.AND));
    }
    
    default <T extends Entity> List<T> getEntitiesOfClass(final Class<? extends T> class1, final AABB cvf) {
        return this.<T>getEntitiesOfClass(class1, cvf, EntitySelector.NO_SPECTATORS);
    }
    
    default <T extends Entity> List<T> getLoadedEntitiesOfClass(final Class<? extends T> class1, final AABB cvf) {
        return this.<T>getLoadedEntitiesOfClass(class1, cvf, EntitySelector.NO_SPECTATORS);
    }
    
    default Stream<VoxelShape> getEntityCollisions(@Nullable final Entity akq, final AABB cvf, final Set<Entity> set) {
        if (cvf.getSize() < 1.0E-7) {
            return Stream.<VoxelShape>empty();
        }
        final AABB cvf2 = cvf.inflate(1.0E-7);
        return this.getEntities(akq, cvf2).stream().filter(akq -> !set.contains(akq)).filter(akq2 -> akq == null || !akq.isPassengerOfSameVehicle(akq2)).flatMap(akq2 -> Stream.<AABB>of(new AABB[] { akq2.getCollideBox(), (akq == null) ? null : akq.getCollideAgainstBox(akq2) })).filter(Objects::nonNull).filter(cvf2::intersects).<VoxelShape>map(Shapes::create);
    }
    
    @Nullable
    default Player getNearestPlayer(final double double1, final double double2, final double double3, final double double4, @Nullable final Predicate<Entity> predicate) {
        double double5 = -1.0;
        Player ayj13 = null;
        for (final Player ayj14 : this.players()) {
            if (predicate != null && !predicate.test(ayj14)) {
                continue;
            }
            final double double6 = ayj14.distanceToSqr(double1, double2, double3);
            if ((double4 >= 0.0 && double6 >= double4 * double4) || (double5 != -1.0 && double6 >= double5)) {
                continue;
            }
            double5 = double6;
            ayj13 = ayj14;
        }
        return ayj13;
    }
    
    @Nullable
    default Player getNearestPlayer(final Entity akq, final double double2) {
        return this.getNearestPlayer(akq.getX(), akq.getY(), akq.getZ(), double2, false);
    }
    
    @Nullable
    default Player getNearestPlayer(final double double1, final double double2, final double double3, final double double4, final boolean boolean5) {
        final Predicate<Entity> predicate11 = boolean5 ? EntitySelector.NO_CREATIVE_OR_SPECTATOR : EntitySelector.NO_SPECTATORS;
        return this.getNearestPlayer(double1, double2, double3, double4, predicate11);
    }
    
    @Nullable
    default Player getNearestPlayerIgnoreY(final double double1, final double double2, final double double3) {
        double double4 = -1.0;
        Player ayj10 = null;
        for (final Player ayj11 : this.players()) {
            if (!EntitySelector.NO_SPECTATORS.test(ayj11)) {
                continue;
            }
            final double double5 = ayj11.distanceToSqr(double1, ayj11.getY(), double2);
            if ((double3 >= 0.0 && double5 >= double3 * double3) || (double4 != -1.0 && double5 >= double4)) {
                continue;
            }
            double4 = double5;
            ayj10 = ayj11;
        }
        return ayj10;
    }
    
    default boolean hasNearbyAlivePlayer(final double double1, final double double2, final double double3, final double double4) {
        for (final Player ayj11 : this.players()) {
            if (EntitySelector.NO_SPECTATORS.test(ayj11)) {
                if (!EntitySelector.LIVING_ENTITY_STILL_ALIVE.test(ayj11)) {
                    continue;
                }
                final double double5 = ayj11.distanceToSqr(double1, double2, double3);
                if (double4 < 0.0 || double5 < double4 * double4) {
                    return true;
                }
                continue;
            }
        }
        return false;
    }
    
    @Nullable
    default Player getNearestPlayer(final TargetingConditions ask, final LivingEntity akz) {
        return this.<Player>getNearestEntity(this.players(), ask, akz, akz.getX(), akz.getY(), akz.getZ());
    }
    
    @Nullable
    default Player getNearestPlayer(final TargetingConditions ask, final LivingEntity akz, final double double3, final double double4, final double double5) {
        return this.<Player>getNearestEntity(this.players(), ask, akz, double3, double4, double5);
    }
    
    @Nullable
    default Player getNearestPlayer(final TargetingConditions ask, final double double2, final double double3, final double double4) {
        return this.<Player>getNearestEntity(this.players(), ask, (LivingEntity)null, double2, double3, double4);
    }
    
    @Nullable
    default <T extends LivingEntity> T getNearestEntity(final Class<? extends T> class1, final TargetingConditions ask, @Nullable final LivingEntity akz, final double double4, final double double5, final double double6, final AABB cvf) {
        return this.<T>getNearestEntity(this.getEntitiesOfClass(class1, cvf, null), ask, akz, double4, double5, double6);
    }
    
    @Nullable
    default <T extends LivingEntity> T getNearestLoadedEntity(final Class<? extends T> class1, final TargetingConditions ask, @Nullable final LivingEntity akz, final double double4, final double double5, final double double6, final AABB cvf) {
        return this.<T>getNearestEntity(this.getLoadedEntitiesOfClass(class1, cvf, null), ask, akz, double4, double5, double6);
    }
    
    @Nullable
    default <T extends LivingEntity> T getNearestEntity(final List<? extends T> list, final TargetingConditions ask, @Nullable final LivingEntity akz, final double double4, final double double5, final double double6) {
        double double7 = -1.0;
        T akz2 = null;
        for (final T akz3 : list) {
            if (!ask.test(akz, akz3)) {
                continue;
            }
            final double double8 = akz3.distanceToSqr(double4, double5, double6);
            if (double7 != -1.0 && double8 >= double7) {
                continue;
            }
            double7 = double8;
            akz2 = akz3;
        }
        return akz2;
    }
    
    default List<Player> getNearbyPlayers(final TargetingConditions ask, final LivingEntity akz, final AABB cvf) {
        final List<Player> list5 = Lists.newArrayList();
        for (final Player ayj7 : this.players()) {
            if (cvf.contains(ayj7.getX(), ayj7.getY(), ayj7.getZ()) && ask.test(akz, ayj7)) {
                list5.add(ayj7);
            }
        }
        return list5;
    }
    
    default <T extends LivingEntity> List<T> getNearbyEntities(final Class<? extends T> class1, final TargetingConditions ask, final LivingEntity akz, final AABB cvf) {
        final List<T> list6 = this.<T>getEntitiesOfClass(class1, cvf, null);
        final List<T> list7 = Lists.newArrayList();
        for (final T akz2 : list6) {
            if (ask.test(akz, akz2)) {
                list7.add(akz2);
            }
        }
        return list7;
    }
    
    @Nullable
    default Player getPlayerByUUID(final UUID uUID) {
        for (int integer3 = 0; integer3 < this.players().size(); ++integer3) {
            final Player ayj4 = (Player)this.players().get(integer3);
            if (uUID.equals(ayj4.getUUID())) {
                return ayj4;
            }
        }
        return null;
    }
}
