package net.minecraft.world.level;

import java.util.Spliterator;
import java.util.stream.StreamSupport;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.phys.shapes.BooleanOp;
import java.util.function.Consumer;
import java.util.Spliterators;
import net.minecraft.core.Cursor3D;
import net.minecraft.util.Mth;
import com.google.common.collect.Streams;
import java.util.stream.Stream;
import java.util.Set;
import java.util.Collections;
import net.minecraft.world.phys.AABB;
import net.minecraft.world.phys.shapes.Shapes;
import net.minecraft.world.phys.shapes.CollisionContext;
import net.minecraft.core.BlockPos;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.phys.shapes.VoxelShape;
import net.minecraft.world.entity.Entity;
import javax.annotation.Nullable;
import net.minecraft.world.level.border.WorldBorder;

public interface CollisionGetter extends BlockGetter {
    WorldBorder getWorldBorder();
    
    @Nullable
    BlockGetter getChunkForCollisions(final int integer1, final int integer2);
    
    default boolean isUnobstructed(@Nullable final Entity akq, final VoxelShape cwf) {
        return true;
    }
    
    default boolean isUnobstructed(final BlockState byj, final BlockPos fk, final CollisionContext cvq) {
        final VoxelShape cwf5 = byj.getCollisionShape(this, fk, cvq);
        return cwf5.isEmpty() || this.isUnobstructed(null, cwf5.move(fk.getX(), fk.getY(), fk.getZ()));
    }
    
    default boolean isUnobstructed(final Entity akq) {
        return this.isUnobstructed(akq, Shapes.create(akq.getBoundingBox()));
    }
    
    default boolean noCollision(final AABB cvf) {
        return this.noCollision(null, cvf, Collections.<Entity>emptySet());
    }
    
    default boolean noCollision(final Entity akq) {
        return this.noCollision(akq, akq.getBoundingBox(), Collections.<Entity>emptySet());
    }
    
    default boolean noCollision(final Entity akq, final AABB cvf) {
        return this.noCollision(akq, cvf, Collections.<Entity>emptySet());
    }
    
    default boolean noCollision(@Nullable final Entity akq, final AABB cvf, final Set<Entity> set) {
        return this.getCollisions(akq, cvf, set).allMatch(VoxelShape::isEmpty);
    }
    
    default Stream<VoxelShape> getEntityCollisions(@Nullable final Entity akq, final AABB cvf, final Set<Entity> set) {
        return Stream.<VoxelShape>empty();
    }
    
    default Stream<VoxelShape> getCollisions(@Nullable final Entity akq, final AABB cvf, final Set<Entity> set) {
        return Streams.<VoxelShape>concat(new Stream[] { this.getBlockCollisions(akq, cvf), this.getEntityCollisions(akq, cvf, set) });
    }
    
    default Stream<VoxelShape> getBlockCollisions(@Nullable final Entity akq, final AABB cvf) {
        final int integer4 = Mth.floor(cvf.minX - 1.0E-7) - 1;
        final int integer5 = Mth.floor(cvf.maxX + 1.0E-7) + 1;
        final int integer6 = Mth.floor(cvf.minY - 1.0E-7) - 1;
        final int integer7 = Mth.floor(cvf.maxY + 1.0E-7) + 1;
        final int integer8 = Mth.floor(cvf.minZ - 1.0E-7) - 1;
        final int integer9 = Mth.floor(cvf.maxZ + 1.0E-7) + 1;
        final CollisionContext cvq10 = (akq == null) ? CollisionContext.empty() : CollisionContext.of(akq);
        final Cursor3D fn11 = new Cursor3D(integer4, integer6, integer8, integer5, integer7, integer9);
        final BlockPos.MutableBlockPos a12 = new BlockPos.MutableBlockPos();
        final VoxelShape cwf13 = Shapes.create(cvf);
        return StreamSupport.<VoxelShape>stream((Spliterator<VoxelShape>)new Spliterators.AbstractSpliterator<VoxelShape>(Long.MAX_VALUE, 1280) {
            boolean checkedBorder = akq == null;
            
            @Override
            public boolean tryAdvance(final Consumer<? super VoxelShape> consumer) {
                if (!this.checkedBorder) {
                    this.checkedBorder = true;
                    final VoxelShape cwf3 = CollisionGetter.this.getWorldBorder().getCollisionShape();
                    final boolean boolean4 = Shapes.joinIsNotEmpty(cwf3, Shapes.create(akq.getBoundingBox().deflate(1.0E-7)), BooleanOp.AND);
                    final boolean boolean5 = Shapes.joinIsNotEmpty(cwf3, Shapes.create(akq.getBoundingBox().inflate(1.0E-7)), BooleanOp.AND);
                    if (!boolean4 && boolean5) {
                        consumer.accept(cwf3);
                        return true;
                    }
                }
                while (fn11.advance()) {
                    final int integer3 = fn11.nextX();
                    final int integer4 = fn11.nextY();
                    final int integer5 = fn11.nextZ();
                    final int integer6 = fn11.getNextType();
                    if (integer6 == 3) {
                        continue;
                    }
                    final int integer7 = integer3 >> 4;
                    final int integer8 = integer5 >> 4;
                    final BlockGetter bjg9 = CollisionGetter.this.getChunkForCollisions(integer7, integer8);
                    if (bjg9 == null) {
                        continue;
                    }
                    a12.set(integer3, integer4, integer5);
                    final BlockState byj10 = bjg9.getBlockState(a12);
                    if (integer6 == 1 && !byj10.hasLargeCollisionShape()) {
                        continue;
                    }
                    if (integer6 == 2 && byj10.getBlock() != Blocks.MOVING_PISTON) {
                        continue;
                    }
                    final VoxelShape cwf4 = byj10.getCollisionShape(CollisionGetter.this, a12, cvq10);
                    final VoxelShape cwf5 = cwf4.move(integer3, integer4, integer5);
                    if (Shapes.joinIsNotEmpty(cwf13, cwf5, BooleanOp.AND)) {
                        consumer.accept(cwf5);
                        return true;
                    }
                }
                return false;
            }
        }, false);
    }
}
