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 deg) {
        final int integer2 = findBits(deg.minX, deg.maxX);
        final int integer3 = findBits(deg.minY, deg.maxY);
        final int integer4 = findBits(deg.minZ, deg.maxZ);
        if (integer2 < 0 || integer3 < 0 || integer4 < 0) {
            return new ArrayVoxelShape(Shapes.BLOCK.shape, new double[] { deg.minX, deg.maxX }, new double[] { deg.minY, deg.maxY }, new double[] { deg.minZ, deg.maxZ });
        }
        if (integer2 == 0 && integer3 == 0 && integer4 == 0) {
            return deg.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(deg.minX * integer5);
        final int integer9 = (int)Math.round(deg.maxX * integer5);
        final int integer10 = (int)Math.round(deg.minY * integer6);
        final int integer11 = (int)Math.round(deg.maxY * integer6);
        final int integer12 = (int)Math.round(deg.minZ * integer7);
        final int integer13 = (int)Math.round(deg.maxZ * integer7);
        final BitSetDiscreteVoxelShape dep14 = 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) {
                    dep14.setFull((int)long15, (int)long16, (int)long17, false, true);
                }
            }
        }
        return new CubeVoxelShape(dep14);
    }
    
    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 dfg1, final VoxelShape dfg2) {
        return join(dfg1, dfg2, BooleanOp.OR);
    }
    
    public static VoxelShape or(final VoxelShape dfg, final VoxelShape... arr) {
        return Arrays.<VoxelShape>stream(arr).reduce(dfg, Shapes::or);
    }
    
    public static VoxelShape join(final VoxelShape dfg1, final VoxelShape dfg2, final BooleanOp deq) {
        return joinUnoptimized(dfg1, dfg2, deq).optimize();
    }
    
    public static VoxelShape joinUnoptimized(final VoxelShape dfg1, final VoxelShape dfg2, final BooleanOp deq) {
        if (deq.apply(false, false)) {
            throw Util.<IllegalArgumentException>pauseInIde(new IllegalArgumentException());
        }
        if (dfg1 == dfg2) {
            return deq.apply(true, true) ? dfg1 : empty();
        }
        final boolean boolean4 = deq.apply(true, false);
        final boolean boolean5 = deq.apply(false, true);
        if (dfg1.isEmpty()) {
            return boolean5 ? dfg2 : empty();
        }
        if (dfg2.isEmpty()) {
            return boolean4 ? dfg1 : empty();
        }
        final IndexMerger dey6 = createIndexMerger(1, dfg1.getCoords(Direction.Axis.X), dfg2.getCoords(Direction.Axis.X), boolean4, boolean5);
        final IndexMerger dey7 = createIndexMerger(dey6.getList().size() - 1, dfg1.getCoords(Direction.Axis.Y), dfg2.getCoords(Direction.Axis.Y), boolean4, boolean5);
        final IndexMerger dey8 = createIndexMerger((dey6.getList().size() - 1) * (dey7.getList().size() - 1), dfg1.getCoords(Direction.Axis.Z), dfg2.getCoords(Direction.Axis.Z), boolean4, boolean5);
        final BitSetDiscreteVoxelShape dep9 = BitSetDiscreteVoxelShape.join(dfg1.shape, dfg2.shape, dey6, dey7, dey8, deq);
        if (dey6 instanceof DiscreteCubeMerger && dey7 instanceof DiscreteCubeMerger && dey8 instanceof DiscreteCubeMerger) {
            return new CubeVoxelShape(dep9);
        }
        return new ArrayVoxelShape(dep9, dey6.getList(), dey7.getList(), dey8.getList());
    }
    
    public static boolean joinIsNotEmpty(final VoxelShape dfg1, final VoxelShape dfg2, final BooleanOp deq) {
        if (deq.apply(false, false)) {
            throw Util.<IllegalArgumentException>pauseInIde(new IllegalArgumentException());
        }
        if (dfg1 == dfg2) {
            return deq.apply(true, true);
        }
        if (dfg1.isEmpty()) {
            return deq.apply(false, !dfg2.isEmpty());
        }
        if (dfg2.isEmpty()) {
            return deq.apply(!dfg1.isEmpty(), false);
        }
        final boolean boolean4 = deq.apply(true, false);
        final boolean boolean5 = deq.apply(false, true);
        for (final Direction.Axis a9 : AxisCycle.AXIS_VALUES) {
            if (dfg1.max(a9) < dfg2.min(a9) - 1.0E-7) {
                return boolean4 || boolean5;
            }
            if (dfg2.max(a9) < dfg1.min(a9) - 1.0E-7) {
                return boolean4 || boolean5;
            }
        }
        final IndexMerger dey6 = createIndexMerger(1, dfg1.getCoords(Direction.Axis.X), dfg2.getCoords(Direction.Axis.X), boolean4, boolean5);
        final IndexMerger dey7 = createIndexMerger(dey6.getList().size() - 1, dfg1.getCoords(Direction.Axis.Y), dfg2.getCoords(Direction.Axis.Y), boolean4, boolean5);
        final IndexMerger dey8 = createIndexMerger((dey6.getList().size() - 1) * (dey7.getList().size() - 1), dfg1.getCoords(Direction.Axis.Z), dfg2.getCoords(Direction.Axis.Z), boolean4, boolean5);
        return joinIsNotEmpty(dey6, dey7, dey8, dfg1.shape, dfg2.shape, deq);
    }
    
    private static boolean joinIsNotEmpty(final IndexMerger dey1, final IndexMerger dey2, final IndexMerger dey3, final DiscreteVoxelShape dev4, final DiscreteVoxelShape dev5, final BooleanOp deq) {
        return !dey1.forMergedIndexes((integer6, integer7, integer8) -> dey2.forMergedIndexes((integer7, integer8, integer9) -> dey3.forMergedIndexes((integer8, integer9, integer10) -> !deq.apply(dev4.isFullWide(integer6, integer7, integer8), dev5.isFullWide(integer7, integer8, integer9)))));
    }
    
    public static double collide(final Direction.Axis a, final AABB deg, 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, deg, double4);
        }
        return double4;
    }
    
    public static double collide(final Direction.Axis a, final AABB deg, final LevelReader bqd, final double double4, final CollisionContext der, final Stream<VoxelShape> stream) {
        return collide(deg, bqd, double4, der, AxisCycle.between(a, Direction.Axis.Z), stream);
    }
    
    private static double collide(final AABB deg, final LevelReader bqd, double double3, final CollisionContext der, final AxisCycle fs, final Stream<VoxelShape> stream) {
        if (deg.getXsize() < 1.0E-6 || deg.getYsize() < 1.0E-6 || deg.getZsize() < 1.0E-6) {
            return double3;
        }
        if (Math.abs(double3) < 1.0E-7) {
            return 0.0;
        }
        final AxisCycle fs2 = fs.inverse();
        final Direction.Axis a9 = fs2.cycle(Direction.Axis.X);
        final Direction.Axis a10 = fs2.cycle(Direction.Axis.Y);
        final Direction.Axis a11 = fs2.cycle(Direction.Axis.Z);
        final BlockPos.MutableBlockPos a12 = new BlockPos.MutableBlockPos();
        final int integer13 = Mth.floor(deg.min(a9) - 1.0E-7) - 1;
        final int integer14 = Mth.floor(deg.max(a9) + 1.0E-7) + 1;
        final int integer15 = Mth.floor(deg.min(a10) - 1.0E-7) - 1;
        final int integer16 = Mth.floor(deg.max(a10) + 1.0E-7) + 1;
        final double double4 = deg.min(a11) - 1.0E-7;
        final double double5 = deg.max(a11) + 1.0E-7;
        final boolean boolean21 = double3 > 0.0;
        final int integer17 = boolean21 ? (Mth.floor(deg.max(a11) - 1.0E-7) - 1) : (Mth.floor(deg.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(fs2, integer21, integer22, integer20);
                        final BlockState cfj29 = bqd.getBlockState(a12);
                        if (integer23 != 1 || cfj29.hasLargeCollisionShape()) {
                            if (integer23 != 2 || cfj29.is(Blocks.MOVING_PISTON)) {
                                double3 = cfj29.getCollisionShape(bqd, a12, der).collide(a11, deg.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(dfg -> o[0] = dfg.collide(a11, deg, 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 dfg1, final VoxelShape dfg2, final Direction fz) {
        if (dfg1 == block() && dfg2 == block()) {
            return true;
        }
        if (dfg2.isEmpty()) {
            return false;
        }
        final Direction.Axis a4 = fz.getAxis();
        final Direction.AxisDirection b5 = fz.getAxisDirection();
        final VoxelShape dfg3 = (b5 == Direction.AxisDirection.POSITIVE) ? dfg1 : dfg2;
        final VoxelShape dfg4 = (b5 == Direction.AxisDirection.POSITIVE) ? dfg2 : dfg1;
        final BooleanOp deq8 = (b5 == Direction.AxisDirection.POSITIVE) ? BooleanOp.ONLY_FIRST : BooleanOp.ONLY_SECOND;
        return DoubleMath.fuzzyEquals(dfg3.max(a4), 1.0, 1.0E-7) && DoubleMath.fuzzyEquals(dfg4.min(a4), 0.0, 1.0E-7) && !joinIsNotEmpty(new SliceShape(dfg3, a4, dfg3.shape.getSize(a4) - 1), new SliceShape(dfg4, a4, 0), deq8);
    }
    
    public static VoxelShape getFaceShape(final VoxelShape dfg, final Direction fz) {
        if (dfg == block()) {
            return block();
        }
        final Direction.Axis a5 = fz.getAxis();
        boolean boolean3;
        int integer4;
        if (fz.getAxisDirection() == Direction.AxisDirection.POSITIVE) {
            boolean3 = DoubleMath.fuzzyEquals(dfg.max(a5), 1.0, 1.0E-7);
            integer4 = dfg.shape.getSize(a5) - 1;
        }
        else {
            boolean3 = DoubleMath.fuzzyEquals(dfg.min(a5), 0.0, 1.0E-7);
            integer4 = 0;
        }
        if (!boolean3) {
            return empty();
        }
        return new SliceShape(dfg, a5, integer4);
    }
    
    public static boolean mergedFaceOccludes(final VoxelShape dfg1, final VoxelShape dfg2, final Direction fz) {
        if (dfg1 == block() || dfg2 == block()) {
            return true;
        }
        final Direction.Axis a4 = fz.getAxis();
        final Direction.AxisDirection b5 = fz.getAxisDirection();
        VoxelShape dfg3 = (b5 == Direction.AxisDirection.POSITIVE) ? dfg1 : dfg2;
        VoxelShape dfg4 = (b5 == Direction.AxisDirection.POSITIVE) ? dfg2 : dfg1;
        if (!DoubleMath.fuzzyEquals(dfg3.max(a4), 1.0, 1.0E-7)) {
            dfg3 = empty();
        }
        if (!DoubleMath.fuzzyEquals(dfg4.min(a4), 0.0, 1.0E-7)) {
            dfg4 = empty();
        }
        return !joinIsNotEmpty(block(), joinUnoptimized(new SliceShape(dfg3, a4, dfg3.shape.getSize(a4) - 1), new SliceShape(dfg4, a4, 0), BooleanOp.OR), BooleanOp.ONLY_FIRST);
    }
    
    public static boolean faceShapeOccludes(final VoxelShape dfg1, final VoxelShape dfg2) {
        return dfg1 == block() || dfg2 == block() || ((!dfg1.isEmpty() || !dfg2.isEmpty()) && !joinIsNotEmpty(block(), joinUnoptimized(dfg1, dfg2, 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 dev1;
        BLOCK = Util.<CubeVoxelShape>make(() -> {
            dev1 = new BitSetDiscreteVoxelShape(1, 1, 1);
            dev1.setFull(0, 0, 0, true, true);
            return new CubeVoxelShape(dev1);
        });
        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);
    }
}
