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.core.Direction;
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.Util;
import net.minecraft.util.Mth;
import net.minecraft.world.level.ChunkPos;
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.dimension.DimensionType;
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.WorldGenLevel;

public class WorldGenRegion implements WorldGenLevel {
    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 LevelData levelData;
    private final Random random;
    private final DimensionType dimensionType;
    private final TickList<Block> blockTicks;
    private final TickList<Fluid> liquidTicks;
    private final BiomeManager biomeManager;
    private final ChunkPos firstPos;
    private final ChunkPos lastPos;
    
    public WorldGenRegion(final ServerLevel zd, final List<ChunkAccess> list) {
        this.blockTicks = new WorldGenTickList<Block>(fu -> this.getChunk(fu).getBlockTicks());
        this.liquidTicks = new WorldGenTickList<Fluid>(fu -> this.getChunk(fu).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 bph5 = list.get(list.size() / 2).getPos();
        this.cache = list;
        this.x = bph5.x;
        this.z = bph5.z;
        this.size = integer4;
        this.level = zd;
        this.seed = zd.getSeed();
        this.levelData = zd.getLevelData();
        this.random = zd.getRandom();
        this.dimensionType = zd.dimensionType();
        this.biomeManager = new BiomeManager(this, BiomeManager.obfuscateSeed(this.seed), zd.dimensionType().getBiomeZoomer());
        this.firstPos = list.get(0).getPos();
        this.lastPos = list.get(list.size() - 1).getPos();
    }
    
    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 chc, final boolean boolean4) {
        ChunkAccess cgy6;
        if (this.hasChunk(integer1, integer2)) {
            final int integer3 = integer1 - this.firstPos.x;
            final int integer4 = integer2 - this.firstPos.z;
            cgy6 = this.cache.get(integer3 + integer4 * this.size);
            if (cgy6.getStatus().isOrAfter(chc)) {
                return cgy6;
            }
        }
        else {
            cgy6 = null;
        }
        if (!boolean4) {
            return null;
        }
        WorldGenRegion.LOGGER.error("Requested chunk : {} {}", integer1, integer2);
        WorldGenRegion.LOGGER.error("Region bounds : {} {} | {} {}", this.firstPos.x, this.firstPos.z, this.lastPos.x, this.lastPos.z);
        if (cgy6 != null) {
            throw Util.<RuntimeException>pauseInIde(new RuntimeException(String.format("Chunk is not of correct status. Expecting %s, got %s | %s %s", chc, cgy6.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) {
        return integer1 >= this.firstPos.x && integer1 <= this.lastPos.x && integer2 >= this.firstPos.z && integer2 <= this.lastPos.z;
    }
    
    @Override
    public BlockState getBlockState(final BlockPos fu) {
        return this.getChunk(fu.getX() >> 4, fu.getZ() >> 4).getBlockState(fu);
    }
    
    @Override
    public FluidState getFluidState(final BlockPos fu) {
        return this.getChunk(fu).getFluidState(fu);
    }
    
    @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 float getShade(final Direction fz, final boolean boolean2) {
        return 1.0f;
    }
    
    @Override
    public LevelLightEngine getLightEngine() {
        return this.level.getLightEngine();
    }
    
    @Override
    public boolean destroyBlock(final BlockPos fu, final boolean boolean2, @Nullable final Entity aom, final int integer) {
        final BlockState cfj6 = this.getBlockState(fu);
        if (cfj6.isAir()) {
            return false;
        }
        if (boolean2) {
            final BlockEntity cdl7 = cfj6.getBlock().isEntityBlock() ? this.getBlockEntity(fu) : null;
            Block.dropResources(cfj6, this.level, fu, cdl7, aom, ItemStack.EMPTY);
        }
        return this.setBlock(fu, Blocks.AIR.defaultBlockState(), 3, integer);
    }
    
    @Nullable
    @Override
    public BlockEntity getBlockEntity(final BlockPos fu) {
        final ChunkAccess cgy3 = this.getChunk(fu);
        BlockEntity cdl4 = cgy3.getBlockEntity(fu);
        if (cdl4 != null) {
            return cdl4;
        }
        final CompoundTag le5 = cgy3.getBlockEntityNbt(fu);
        final BlockState cfj6 = cgy3.getBlockState(fu);
        if (le5 != null) {
            if ("DUMMY".equals(le5.getString("id"))) {
                final Block bvr7 = cfj6.getBlock();
                if (!(bvr7 instanceof EntityBlock)) {
                    return null;
                }
                cdl4 = ((EntityBlock)bvr7).newBlockEntity(this.level);
            }
            else {
                cdl4 = BlockEntity.loadStatic(cfj6, le5);
            }
            if (cdl4 != null) {
                cgy3.setBlockEntity(fu, cdl4);
                return cdl4;
            }
        }
        if (cfj6.getBlock() instanceof EntityBlock) {
            WorldGenRegion.LOGGER.warn("Tried to access a block entity before it was created. {}", fu);
        }
        return null;
    }
    
    @Override
    public boolean setBlock(final BlockPos fu, final BlockState cfj, final int integer3, final int integer4) {
        final ChunkAccess cgy6 = this.getChunk(fu);
        final BlockState cfj2 = cgy6.setBlockState(fu, cfj, false);
        if (cfj2 != null) {
            this.level.onBlockStateChange(fu, cfj2, cfj);
        }
        final Block bvr8 = cfj.getBlock();
        if (bvr8.isEntityBlock()) {
            if (cgy6.getStatus().getChunkType() == ChunkStatus.ChunkType.LEVELCHUNK) {
                cgy6.setBlockEntity(fu, ((EntityBlock)bvr8).newBlockEntity(this));
            }
            else {
                final CompoundTag le9 = new CompoundTag();
                le9.putInt("x", fu.getX());
                le9.putInt("y", fu.getY());
                le9.putInt("z", fu.getZ());
                le9.putString("id", "DUMMY");
                cgy6.setBlockEntityNbt(le9);
            }
        }
        else if (cfj2 != null && cfj2.getBlock().isEntityBlock()) {
            cgy6.removeBlockEntity(fu);
        }
        if (cfj.hasPostProcess(this, fu)) {
            this.markPosForPostprocessing(fu);
        }
        return true;
    }
    
    private void markPosForPostprocessing(final BlockPos fu) {
        this.getChunk(fu).markPosForPostprocessing(fu);
    }
    
    @Override
    public boolean addFreshEntity(final Entity aom) {
        final int integer3 = Mth.floor(aom.getX() / 16.0);
        final int integer4 = Mth.floor(aom.getZ() / 16.0);
        this.getChunk(integer3, integer4).addEntity(aom);
        return true;
    }
    
    @Override
    public boolean removeBlock(final BlockPos fu, final boolean boolean2) {
        return this.setBlock(fu, 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 fu) {
        if (!this.hasChunk(fu.getX() >> 4, fu.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.level.getSeaLevel();
    }
    
    @Override
    public Random getRandom() {
        return this.random;
    }
    
    @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 bec, final BlockPos fu, final SoundEvent ack, final SoundSource acm, final float float5, final float float6) {
    }
    
    @Override
    public void addParticle(final ParticleOptions hf, 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 bec, final int integer2, final BlockPos fu, final int integer4) {
    }
    
    @Override
    public DimensionType dimensionType() {
        return this.dimensionType;
    }
    
    @Override
    public boolean isStateAtPosition(final BlockPos fu, final Predicate<BlockState> predicate) {
        return predicate.test(this.getBlockState(fu));
    }
    
    @Override
    public <T extends Entity> List<T> getEntitiesOfClass(final Class<? extends T> class1, final AABB deg, @Nullable final Predicate<? super T> predicate) {
        return Collections.<T>emptyList();
    }
    
    @Override
    public List<Entity> getEntities(@Nullable final Entity aom, final AABB deg, @Nullable final Predicate<? super Entity> predicate) {
        return Collections.<Entity>emptyList();
    }
    
    @Override
    public List<Player> players() {
        return Collections.<Player>emptyList();
    }
    
    static {
        LOGGER = LogManager.getLogger();
    }
}
