package net.minecraft.world.level;

import net.minecraft.util.Mth;
import java.util.function.BiFunction;
import net.minecraft.world.phys.shapes.VoxelShape;
import net.minecraft.world.phys.Vec3;
import net.minecraft.core.Direction;
import net.minecraft.world.phys.BlockHitResult;
import java.util.function.Function;
import java.util.stream.Stream;
import net.minecraft.world.phys.AABB;
import net.minecraft.world.level.material.FluidState;
import net.minecraft.world.level.block.state.BlockState;
import javax.annotation.Nullable;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.core.BlockPos;

public interface BlockGetter {
    @Nullable
    BlockEntity getBlockEntity(final BlockPos fu);
    
    BlockState getBlockState(final BlockPos fu);
    
    FluidState getFluidState(final BlockPos fu);
    
    default int getLightEmission(final BlockPos fu) {
        return this.getBlockState(fu).getLightEmission();
    }
    
    default int getMaxLightLevel() {
        return 15;
    }
    
    default int getMaxBuildHeight() {
        return 256;
    }
    
    default Stream<BlockState> getBlockStates(final AABB deg) {
        return BlockPos.betweenClosedStream(deg).<BlockState>map(this::getBlockState);
    }
    
    default BlockHitResult clip(final ClipContext bpj) {
        final BlockState cfj4;
        final FluidState cxa5;
        final Vec3 dem6;
        final Vec3 dem7;
        final VoxelShape dfg8;
        final BlockHitResult deh9;
        final VoxelShape dfg9;
        final BlockHitResult deh10;
        final double double12;
        final double double13;
        final Vec3 dem8;
        return BlockGetter.<BlockHitResult>traverseBlocks(bpj, (bpj, fu) -> {
            cfj4 = this.getBlockState(fu);
            cxa5 = this.getFluidState(fu);
            dem6 = bpj.getFrom();
            dem7 = bpj.getTo();
            dfg8 = bpj.getBlockShape(cfj4, this, fu);
            deh9 = this.clipWithInteractionOverride(dem6, dem7, fu, dfg8, cfj4);
            dfg9 = bpj.getFluidShape(cxa5, this, fu);
            deh10 = dfg9.clip(dem6, dem7, fu);
            double12 = ((deh9 == null) ? Double.MAX_VALUE : bpj.getFrom().distanceToSqr(deh9.getLocation()));
            double13 = ((deh10 == null) ? Double.MAX_VALUE : bpj.getFrom().distanceToSqr(deh10.getLocation()));
            return (double12 <= double13) ? deh9 : deh10;
        }, bpj -> {
            dem8 = bpj.getFrom().subtract(bpj.getTo());
            return BlockHitResult.miss(bpj.getTo(), Direction.getNearest(dem8.x, dem8.y, dem8.z), new BlockPos(bpj.getTo()));
        });
    }
    
    @Nullable
    default BlockHitResult clipWithInteractionOverride(final Vec3 dem1, final Vec3 dem2, final BlockPos fu, final VoxelShape dfg, final BlockState cfj) {
        final BlockHitResult deh7 = dfg.clip(dem1, dem2, fu);
        if (deh7 != null) {
            final BlockHitResult deh8 = cfj.getInteractionShape(this, fu).clip(dem1, dem2, fu);
            if (deh8 != null && deh8.getLocation().subtract(dem1).lengthSqr() < deh7.getLocation().subtract(dem1).lengthSqr()) {
                return deh7.withDirection(deh8.getDirection());
            }
        }
        return deh7;
    }
    
    default <T> T traverseBlocks(final ClipContext bpj, final BiFunction<ClipContext, BlockPos, T> biFunction, final Function<ClipContext, T> function) {
        final Vec3 dem4 = bpj.getFrom();
        final Vec3 dem5 = bpj.getTo();
        if (dem4.equals(dem5)) {
            return function.apply(bpj);
        }
        final double double6 = Mth.lerp(-1.0E-7, dem5.x, dem4.x);
        final double double7 = Mth.lerp(-1.0E-7, dem5.y, dem4.y);
        final double double8 = Mth.lerp(-1.0E-7, dem5.z, dem4.z);
        final double double9 = Mth.lerp(-1.0E-7, dem4.x, dem5.x);
        final double double10 = Mth.lerp(-1.0E-7, dem4.y, dem5.y);
        final double double11 = Mth.lerp(-1.0E-7, dem4.z, dem5.z);
        int integer18 = Mth.floor(double9);
        int integer19 = Mth.floor(double10);
        int integer20 = Mth.floor(double11);
        final BlockPos.MutableBlockPos a21 = new BlockPos.MutableBlockPos(integer18, integer19, integer20);
        final T object22 = biFunction.apply(bpj, a21);
        if (object22 != null) {
            return object22;
        }
        final double double12 = double6 - double9;
        final double double13 = double7 - double10;
        final double double14 = double8 - double11;
        final int integer21 = Mth.sign(double12);
        final int integer22 = Mth.sign(double13);
        final int integer23 = Mth.sign(double14);
        final double double15 = (integer21 == 0) ? Double.MAX_VALUE : (integer21 / double12);
        final double double16 = (integer22 == 0) ? Double.MAX_VALUE : (integer22 / double13);
        final double double17 = (integer23 == 0) ? Double.MAX_VALUE : (integer23 / double14);
        double double18 = double15 * ((integer21 > 0) ? (1.0 - Mth.frac(double9)) : Mth.frac(double9));
        double double19 = double16 * ((integer22 > 0) ? (1.0 - Mth.frac(double10)) : Mth.frac(double10));
        double double20 = double17 * ((integer23 > 0) ? (1.0 - Mth.frac(double11)) : Mth.frac(double11));
        while (double18 <= 1.0 || double19 <= 1.0 || double20 <= 1.0) {
            if (double18 < double19) {
                if (double18 < double20) {
                    integer18 += integer21;
                    double18 += double15;
                }
                else {
                    integer20 += integer23;
                    double20 += double17;
                }
            }
            else if (double19 < double20) {
                integer19 += integer22;
                double19 += double16;
            }
            else {
                integer20 += integer23;
                double20 += double17;
            }
            final T object23 = biFunction.apply(bpj, a21.set(integer18, integer19, integer20));
            if (object23 != null) {
                return object23;
            }
        }
        return function.apply(bpj);
    }
}
