package net.minecraft.world.phys.shapes;

import it.unimi.dsi.fastutil.doubles.DoubleArrayList;
import com.google.common.annotations.VisibleForTesting;
import java.util.Objects;
import it.unimi.dsi.fastutil.doubles.DoubleList;
import com.google.common.math.DoubleMath;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.BlockGetter;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.util.Mth;
import net.minecraft.core.BlockPos;
import net.minecraft.world.level.LevelReader;
import java.util.Iterator;
import java.util.stream.Stream;
import net.minecraft.core.AxisCycle;
import net.minecraft.core.Direction;
import net.minecraft.Util;
import java.util.Arrays;
import com.google.common.math.IntMath;
import net.minecraft.world.phys.AABB;

public final class Shapes {
    private static final VoxelShape BLOCK;
    public static final VoxelShape INFINITY;
    private static final VoxelShape EMPTY;
    
    public static VoxelShape empty() {
        return Shapes.EMPTY;
    }
    
    public static VoxelShape block() {
        return Shapes.BLOCK;
    }
    
    public static VoxelShape box(final double double1, final double double2, final double double3, final double double4, final double double5, final double double6) {
        return create(new AABB(double1, double2, double3, double4, double5, double6));
    }
    
    public static VoxelShape create(final AABB cvf) {
        final int integer2 = findBits(cvf.minX, cvf.maxX);
        final int integer3 = findBits(cvf.minY, cvf.maxY);
        final int integer4 = findBits(cvf.minZ, cvf.maxZ);
        if (integer2 < 0 || integer3 < 0 || integer4 < 0) {
            return new ArrayVoxelShape(Shapes.BLOCK.shape, new double[] { cvf.minX, cvf.maxX }, new double[] { cvf.minY, cvf.maxY }, new double[] { cvf.minZ, cvf.maxZ });
        }
        if (integer2 == 0 && integer3 == 0 && integer4 == 0) {
            return cvf.contains(0.5, 0.5, 0.5) ? block() : empty();
        }
        final int integer5 = 1 << integer2;
        final int integer6 = 1 << integer3;
        final int integer7 = 1 << integer4;
        final int integer8 = (int)Math.round(cvf.minX * integer5);
        final int integer9 = (int)Math.round(cvf.maxX * integer5);
        final int integer10 = (int)Math.round(cvf.minY * integer6);
        final int integer11 = (int)Math.round(cvf.maxY * integer6);
        final int integer12 = (int)Math.round(cvf.minZ * integer7);
        final int integer13 = (int)Math.round(cvf.maxZ * integer7);
        final BitSetDiscreteVoxelShape cvo14 = new BitSetDiscreteVoxelShape(integer5, integer6, integer7, integer8, integer10, integer12, integer9, integer11, integer13);
        for (long long15 = integer8; long15 < integer9; ++long15) {
            for (long long16 = integer10; long16 < integer11; ++long16) {
                for (long long17 = integer12; long17 < integer13; ++long17) {
                    cvo14.setFull((int)long15, (int)long16, (int)long17, false, true);
                }
            }
        }
        return new CubeVoxelShape(cvo14);
    }
    
    private static int findBits(final double double1, final double double2) {
        if (double1 < -1.0E-7 || double2 > 1.0000001) {
            return -1;
        }
        for (int integer5 = 0; integer5 <= 3; ++integer5) {
            final double double3 = double1 * (1 << integer5);
            final double double4 = double2 * (1 << integer5);
            final boolean boolean10 = Math.abs(double3 - Math.floor(double3)) < 1.0E-7;
            final boolean boolean11 = Math.abs(double4 - Math.floor(double4)) < 1.0E-7;
            if (boolean10 && boolean11) {
                return integer5;
            }
        }
        return -1;
    }
    
    protected static long lcm(final int integer1, final int integer2) {
        return integer1 * (long)(integer2 / IntMath.gcd(integer1, integer2));
    }
    
    public static VoxelShape or(final VoxelShape cwf1, final VoxelShape cwf2) {
        return join(cwf1, cwf2, BooleanOp.OR);
    }
    
    public static VoxelShape or(final VoxelShape cwf, final VoxelShape... arr) {
        return Arrays.<VoxelShape>stream(arr).reduce(cwf, Shapes::or);
    }
    
    public static VoxelShape join(final VoxelShape cwf1, final VoxelShape cwf2, final BooleanOp cvp) {
        return joinUnoptimized(cwf1, cwf2, cvp).optimize();
    }
    
    public static VoxelShape joinUnoptimized(final VoxelShape cwf1, final VoxelShape cwf2, final BooleanOp cvp) {
        if (cvp.apply(false, false)) {
            throw Util.<IllegalArgumentException>pauseInIde(new IllegalArgumentException());
        }
        if (cwf1 == cwf2) {
            return cvp.apply(true, true) ? cwf1 : empty();
        }
        final boolean boolean4 = cvp.apply(true, false);
        final boolean boolean5 = cvp.apply(false, true);
        if (cwf1.isEmpty()) {
            return boolean5 ? cwf2 : empty();
        }
        if (cwf2.isEmpty()) {
            return boolean4 ? cwf1 : empty();
        }
        final IndexMerger cvx6 = createIndexMerger(1, cwf1.getCoords(Direction.Axis.X), cwf2.getCoords(Direction.Axis.X), boolean4, boolean5);
        final IndexMerger cvx7 = createIndexMerger(cvx6.getList().size() - 1, cwf1.getCoords(Direction.Axis.Y), cwf2.getCoords(Direction.Axis.Y), boolean4, boolean5);
        final IndexMerger cvx8 = createIndexMerger((cvx6.getList().size() - 1) * (cvx7.getList().size() - 1), cwf1.getCoords(Direction.Axis.Z), cwf2.getCoords(Direction.Axis.Z), boolean4, boolean5);
        final BitSetDiscreteVoxelShape cvo9 = BitSetDiscreteVoxelShape.join(cwf1.shape, cwf2.shape, cvx6, cvx7, cvx8, cvp);
        if (cvx6 instanceof DiscreteCubeMerger && cvx7 instanceof DiscreteCubeMerger && cvx8 instanceof DiscreteCubeMerger) {
            return new CubeVoxelShape(cvo9);
        }
        return new ArrayVoxelShape(cvo9, cvx6.getList(), cvx7.getList(), cvx8.getList());
    }
    
    public static boolean joinIsNotEmpty(final VoxelShape cwf1, final VoxelShape cwf2, final BooleanOp cvp) {
        if (cvp.apply(false, false)) {
            throw Util.<IllegalArgumentException>pauseInIde(new IllegalArgumentException());
        }
        if (cwf1 == cwf2) {
            return cvp.apply(true, true);
        }
        if (cwf1.isEmpty()) {
            return cvp.apply(false, !cwf2.isEmpty());
        }
        if (cwf2.isEmpty()) {
            return cvp.apply(!cwf1.isEmpty(), false);
        }
        final boolean boolean4 = cvp.apply(true, false);
        final boolean boolean5 = cvp.apply(false, true);
        for (final Direction.Axis a9 : AxisCycle.AXIS_VALUES) {
            if (cwf1.max(a9) < cwf2.min(a9) - 1.0E-7) {
                return boolean4 || boolean5;
            }
            if (cwf2.max(a9) < cwf1.min(a9) - 1.0E-7) {
                return boolean4 || boolean5;
            }
        }
        final IndexMerger cvx6 = createIndexMerger(1, cwf1.getCoords(Direction.Axis.X), cwf2.getCoords(Direction.Axis.X), boolean4, boolean5);
        final IndexMerger cvx7 = createIndexMerger(cvx6.getList().size() - 1, cwf1.getCoords(Direction.Axis.Y), cwf2.getCoords(Direction.Axis.Y), boolean4, boolean5);
        final IndexMerger cvx8 = createIndexMerger((cvx6.getList().size() - 1) * (cvx7.getList().size() - 1), cwf1.getCoords(Direction.Axis.Z), cwf2.getCoords(Direction.Axis.Z), boolean4, boolean5);
        return joinIsNotEmpty(cvx6, cvx7, cvx8, cwf1.shape, cwf2.shape, cvp);
    }
    
    private static boolean joinIsNotEmpty(final IndexMerger cvx1, final IndexMerger cvx2, final IndexMerger cvx3, final DiscreteVoxelShape cvu4, final DiscreteVoxelShape cvu5, final BooleanOp cvp) {
        return !cvx1.forMergedIndexes((integer6, integer7, integer8) -> cvx2.forMergedIndexes((integer7, integer8, integer9) -> cvx3.forMergedIndexes((integer8, integer9, integer10) -> !cvp.apply(cvu4.isFullWide(integer6, integer7, integer8), cvu5.isFullWide(integer7, integer8, integer9)))));
    }
    
    public static double collide(final Direction.Axis a, final AABB cvf, final Stream<VoxelShape> stream, double double4) {
        final Iterator<VoxelShape> iterator6 = stream.iterator();
        while (iterator6.hasNext()) {
            if (Math.abs(double4) < 1.0E-7) {
                return 0.0;
            }
            double4 = iterator6.next().collide(a, cvf, double4);
        }
        return double4;
    }
    
    public static double collide(final Direction.Axis a, final AABB cvf, final LevelReader bjz, final double double4, final CollisionContext cvq, final Stream<VoxelShape> stream) {
        return collide(cvf, bjz, double4, cvq, AxisCycle.between(a, Direction.Axis.Z), stream);
    }
    
    private static double collide(final AABB cvf, final LevelReader bjz, double double3, final CollisionContext cvq, final AxisCycle fi, final Stream<VoxelShape> stream) {
        if (cvf.getXsize() < 1.0E-6 || cvf.getYsize() < 1.0E-6 || cvf.getZsize() < 1.0E-6) {
            return double3;
        }
        if (Math.abs(double3) < 1.0E-7) {
            return 0.0;
        }
        final AxisCycle fi2 = fi.inverse();
        final Direction.Axis a9 = fi2.cycle(Direction.Axis.X);
        final Direction.Axis a10 = fi2.cycle(Direction.Axis.Y);
        final Direction.Axis a11 = fi2.cycle(Direction.Axis.Z);
        final BlockPos.MutableBlockPos a12 = new BlockPos.MutableBlockPos();
        final int integer13 = Mth.floor(cvf.min(a9) - 1.0E-7) - 1;
        final int integer14 = Mth.floor(cvf.max(a9) + 1.0E-7) + 1;
        final int integer15 = Mth.floor(cvf.min(a10) - 1.0E-7) - 1;
        final int integer16 = Mth.floor(cvf.max(a10) + 1.0E-7) + 1;
        final double double4 = cvf.min(a11) - 1.0E-7;
        final double double5 = cvf.max(a11) + 1.0E-7;
        final boolean boolean21 = double3 > 0.0;
        final int integer17 = boolean21 ? (Mth.floor(cvf.max(a11) - 1.0E-7) - 1) : (Mth.floor(cvf.min(a11) + 1.0E-7) + 1);
        int integer18 = lastC(double3, double4, double5);
        final int integer19 = boolean21 ? 1 : -1;
        int integer20 = integer17;
        while (true) {
            if (boolean21) {
                if (integer20 > integer18) {
                    break;
                }
            }
            else if (integer20 < integer18) {
                break;
            }
            for (int integer21 = integer13; integer21 <= integer14; ++integer21) {
                for (int integer22 = integer15; integer22 <= integer16; ++integer22) {
                    int integer23 = 0;
                    if (integer21 == integer13 || integer21 == integer14) {
                        ++integer23;
                    }
                    if (integer22 == integer15 || integer22 == integer16) {
                        ++integer23;
                    }
                    if (integer20 == integer17 || integer20 == integer18) {
                        ++integer23;
                    }
                    if (integer23 < 3) {
                        a12.set(fi2, integer21, integer22, integer20);
                        final BlockState byj29 = bjz.getBlockState(a12);
                        if (integer23 != 1 || byj29.hasLargeCollisionShape()) {
                            if (integer23 != 2 || byj29.getBlock() == Blocks.MOVING_PISTON) {
                                double3 = byj29.getCollisionShape(bjz, a12, cvq).collide(a11, cvf.move(-a12.getX(), -a12.getY(), -a12.getZ()), double3);
                                if (Math.abs(double3) < 1.0E-7) {
                                    return 0.0;
                                }
                                integer18 = lastC(double3, double4, double5);
                            }
                        }
                    }
                }
            }
            integer20 += integer19;
        }
        final double[] arr25 = { double3 };
        final Object o;
        stream.forEach(cwf -> o[0] = cwf.collide(a11, cvf, o[0]));
        return arr25[0];
    }
    
    private static int lastC(final double double1, final double double2, final double double3) {
        return (double1 > 0.0) ? (Mth.floor(double3 + double1) + 1) : (Mth.floor(double2 + double1) - 1);
    }
    
    public static boolean blockOccudes(final VoxelShape cwf1, final VoxelShape cwf2, final Direction fp) {
        if (cwf1 == block() && cwf2 == block()) {
            return true;
        }
        if (cwf2.isEmpty()) {
            return false;
        }
        final Direction.Axis a4 = fp.getAxis();
        final Direction.AxisDirection b5 = fp.getAxisDirection();
        final VoxelShape cwf3 = (b5 == Direction.AxisDirection.POSITIVE) ? cwf1 : cwf2;
        final VoxelShape cwf4 = (b5 == Direction.AxisDirection.POSITIVE) ? cwf2 : cwf1;
        final BooleanOp cvp8 = (b5 == Direction.AxisDirection.POSITIVE) ? BooleanOp.ONLY_FIRST : BooleanOp.ONLY_SECOND;
        return DoubleMath.fuzzyEquals(cwf3.max(a4), 1.0, 1.0E-7) && DoubleMath.fuzzyEquals(cwf4.min(a4), 0.0, 1.0E-7) && !joinIsNotEmpty(new SliceShape(cwf3, a4, cwf3.shape.getSize(a4) - 1), new SliceShape(cwf4, a4, 0), cvp8);
    }
    
    public static VoxelShape getFaceShape(final VoxelShape cwf, final Direction fp) {
        if (cwf == block()) {
            return block();
        }
        final Direction.Axis a5 = fp.getAxis();
        boolean boolean3;
        int integer4;
        if (fp.getAxisDirection() == Direction.AxisDirection.POSITIVE) {
            boolean3 = DoubleMath.fuzzyEquals(cwf.max(a5), 1.0, 1.0E-7);
            integer4 = cwf.shape.getSize(a5) - 1;
        }
        else {
            boolean3 = DoubleMath.fuzzyEquals(cwf.min(a5), 0.0, 1.0E-7);
            integer4 = 0;
        }
        if (!boolean3) {
            return empty();
        }
        return new SliceShape(cwf, a5, integer4);
    }
    
    public static boolean mergedFaceOccludes(final VoxelShape cwf1, final VoxelShape cwf2, final Direction fp) {
        if (cwf1 == block() || cwf2 == block()) {
            return true;
        }
        final Direction.Axis a4 = fp.getAxis();
        final Direction.AxisDirection b5 = fp.getAxisDirection();
        VoxelShape cwf3 = (b5 == Direction.AxisDirection.POSITIVE) ? cwf1 : cwf2;
        VoxelShape cwf4 = (b5 == Direction.AxisDirection.POSITIVE) ? cwf2 : cwf1;
        if (!DoubleMath.fuzzyEquals(cwf3.max(a4), 1.0, 1.0E-7)) {
            cwf3 = empty();
        }
        if (!DoubleMath.fuzzyEquals(cwf4.min(a4), 0.0, 1.0E-7)) {
            cwf4 = empty();
        }
        return !joinIsNotEmpty(block(), joinUnoptimized(new SliceShape(cwf3, a4, cwf3.shape.getSize(a4) - 1), new SliceShape(cwf4, a4, 0), BooleanOp.OR), BooleanOp.ONLY_FIRST);
    }
    
    public static boolean faceShapeOccludes(final VoxelShape cwf1, final VoxelShape cwf2) {
        return cwf1 == block() || cwf2 == block() || ((!cwf1.isEmpty() || !cwf2.isEmpty()) && !joinIsNotEmpty(block(), joinUnoptimized(cwf1, cwf2, BooleanOp.OR), BooleanOp.ONLY_FIRST));
    }
    
    @VisibleForTesting
    protected static IndexMerger createIndexMerger(final int integer, final DoubleList doubleList2, final DoubleList doubleList3, final boolean boolean4, final boolean boolean5) {
        final int integer2 = doubleList2.size() - 1;
        final int integer3 = doubleList3.size() - 1;
        if (doubleList2 instanceof CubePointRange && doubleList3 instanceof CubePointRange) {
            final long long8 = lcm(integer2, integer3);
            if (integer * long8 <= 256L) {
                return new DiscreteCubeMerger(integer2, integer3);
            }
        }
        if (doubleList2.getDouble(integer2) < doubleList3.getDouble(0) - 1.0E-7) {
            return new NonOverlappingMerger(doubleList2, doubleList3, false);
        }
        if (doubleList3.getDouble(integer3) < doubleList2.getDouble(0) - 1.0E-7) {
            return new NonOverlappingMerger(doubleList3, doubleList2, true);
        }
        if (integer2 != integer3 || !Objects.equals(doubleList2, doubleList3)) {
            return new IndirectMerger(doubleList2, doubleList3, boolean4, boolean5);
        }
        if (doubleList2 instanceof IdenticalMerger) {
            return (IndexMerger)doubleList2;
        }
        if (doubleList3 instanceof IdenticalMerger) {
            return (IndexMerger)doubleList3;
        }
        return new IdenticalMerger(doubleList2);
    }
    
    static {
        final BitSetDiscreteVoxelShape cvu1;
        BLOCK = Util.<CubeVoxelShape>make(() -> {
            cvu1 = new BitSetDiscreteVoxelShape(1, 1, 1);
            cvu1.setFull(0, 0, 0, true, true);
            return new CubeVoxelShape(cvu1);
        });
        INFINITY = box(Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY);
        EMPTY = new ArrayVoxelShape(new BitSetDiscreteVoxelShape(0, 0, 0), (DoubleList)new DoubleArrayList(new double[] { 0.0 }), (DoubleList)new DoubleArrayList(new double[] { 0.0 }), (DoubleList)new DoubleArrayList(new double[] { 0.0 }));
    }
    
    public interface DoubleLineConsumer {
        void consume(final double double1, final double double2, final double double3, final double double4, final double double5, final double double6);
    }
}
