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.function.Function;
import java.util.Objects;
import java.util.stream.Stream;
import java.util.Iterator;
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 aom, final AABB deg, @Nullable final Predicate<? super Entity> predicate);
    
     <T extends Entity> List<T> getEntitiesOfClass(final Class<? extends T> class1, final AABB deg, @Nullable final Predicate<? super T> predicate);
    
    default <T extends Entity> List<T> getLoadedEntitiesOfClass(final Class<? extends T> class1, final AABB deg, @Nullable final Predicate<? super T> predicate) {
        return this.getEntitiesOfClass(class1, deg, predicate);
    }
    
    List<? extends Player> players();
    
    default List<Entity> getEntities(@Nullable final Entity aom, final AABB deg) {
        return this.getEntities(aom, deg, EntitySelector.NO_SPECTATORS);
    }
    
    default boolean isUnobstructed(@Nullable final Entity aom, final VoxelShape dfg) {
        if (dfg.isEmpty()) {
            return true;
        }
        for (final Entity aom2 : this.getEntities(aom, dfg.bounds())) {
            if (!aom2.removed && aom2.blocksBuilding && (aom == null || !aom2.isPassengerOfSameVehicle(aom)) && Shapes.joinIsNotEmpty(dfg, Shapes.create(aom2.getBoundingBox()), BooleanOp.AND)) {
                return false;
            }
        }
        return true;
    }
    
    default <T extends Entity> List<T> getEntitiesOfClass(final Class<? extends T> class1, final AABB deg) {
        return this.<T>getEntitiesOfClass(class1, deg, EntitySelector.NO_SPECTATORS);
    }
    
    default <T extends Entity> List<T> getLoadedEntitiesOfClass(final Class<? extends T> class1, final AABB deg) {
        return this.<T>getLoadedEntitiesOfClass(class1, deg, EntitySelector.NO_SPECTATORS);
    }
    
    default Stream<VoxelShape> getEntityCollisions(@Nullable final Entity aom, final AABB deg, final Predicate<Entity> predicate) {
        if (deg.getSize() < 1.0E-7) {
            return Stream.<VoxelShape>empty();
        }
        final AABB deg2 = deg.inflate(1.0E-7);
        AABB deg3;
        final AABB deg4;
        return this.getEntities(aom, deg2, predicate.and(aom2 -> aom == null || !aom.isPassengerOfSameVehicle(aom2))).stream().flatMap(aom3 -> {
            if (aom != null) {
                deg3 = aom.getCollideAgainstBox(aom3);
                if (deg3 != null && deg3.intersects(deg4)) {
                    return Stream.<AABB>of(new AABB[] { aom3.getCollideBox(), deg3 });
                }
            }
            return Stream.<AABB>of(aom3.getCollideBox());
        }).filter(Objects::nonNull).<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 bec13 = null;
        for (final Player bec14 : this.players()) {
            if (predicate != null && !predicate.test(bec14)) {
                continue;
            }
            final double double6 = bec14.distanceToSqr(double1, double2, double3);
            if ((double4 >= 0.0 && double6 >= double4 * double4) || (double5 != -1.0 && double6 >= double5)) {
                continue;
            }
            double5 = double6;
            bec13 = bec14;
        }
        return bec13;
    }
    
    @Nullable
    default Player getNearestPlayer(final Entity aom, final double double2) {
        return this.getNearestPlayer(aom.getX(), aom.getY(), aom.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);
    }
    
    default boolean hasNearbyAlivePlayer(final double double1, final double double2, final double double3, final double double4) {
        for (final Player bec11 : this.players()) {
            if (EntitySelector.NO_SPECTATORS.test(bec11)) {
                if (!EntitySelector.LIVING_ENTITY_STILL_ALIVE.test(bec11)) {
                    continue;
                }
                final double double5 = bec11.distanceToSqr(double1, double2, double3);
                if (double4 < 0.0 || double5 < double4 * double4) {
                    return true;
                }
                continue;
            }
        }
        return false;
    }
    
    @Nullable
    default Player getNearestPlayer(final TargetingConditions axs, final LivingEntity aoy) {
        return this.<Player>getNearestEntity(this.players(), axs, aoy, aoy.getX(), aoy.getY(), aoy.getZ());
    }
    
    @Nullable
    default Player getNearestPlayer(final TargetingConditions axs, final LivingEntity aoy, final double double3, final double double4, final double double5) {
        return this.<Player>getNearestEntity(this.players(), axs, aoy, double3, double4, double5);
    }
    
    @Nullable
    default Player getNearestPlayer(final TargetingConditions axs, final double double2, final double double3, final double double4) {
        return this.<Player>getNearestEntity(this.players(), axs, (LivingEntity)null, double2, double3, double4);
    }
    
    @Nullable
    default <T extends LivingEntity> T getNearestEntity(final Class<? extends T> class1, final TargetingConditions axs, @Nullable final LivingEntity aoy, final double double4, final double double5, final double double6, final AABB deg) {
        return this.<T>getNearestEntity(this.getEntitiesOfClass(class1, deg, null), axs, aoy, double4, double5, double6);
    }
    
    @Nullable
    default <T extends LivingEntity> T getNearestLoadedEntity(final Class<? extends T> class1, final TargetingConditions axs, @Nullable final LivingEntity aoy, final double double4, final double double5, final double double6, final AABB deg) {
        return this.<T>getNearestEntity(this.getLoadedEntitiesOfClass(class1, deg, null), axs, aoy, double4, double5, double6);
    }
    
    @Nullable
    default <T extends LivingEntity> T getNearestEntity(final List<? extends T> list, final TargetingConditions axs, @Nullable final LivingEntity aoy, final double double4, final double double5, final double double6) {
        double double7 = -1.0;
        T aoy2 = null;
        for (final T aoy3 : list) {
            if (!axs.test(aoy, aoy3)) {
                continue;
            }
            final double double8 = aoy3.distanceToSqr(double4, double5, double6);
            if (double7 != -1.0 && double8 >= double7) {
                continue;
            }
            double7 = double8;
            aoy2 = aoy3;
        }
        return aoy2;
    }
    
    default List<Player> getNearbyPlayers(final TargetingConditions axs, final LivingEntity aoy, final AABB deg) {
        final List<Player> list5 = Lists.newArrayList();
        for (final Player bec7 : this.players()) {
            if (deg.contains(bec7.getX(), bec7.getY(), bec7.getZ()) && axs.test(aoy, bec7)) {
                list5.add(bec7);
            }
        }
        return list5;
    }
    
    default <T extends LivingEntity> List<T> getNearbyEntities(final Class<? extends T> class1, final TargetingConditions axs, final LivingEntity aoy, final AABB deg) {
        final List<T> list6 = this.<T>getEntitiesOfClass(class1, deg, null);
        final List<T> list7 = Lists.newArrayList();
        for (final T aoy2 : list6) {
            if (axs.test(aoy, aoy2)) {
                list7.add(aoy2);
            }
        }
        return list7;
    }
    
    @Nullable
    default Player getPlayerByUUID(final UUID uUID) {
        for (int integer3 = 0; integer3 < this.players().size(); ++integer3) {
            final Player bec4 = (Player)this.players().get(integer3);
            if (uUID.equals(bec4.getUUID())) {
                return bec4;
            }
        }
        return null;
    }
}
