package net.minecraft.world.level.chunk;

import net.minecraft.world.level.levelgen.Heightmap;
import net.minecraft.network.protocol.game.DebugPackets;
import java.util.Map;
import java.util.Iterator;
import net.minecraft.world.level.levelgen.structure.BoundingBox;
import net.minecraft.world.level.levelgen.structure.StructureStart;
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureManager;
import net.minecraft.world.entity.MobCategory;
import net.minecraft.world.level.levelgen.feature.configurations.FeatureConfiguration;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.ReportedException;
import net.minecraft.core.Registry;
import net.minecraft.CrashReport;
import net.minecraft.server.level.WorldGenRegion;
import javax.annotation.Nullable;
import java.util.Locale;
import net.minecraft.world.level.levelgen.feature.Feature;
import net.minecraft.world.level.levelgen.feature.StructureFeature;
import net.minecraft.world.level.Level;
import java.util.ListIterator;
import java.util.List;
import java.util.BitSet;
import java.util.Random;
import net.minecraft.world.level.levelgen.carver.ConfiguredWorldCarver;
import net.minecraft.world.level.levelgen.WorldgenRandom;
import net.minecraft.world.level.levelgen.GenerationStep;
import net.minecraft.world.level.biome.Biome;
import net.minecraft.core.BlockPos;
import net.minecraft.world.level.biome.BiomeManager;
import net.minecraft.world.level.ChunkPos;
import net.minecraft.world.level.biome.BiomeSource;
import net.minecraft.world.level.LevelAccessor;
import net.minecraft.world.level.levelgen.ChunkGeneratorSettings;

public abstract class ChunkGenerator<C extends ChunkGeneratorSettings> {
    protected final LevelAccessor level;
    protected final long seed;
    protected final BiomeSource biomeSource;
    protected final C settings;
    
    public ChunkGenerator(final LevelAccessor bjx, final BiomeSource bkw, final C cbq) {
        this.level = bjx;
        this.seed = bjx.getSeed();
        this.biomeSource = bkw;
        this.settings = cbq;
    }
    
    public void createBiomes(final ChunkAccess bzy) {
        final ChunkPos bjh3 = bzy.getPos();
        ((ProtoChunk)bzy).setBiomes(new ChunkBiomeContainer(bjh3, this.biomeSource));
    }
    
    protected Biome getCarvingOrDecorationBiome(final BiomeManager bkv, final BlockPos fk) {
        return bkv.getBiome(fk);
    }
    
    public void applyCarvers(final BiomeManager bkv, final ChunkAccess bzy, final GenerationStep.Carving a) {
        final WorldgenRandom ccf5 = new WorldgenRandom();
        final int integer6 = 8;
        final ChunkPos bjh7 = bzy.getPos();
        final int integer7 = bjh7.x;
        final int integer8 = bjh7.z;
        final Biome bkt10 = this.getCarvingOrDecorationBiome(bkv, bjh7.getWorldPosition());
        final BitSet bitSet11 = bzy.getCarvingMask(a);
        for (int integer9 = integer7 - 8; integer9 <= integer7 + 8; ++integer9) {
            for (int integer10 = integer8 - 8; integer10 <= integer8 + 8; ++integer10) {
                final List<ConfiguredWorldCarver<?>> list14 = bkt10.getCarvers(a);
                final ListIterator<ConfiguredWorldCarver<?>> listIterator15 = list14.listIterator();
                while (listIterator15.hasNext()) {
                    final int integer11 = listIterator15.nextIndex();
                    final ConfiguredWorldCarver<?> ccj17 = listIterator15.next();
                    ccf5.setLargeFeatureSeed(this.seed + integer11, integer9, integer10);
                    if (ccj17.isStartChunk(ccf5, integer9, integer10)) {
                        ccj17.carve(bzy, fk -> this.getCarvingOrDecorationBiome(bkv, fk), (Random)ccf5, this.getSeaLevel(), integer9, integer10, integer7, integer8, bitSet11);
                    }
                }
            }
        }
    }
    
    @Nullable
    public BlockPos findNearestMapFeature(final Level bjw, final String string, final BlockPos fk, final int integer, final boolean boolean5) {
        final StructureFeature<?> cfl7 = Feature.STRUCTURES_REGISTRY.get(string.toLowerCase(Locale.ROOT));
        if (cfl7 != null) {
            return cfl7.getNearestGeneratedFeature(bjw, this, fk, integer, boolean5);
        }
        return null;
    }
    
    public void applyBiomeDecoration(final WorldGenRegion xj) {
        final int integer3 = xj.getCenterX();
        final int integer4 = xj.getCenterZ();
        final int integer5 = integer3 * 16;
        final int integer6 = integer4 * 16;
        final BlockPos fk7 = new BlockPos(integer5, 0, integer6);
        final Biome bkt8 = this.getCarvingOrDecorationBiome(xj.getBiomeManager(), fk7.offset(8, 8, 8));
        final WorldgenRandom ccf9 = new WorldgenRandom();
        final long long10 = ccf9.setDecorationSeed(xj.getSeed(), integer5, integer6);
        for (final GenerationStep.Decoration b15 : GenerationStep.Decoration.values()) {
            try {
                bkt8.generate(b15, this, xj, long10, ccf9, fk7);
            }
            catch (Exception exception16) {
                final CrashReport h17 = CrashReport.forThrowable(exception16, "Biome decoration");
                h17.addCategory("Generation").setDetail("CenterX", integer3).setDetail("CenterZ", integer4).setDetail("Step", b15).setDetail("Seed", long10).setDetail("Biome", Registry.BIOME.getKey(bkt8));
                throw new ReportedException(h17);
            }
        }
    }
    
    public abstract void buildSurfaceAndBedrock(final WorldGenRegion xj, final ChunkAccess bzy);
    
    public void spawnOriginalMobs(final WorldGenRegion xj) {
    }
    
    public C getSettings() {
        return this.settings;
    }
    
    public abstract int getSpawnHeight();
    
    public void tickCustomSpawners(final ServerLevel xd, final boolean boolean2, final boolean boolean3) {
    }
    
    public boolean isBiomeValidStartForStructure(final Biome bkt, final StructureFeature<? extends FeatureConfiguration> cfl) {
        return bkt.isValidStart(cfl);
    }
    
    @Nullable
    public <C extends FeatureConfiguration> C getStructureConfiguration(final Biome bkt, final StructureFeature<C> cfl) {
        return bkt.<C>getStructureConfiguration(cfl);
    }
    
    public BiomeSource getBiomeSource() {
        return this.biomeSource;
    }
    
    public long getSeed() {
        return this.seed;
    }
    
    public int getGenDepth() {
        return 256;
    }
    
    public List<Biome.SpawnerData> getMobsAt(final MobCategory alb, final BlockPos fk) {
        return this.level.getBiome(fk).getMobs(alb);
    }
    
    public void createStructures(final BiomeManager bkv, final ChunkAccess bzy, final ChunkGenerator<?> caa, final StructureManager cmo) {
        for (final StructureFeature<?> cfl7 : Feature.STRUCTURES_REGISTRY.values()) {
            if (!caa.getBiomeSource().canGenerateStructure(cfl7)) {
                continue;
            }
            final StructureStart clv8 = bzy.getStartForFeature(cfl7.getFeatureName());
            final int integer9 = (clv8 != null) ? clv8.getReferences() : 0;
            final WorldgenRandom ccf10 = new WorldgenRandom();
            final ChunkPos bjh11 = bzy.getPos();
            StructureStart clv9 = StructureStart.INVALID_START;
            final Biome bkt13 = bkv.getBiome(new BlockPos(bjh11.getMinBlockX() + 9, 0, bjh11.getMinBlockZ() + 9));
            if (cfl7.isFeatureChunk(bkv, caa, ccf10, bjh11.x, bjh11.z, bkt13)) {
                final StructureStart clv10 = cfl7.getStartFactory().create(cfl7, bjh11.x, bjh11.z, BoundingBox.getUnknownBox(), integer9, caa.getSeed());
                clv10.generatePieces(this, cmo, bjh11.x, bjh11.z, bkt13);
                clv9 = (clv10.isValid() ? clv10 : StructureStart.INVALID_START);
            }
            bzy.setStartForFeature(cfl7.getFeatureName(), clv9);
        }
    }
    
    public void createReferences(final LevelAccessor bjx, final ChunkAccess bzy) {
        final int integer4 = 8;
        final int integer5 = bzy.getPos().x;
        final int integer6 = bzy.getPos().z;
        final int integer7 = integer5 << 4;
        final int integer8 = integer6 << 4;
        for (int integer9 = integer5 - 8; integer9 <= integer5 + 8; ++integer9) {
            for (int integer10 = integer6 - 8; integer10 <= integer6 + 8; ++integer10) {
                final long long11 = ChunkPos.asLong(integer9, integer10);
                for (final Map.Entry<String, StructureStart> entry14 : bjx.getChunk(integer9, integer10).getAllStarts().entrySet()) {
                    final StructureStart clv15 = entry14.getValue();
                    if (clv15 != StructureStart.INVALID_START && clv15.getBoundingBox().intersects(integer7, integer8, integer7 + 15, integer8 + 15)) {
                        bzy.addReferenceForFeature(entry14.getKey(), long11);
                        DebugPackets.sendStructurePacket(bjx, clv15);
                    }
                }
            }
        }
    }
    
    public abstract void fillFromNoise(final LevelAccessor bjx, final ChunkAccess bzy);
    
    public int getSeaLevel() {
        return 63;
    }
    
    public abstract int getBaseHeight(final int integer1, final int integer2, final Heightmap.Types a);
    
    public int getFirstFreeHeight(final int integer1, final int integer2, final Heightmap.Types a) {
        return this.getBaseHeight(integer1, integer2, a);
    }
    
    public int getFirstOccupiedHeight(final int integer1, final int integer2, final Heightmap.Types a) {
        return this.getBaseHeight(integer1, integer2, a) - 1;
    }
}
