package net.minecraft.server.level;

import org.apache.logging.log4j.LogManager;
import java.util.Collections;
import net.minecraft.world.phys.AABB;
import net.minecraft.core.particles.ParticleOptions;
import net.minecraft.sounds.SoundSource;
import net.minecraft.sounds.SoundEvent;
import net.minecraft.world.level.levelgen.Heightmap;
import net.minecraft.world.level.chunk.ChunkSource;
import net.minecraft.world.DifficultyInstance;
import net.minecraft.world.level.border.WorldBorder;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.world.level.BlockGetter;
import net.minecraft.world.level.block.EntityBlock;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.Level;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.lighting.LevelLightEngine;
import net.minecraft.world.level.biome.Biome;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.entity.Entity;
import java.util.function.Predicate;
import net.minecraft.world.level.material.FluidState;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.core.BlockPos;
import javax.annotation.Nullable;
import net.minecraft.world.level.chunk.ChunkStatus;
import net.minecraft.world.level.ChunkPos;
import net.minecraft.Util;
import net.minecraft.util.Mth;
import net.minecraft.world.level.biome.BiomeManager;
import net.minecraft.world.level.material.Fluid;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.TickList;
import net.minecraft.world.level.levelgen.ChunkGeneratorSettings;
import net.minecraft.world.level.dimension.Dimension;
import java.util.Random;
import net.minecraft.world.level.storage.LevelData;
import net.minecraft.world.level.chunk.ChunkAccess;
import java.util.List;
import org.apache.logging.log4j.Logger;
import net.minecraft.world.level.LevelAccessor;

public class WorldGenRegion implements LevelAccessor {
    private static final Logger LOGGER;
    private final List<ChunkAccess> cache;
    private final int x;
    private final int z;
    private final int size;
    private final ServerLevel level;
    private final long seed;
    private final int seaLevel;
    private final LevelData levelData;
    private final Random random;
    private final Dimension dimension;
    private final ChunkGeneratorSettings settings;
    private final TickList<Block> blockTicks;
    private final TickList<Fluid> liquidTicks;
    private final BiomeManager biomeManager;
    
    public WorldGenRegion(final ServerLevel xd, final List<ChunkAccess> list) {
        this.blockTicks = new WorldGenTickList<Block>(fk -> this.getChunk(fk).getBlockTicks());
        this.liquidTicks = new WorldGenTickList<Fluid>(fk -> this.getChunk(fk).getLiquidTicks());
        final int integer4 = Mth.floor(Math.sqrt(list.size()));
        if (integer4 * integer4 != list.size()) {
            throw Util.<IllegalStateException>pauseInIde(new IllegalStateException("Cache size is not a square."));
        }
        final ChunkPos bjh5 = list.get(list.size() / 2).getPos();
        this.cache = list;
        this.x = bjh5.x;
        this.z = bjh5.z;
        this.size = integer4;
        this.level = xd;
        this.seed = xd.getSeed();
        this.settings = (ChunkGeneratorSettings)xd.getChunkSource().getGenerator().getSettings();
        this.seaLevel = xd.getSeaLevel();
        this.levelData = xd.getLevelData();
        this.random = xd.getRandom();
        this.dimension = xd.getDimension();
        this.biomeManager = new BiomeManager(this, LevelData.obfuscateSeed(this.seed), this.dimension.getType().getBiomeZoomer());
    }
    
    public int getCenterX() {
        return this.x;
    }
    
    public int getCenterZ() {
        return this.z;
    }
    
    @Override
    public ChunkAccess getChunk(final int integer1, final int integer2) {
        return this.getChunk(integer1, integer2, ChunkStatus.EMPTY);
    }
    
    @Nullable
    @Override
    public ChunkAccess getChunk(final int integer1, final int integer2, final ChunkStatus cae, final boolean boolean4) {
        ChunkAccess bzy6;
        if (this.hasChunk(integer1, integer2)) {
            final ChunkPos bjh7 = this.cache.get(0).getPos();
            final int integer3 = integer1 - bjh7.x;
            final int integer4 = integer2 - bjh7.z;
            bzy6 = this.cache.get(integer3 + integer4 * this.size);
            if (bzy6.getStatus().isOrAfter(cae)) {
                return bzy6;
            }
        }
        else {
            bzy6 = null;
        }
        if (!boolean4) {
            return null;
        }
        final ChunkAccess bzy7 = this.cache.get(0);
        final ChunkAccess bzy8 = this.cache.get(this.cache.size() - 1);
        WorldGenRegion.LOGGER.error("Requested chunk : {} {}", integer1, integer2);
        WorldGenRegion.LOGGER.error("Region bounds : {} {} | {} {}", bzy7.getPos().x, bzy7.getPos().z, bzy8.getPos().x, bzy8.getPos().z);
        if (bzy6 != null) {
            throw Util.<RuntimeException>pauseInIde(new RuntimeException(String.format("Chunk is not of correct status. Expecting %s, got %s | %s %s", cae, bzy6.getStatus(), integer1, integer2)));
        }
        throw Util.<RuntimeException>pauseInIde(new RuntimeException(String.format("We are asking a region for a chunk out of bound | %s %s", integer1, integer2)));
    }
    
    @Override
    public boolean hasChunk(final int integer1, final int integer2) {
        final ChunkAccess bzy4 = this.cache.get(0);
        final ChunkAccess bzy5 = this.cache.get(this.cache.size() - 1);
        return integer1 >= bzy4.getPos().x && integer1 <= bzy5.getPos().x && integer2 >= bzy4.getPos().z && integer2 <= bzy5.getPos().z;
    }
    
    @Override
    public BlockState getBlockState(final BlockPos fk) {
        return this.getChunk(fk.getX() >> 4, fk.getZ() >> 4).getBlockState(fk);
    }
    
    @Override
    public FluidState getFluidState(final BlockPos fk) {
        return this.getChunk(fk).getFluidState(fk);
    }
    
    @Nullable
    @Override
    public Player getNearestPlayer(final double double1, final double double2, final double double3, final double double4, final Predicate<Entity> predicate) {
        return null;
    }
    
    @Override
    public int getSkyDarken() {
        return 0;
    }
    
    @Override
    public BiomeManager getBiomeManager() {
        return this.biomeManager;
    }
    
    @Override
    public Biome getUncachedNoiseBiome(final int integer1, final int integer2, final int integer3) {
        return this.level.getUncachedNoiseBiome(integer1, integer2, integer3);
    }
    
    @Override
    public LevelLightEngine getLightEngine() {
        return this.level.getLightEngine();
    }
    
    @Override
    public boolean destroyBlock(final BlockPos fk, final boolean boolean2, @Nullable final Entity akq) {
        final BlockState byj5 = this.getBlockState(fk);
        if (byj5.isAir()) {
            return false;
        }
        if (boolean2) {
            final BlockEntity bwl6 = byj5.getBlock().isEntityBlock() ? this.getBlockEntity(fk) : null;
            Block.dropResources(byj5, this.level, fk, bwl6, akq, ItemStack.EMPTY);
        }
        return this.setBlock(fk, Blocks.AIR.defaultBlockState(), 3);
    }
    
    @Nullable
    @Override
    public BlockEntity getBlockEntity(final BlockPos fk) {
        final ChunkAccess bzy3 = this.getChunk(fk);
        BlockEntity bwl4 = bzy3.getBlockEntity(fk);
        if (bwl4 != null) {
            return bwl4;
        }
        final CompoundTag jt5 = bzy3.getBlockEntityNbt(fk);
        if (jt5 != null) {
            if ("DUMMY".equals(jt5.getString("id"))) {
                final Block bph6 = this.getBlockState(fk).getBlock();
                if (!(bph6 instanceof EntityBlock)) {
                    return null;
                }
                bwl4 = ((EntityBlock)bph6).newBlockEntity(this.level);
            }
            else {
                bwl4 = BlockEntity.loadStatic(jt5);
            }
            if (bwl4 != null) {
                bzy3.setBlockEntity(fk, bwl4);
                return bwl4;
            }
        }
        if (bzy3.getBlockState(fk).getBlock() instanceof EntityBlock) {
            WorldGenRegion.LOGGER.warn("Tried to access a block entity before it was created. {}", fk);
        }
        return null;
    }
    
    @Override
    public boolean setBlock(final BlockPos fk, final BlockState byj, final int integer) {
        final ChunkAccess bzy5 = this.getChunk(fk);
        final BlockState byj2 = bzy5.setBlockState(fk, byj, false);
        if (byj2 != null) {
            this.level.onBlockStateChange(fk, byj2, byj);
        }
        final Block bph7 = byj.getBlock();
        if (bph7.isEntityBlock()) {
            if (bzy5.getStatus().getChunkType() == ChunkStatus.ChunkType.LEVELCHUNK) {
                bzy5.setBlockEntity(fk, ((EntityBlock)bph7).newBlockEntity(this));
            }
            else {
                final CompoundTag jt8 = new CompoundTag();
                jt8.putInt("x", fk.getX());
                jt8.putInt("y", fk.getY());
                jt8.putInt("z", fk.getZ());
                jt8.putString("id", "DUMMY");
                bzy5.setBlockEntityNbt(jt8);
            }
        }
        else if (byj2 != null && byj2.getBlock().isEntityBlock()) {
            bzy5.removeBlockEntity(fk);
        }
        if (byj.hasPostProcess(this, fk)) {
            this.markPosForPostprocessing(fk);
        }
        return true;
    }
    
    private void markPosForPostprocessing(final BlockPos fk) {
        this.getChunk(fk).markPosForPostprocessing(fk);
    }
    
    @Override
    public boolean addFreshEntity(final Entity akq) {
        final int integer3 = Mth.floor(akq.getX() / 16.0);
        final int integer4 = Mth.floor(akq.getZ() / 16.0);
        this.getChunk(integer3, integer4).addEntity(akq);
        return true;
    }
    
    @Override
    public boolean removeBlock(final BlockPos fk, final boolean boolean2) {
        return this.setBlock(fk, Blocks.AIR.defaultBlockState(), 3);
    }
    
    @Override
    public WorldBorder getWorldBorder() {
        return this.level.getWorldBorder();
    }
    
    @Override
    public boolean isClientSide() {
        return false;
    }
    
    @Deprecated
    @Override
    public ServerLevel getLevel() {
        return this.level;
    }
    
    @Override
    public LevelData getLevelData() {
        return this.levelData;
    }
    
    @Override
    public DifficultyInstance getCurrentDifficultyAt(final BlockPos fk) {
        if (!this.hasChunk(fk.getX() >> 4, fk.getZ() >> 4)) {
            throw new RuntimeException("We are asking a region for a chunk out of bound");
        }
        return new DifficultyInstance(this.level.getDifficulty(), this.level.getDayTime(), 0L, this.level.getMoonBrightness());
    }
    
    @Override
    public ChunkSource getChunkSource() {
        return this.level.getChunkSource();
    }
    
    @Override
    public long getSeed() {
        return this.seed;
    }
    
    @Override
    public TickList<Block> getBlockTicks() {
        return this.blockTicks;
    }
    
    @Override
    public TickList<Fluid> getLiquidTicks() {
        return this.liquidTicks;
    }
    
    @Override
    public int getSeaLevel() {
        return this.seaLevel;
    }
    
    @Override
    public Random getRandom() {
        return this.random;
    }
    
    @Override
    public void blockUpdated(final BlockPos fk, final Block bph) {
    }
    
    @Override
    public int getHeight(final Heightmap.Types a, final int integer2, final int integer3) {
        return this.getChunk(integer2 >> 4, integer3 >> 4).getHeight(a, integer2 & 0xF, integer3 & 0xF) + 1;
    }
    
    @Override
    public void playSound(@Nullable final Player ayj, final BlockPos fk, final SoundEvent aah, final SoundSource aaj, final float float5, final float float6) {
    }
    
    @Override
    public void addParticle(final ParticleOptions gt, final double double2, final double double3, final double double4, final double double5, final double double6, final double double7) {
    }
    
    @Override
    public void levelEvent(@Nullable final Player ayj, final int integer2, final BlockPos fk, final int integer4) {
    }
    
    @Override
    public BlockPos getSharedSpawnPos() {
        return this.level.getSharedSpawnPos();
    }
    
    @Override
    public Dimension getDimension() {
        return this.dimension;
    }
    
    @Override
    public boolean isStateAtPosition(final BlockPos fk, final Predicate<BlockState> predicate) {
        return predicate.test(this.getBlockState(fk));
    }
    
    @Override
    public <T extends Entity> List<T> getEntitiesOfClass(final Class<? extends T> class1, final AABB cvf, @Nullable final Predicate<? super T> predicate) {
        return Collections.<T>emptyList();
    }
    
    @Override
    public List<Entity> getEntities(@Nullable final Entity akq, final AABB cvf, @Nullable final Predicate<? super Entity> predicate) {
        return Collections.<Entity>emptyList();
    }
    
    @Override
    public List<Player> players() {
        return Collections.<Player>emptyList();
    }
    
    static {
        LOGGER = LogManager.getLogger();
    }
}
