package net.minecraft.world.level;

import net.minecraft.util.Mth;
import net.minecraft.world.phys.AABB;
import net.minecraft.tags.FluidTags;
import net.minecraft.core.Direction;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.dimension.Dimension;
import net.minecraft.world.level.biome.Biome;
import net.minecraft.core.BlockPos;
import net.minecraft.world.level.levelgen.Heightmap;
import javax.annotation.Nullable;
import net.minecraft.world.level.chunk.ChunkAccess;
import net.minecraft.world.level.chunk.ChunkStatus;
import net.minecraft.world.level.biome.BiomeManager;

public interface LevelReader extends BlockAndTintGetter, CollisionGetter, BiomeManager.NoiseBiomeSource {
    @Nullable
    ChunkAccess getChunk(final int integer1, final int integer2, final ChunkStatus cae, final boolean boolean4);
    
    @Deprecated
    boolean hasChunk(final int integer1, final int integer2);
    
    int getHeight(final Heightmap.Types a, final int integer2, final int integer3);
    
    int getSkyDarken();
    
    BiomeManager getBiomeManager();
    
    default Biome getBiome(final BlockPos fk) {
        return this.getBiomeManager().getBiome(fk);
    }
    
    default int getBlockTint(final BlockPos fk, final ColorResolver colorResolver) {
        return colorResolver.getColor(this.getBiome(fk), fk.getX(), fk.getZ());
    }
    
    default Biome getNoiseBiome(final int integer1, final int integer2, final int integer3) {
        final ChunkAccess bzy5 = this.getChunk(integer1 >> 2, integer3 >> 2, ChunkStatus.BIOMES, false);
        if (bzy5 != null && bzy5.getBiomes() != null) {
            return bzy5.getBiomes().getNoiseBiome(integer1, integer2, integer3);
        }
        return this.getUncachedNoiseBiome(integer1, integer2, integer3);
    }
    
    Biome getUncachedNoiseBiome(final int integer1, final int integer2, final int integer3);
    
    boolean isClientSide();
    
    int getSeaLevel();
    
    Dimension getDimension();
    
    default BlockPos getHeightmapPos(final Heightmap.Types a, final BlockPos fk) {
        return new BlockPos(fk.getX(), this.getHeight(a, fk.getX(), fk.getZ()), fk.getZ());
    }
    
    default boolean isEmptyBlock(final BlockPos fk) {
        return this.getBlockState(fk).isAir();
    }
    
    default boolean canSeeSkyFromBelowWater(final BlockPos fk) {
        if (fk.getY() >= this.getSeaLevel()) {
            return this.canSeeSky(fk);
        }
        BlockPos fk2 = new BlockPos(fk.getX(), this.getSeaLevel(), fk.getZ());
        if (!this.canSeeSky(fk2)) {
            return false;
        }
        for (fk2 = fk2.below(); fk2.getY() > fk.getY(); fk2 = fk2.below()) {
            final BlockState byj4 = this.getBlockState(fk2);
            if (byj4.getLightBlock(this, fk2) > 0 && !byj4.getMaterial().isLiquid()) {
                return false;
            }
        }
        return true;
    }
    
    @Deprecated
    default float getBrightness(final BlockPos fk) {
        return this.getDimension().getBrightness(this.getMaxLocalRawBrightness(fk));
    }
    
    default int getDirectSignal(final BlockPos fk, final Direction fp) {
        return this.getBlockState(fk).getDirectSignal(this, fk, fp);
    }
    
    default ChunkAccess getChunk(final BlockPos fk) {
        return this.getChunk(fk.getX() >> 4, fk.getZ() >> 4);
    }
    
    default ChunkAccess getChunk(final int integer1, final int integer2) {
        return this.getChunk(integer1, integer2, ChunkStatus.FULL, true);
    }
    
    default ChunkAccess getChunk(final int integer1, final int integer2, final ChunkStatus cae) {
        return this.getChunk(integer1, integer2, cae, true);
    }
    
    @Nullable
    default BlockGetter getChunkForCollisions(final int integer1, final int integer2) {
        return this.getChunk(integer1, integer2, ChunkStatus.EMPTY, false);
    }
    
    default boolean isWaterAt(final BlockPos fk) {
        return this.getFluidState(fk).is(FluidTags.WATER);
    }
    
    default boolean containsAnyLiquid(final AABB cvf) {
        final int integer3 = Mth.floor(cvf.minX);
        final int integer4 = Mth.ceil(cvf.maxX);
        final int integer5 = Mth.floor(cvf.minY);
        final int integer6 = Mth.ceil(cvf.maxY);
        final int integer7 = Mth.floor(cvf.minZ);
        final int integer8 = Mth.ceil(cvf.maxZ);
        try (final BlockPos.PooledMutableBlockPos b9 = BlockPos.PooledMutableBlockPos.acquire()) {
            for (int integer9 = integer3; integer9 < integer4; ++integer9) {
                for (int integer10 = integer5; integer10 < integer6; ++integer10) {
                    for (int integer11 = integer7; integer11 < integer8; ++integer11) {
                        final BlockState byj14 = this.getBlockState(b9.set(integer9, integer10, integer11));
                        if (!byj14.getFluidState().isEmpty()) {
                            return true;
                        }
                    }
                }
            }
        }
        return false;
    }
    
    default int getMaxLocalRawBrightness(final BlockPos fk) {
        return this.getMaxLocalRawBrightness(fk, this.getSkyDarken());
    }
    
    default int getMaxLocalRawBrightness(final BlockPos fk, final int integer) {
        if (fk.getX() < -30000000 || fk.getZ() < -30000000 || fk.getX() >= 30000000 || fk.getZ() >= 30000000) {
            return 15;
        }
        return this.getRawBrightness(fk, integer);
    }
    
    @Deprecated
    default boolean hasChunkAt(final BlockPos fk) {
        return this.hasChunk(fk.getX() >> 4, fk.getZ() >> 4);
    }
    
    @Deprecated
    default boolean hasChunksAt(final BlockPos fk1, final BlockPos fk2) {
        return this.hasChunksAt(fk1.getX(), fk1.getY(), fk1.getZ(), fk2.getX(), fk2.getY(), fk2.getZ());
    }
    
    @Deprecated
    default boolean hasChunksAt(int integer1, final int integer2, int integer3, int integer4, final int integer5, int integer6) {
        if (integer5 < 0 || integer2 >= 256) {
            return false;
        }
        integer1 >>= 4;
        integer3 >>= 4;
        integer4 >>= 4;
        integer6 >>= 4;
        for (int integer7 = integer1; integer7 <= integer4; ++integer7) {
            for (int integer8 = integer3; integer8 <= integer6; ++integer8) {
                if (!this.hasChunk(integer7, integer8)) {
                    return false;
                }
            }
        }
        return true;
    }
}
