package net.minecraft.world.level.chunk;

import net.minecraft.Util;
import com.google.common.collect.ImmutableList;
import net.minecraft.world.level.LevelAccessor;
import net.minecraft.world.level.levelgen.GenerationStep;
import java.util.Set;
import net.minecraft.world.level.lighting.LevelLightEngine;
import net.minecraft.server.level.WorldGenRegion;
import it.unimi.dsi.fastutil.ints.IntArrayList;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureManager;
import net.minecraft.server.level.ServerLevel;
import java.util.Collections;
import com.google.common.collect.Lists;
import net.minecraft.core.Registry;
import javax.annotation.Nullable;
import java.util.function.Function;
import net.minecraft.server.level.ChunkHolder;
import com.mojang.datafixers.util.Either;
import java.util.concurrent.CompletableFuture;
import net.minecraft.server.level.ThreadedLevelLightEngine;
import it.unimi.dsi.fastutil.ints.IntList;
import java.util.List;
import net.minecraft.world.level.levelgen.Heightmap;
import java.util.EnumSet;

public class ChunkStatus {
    private static final EnumSet<Heightmap.Types> PRE_FEATURES;
    private static final EnumSet<Heightmap.Types> POST_FEATURES;
    private static final LoadingTask PASSTHROUGH_LOAD_TASK;
    public static final ChunkStatus EMPTY;
    public static final ChunkStatus STRUCTURE_STARTS;
    public static final ChunkStatus STRUCTURE_REFERENCES;
    public static final ChunkStatus BIOMES;
    public static final ChunkStatus NOISE;
    public static final ChunkStatus SURFACE;
    public static final ChunkStatus CARVERS;
    public static final ChunkStatus LIQUID_CARVERS;
    public static final ChunkStatus FEATURES;
    public static final ChunkStatus LIGHT;
    public static final ChunkStatus SPAWN;
    public static final ChunkStatus HEIGHTMAPS;
    public static final ChunkStatus FULL;
    private static final List<ChunkStatus> STATUS_BY_RANGE;
    private static final IntList RANGE_BY_STATUS;
    private final String name;
    private final int index;
    private final ChunkStatus parent;
    private final GenerationTask generationTask;
    private final LoadingTask loadingTask;
    private final int range;
    private final ChunkType chunkType;
    private final EnumSet<Heightmap.Types> heightmapsAfter;
    
    private static CompletableFuture<Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure>> lightChunk(final ChunkStatus chc, final ThreadedLevelLightEngine zg, final ChunkAccess cgy) {
        final boolean boolean4 = isLighted(chc, cgy);
        if (!cgy.getStatus().isOrAfter(chc)) {
            ((ProtoChunk)cgy).setStatus(chc);
        }
        return zg.lightChunk(cgy, boolean4).<Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure>>thenApply(Either::left);
    }
    
    private static ChunkStatus registerSimple(final String string, @Nullable final ChunkStatus chc, final int integer, final EnumSet<Heightmap.Types> enumSet, final ChunkType a, final SimpleGenerationTask d) {
        return register(string, chc, integer, enumSet, a, d);
    }
    
    private static ChunkStatus register(final String string, @Nullable final ChunkStatus chc, final int integer, final EnumSet<Heightmap.Types> enumSet, final ChunkType a, final GenerationTask b) {
        return register(string, chc, integer, enumSet, a, b, ChunkStatus.PASSTHROUGH_LOAD_TASK);
    }
    
    private static ChunkStatus register(final String string, @Nullable final ChunkStatus chc, final int integer, final EnumSet<Heightmap.Types> enumSet, final ChunkType a, final GenerationTask b, final LoadingTask c) {
        return Registry.<ChunkStatus>register(Registry.CHUNK_STATUS, string, new ChunkStatus(string, chc, integer, enumSet, a, b, c));
    }
    
    public static List<ChunkStatus> getStatusList() {
        final List<ChunkStatus> list1 = Lists.newArrayList();
        ChunkStatus chc2;
        for (chc2 = ChunkStatus.FULL; chc2.getParent() != chc2; chc2 = chc2.getParent()) {
            list1.add(chc2);
        }
        list1.add(chc2);
        Collections.reverse(list1);
        return list1;
    }
    
    private static boolean isLighted(final ChunkStatus chc, final ChunkAccess cgy) {
        return cgy.getStatus().isOrAfter(chc) && cgy.isLightCorrect();
    }
    
    public static ChunkStatus getStatus(final int integer) {
        if (integer >= ChunkStatus.STATUS_BY_RANGE.size()) {
            return ChunkStatus.EMPTY;
        }
        if (integer < 0) {
            return ChunkStatus.FULL;
        }
        return ChunkStatus.STATUS_BY_RANGE.get(integer);
    }
    
    public static int maxDistance() {
        return ChunkStatus.STATUS_BY_RANGE.size();
    }
    
    public static int getDistance(final ChunkStatus chc) {
        return ChunkStatus.RANGE_BY_STATUS.getInt(chc.getIndex());
    }
    
    ChunkStatus(final String string, @Nullable final ChunkStatus chc, final int integer, final EnumSet<Heightmap.Types> enumSet, final ChunkType a, final GenerationTask b, final LoadingTask c) {
        this.name = string;
        this.parent = ((chc == null) ? this : chc);
        this.generationTask = b;
        this.loadingTask = c;
        this.range = integer;
        this.chunkType = a;
        this.heightmapsAfter = enumSet;
        this.index = ((chc == null) ? 0 : (chc.getIndex() + 1));
    }
    
    public int getIndex() {
        return this.index;
    }
    
    public String getName() {
        return this.name;
    }
    
    public ChunkStatus getParent() {
        return this.parent;
    }
    
    public CompletableFuture<Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure>> generate(final ServerLevel zd, final ChunkGenerator cha, final StructureManager cva, final ThreadedLevelLightEngine zg, final Function<ChunkAccess, CompletableFuture<Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure>>> function, final List<ChunkAccess> list) {
        return this.generationTask.doWork(this, zd, cha, cva, zg, function, list, list.get(list.size() / 2));
    }
    
    public CompletableFuture<Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure>> load(final ServerLevel zd, final StructureManager cva, final ThreadedLevelLightEngine zg, final Function<ChunkAccess, CompletableFuture<Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure>>> function, final ChunkAccess cgy) {
        return this.loadingTask.doWork(this, zd, cva, zg, function, cgy);
    }
    
    public int getRange() {
        return this.range;
    }
    
    public ChunkType getChunkType() {
        return this.chunkType;
    }
    
    public static ChunkStatus byName(final String string) {
        return Registry.CHUNK_STATUS.get(ResourceLocation.tryParse(string));
    }
    
    public EnumSet<Heightmap.Types> heightmapsAfter() {
        return this.heightmapsAfter;
    }
    
    public boolean isOrAfter(final ChunkStatus chc) {
        return this.getIndex() >= chc.getIndex();
    }
    
    @Override
    public String toString() {
        return Registry.CHUNK_STATUS.getKey(this).toString();
    }
    
    static {
        PRE_FEATURES = EnumSet.<Heightmap.Types>of(Heightmap.Types.OCEAN_FLOOR_WG, Heightmap.Types.WORLD_SURFACE_WG);
        POST_FEATURES = EnumSet.<Heightmap.Types>of(Heightmap.Types.OCEAN_FLOOR, Heightmap.Types.WORLD_SURFACE, Heightmap.Types.MOTION_BLOCKING, Heightmap.Types.MOTION_BLOCKING_NO_LEAVES);
        PASSTHROUGH_LOAD_TASK = ((chc, zd, cva, zg, function, cgy) -> {
            if (cgy instanceof ProtoChunk && !cgy.getStatus().isOrAfter(chc)) {
                cgy.setStatus(chc);
            }
            return CompletableFuture.<Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure>>completedFuture(Either.left(cgy));
        });
        EMPTY = registerSimple("empty", null, -1, ChunkStatus.PRE_FEATURES, ChunkType.PROTOCHUNK, (zd, cha, list, cgy) -> {});
        STRUCTURE_STARTS = register("structure_starts", ChunkStatus.EMPTY, 0, ChunkStatus.PRE_FEATURES, ChunkType.PROTOCHUNK, (chc, zd, cha, cva, zg, function, list, cgy) -> {
            if (!cgy.getStatus().isOrAfter(chc)) {
                if (zd.getServer().getWorldData().worldGenSettings().generateFeatures()) {
                    cha.createStructures(zd.structureFeatureManager(), cgy, cva, zd.getSeed());
                }
                if (cgy instanceof ProtoChunk) {
                    cgy.setStatus(chc);
                }
            }
            return CompletableFuture.<Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure>>completedFuture(Either.left(cgy));
        });
        final WorldGenRegion zj5;
        STRUCTURE_REFERENCES = registerSimple("structure_references", ChunkStatus.STRUCTURE_STARTS, 8, ChunkStatus.PRE_FEATURES, ChunkType.PROTOCHUNK, (zd, cha, list, cgy) -> {
            zj5 = new WorldGenRegion(zd, list);
            cha.createReferences(zj5, zd.structureFeatureManager().forWorldGenRegion(zj5), cgy);
            return;
        });
        BIOMES = registerSimple("biomes", ChunkStatus.STRUCTURE_REFERENCES, 0, ChunkStatus.PRE_FEATURES, ChunkType.PROTOCHUNK, (zd, cha, list, cgy) -> cha.createBiomes(cgy));
        final WorldGenRegion zj6;
        NOISE = registerSimple("noise", ChunkStatus.BIOMES, 8, ChunkStatus.PRE_FEATURES, ChunkType.PROTOCHUNK, (zd, cha, list, cgy) -> {
            zj6 = new WorldGenRegion(zd, list);
            cha.fillFromNoise(zj6, zd.structureFeatureManager().forWorldGenRegion(zj6), cgy);
            return;
        });
        SURFACE = registerSimple("surface", ChunkStatus.NOISE, 0, ChunkStatus.PRE_FEATURES, ChunkType.PROTOCHUNK, (zd, cha, list, cgy) -> cha.buildSurfaceAndBedrock(new WorldGenRegion(zd, list), cgy));
        CARVERS = registerSimple("carvers", ChunkStatus.SURFACE, 0, ChunkStatus.PRE_FEATURES, ChunkType.PROTOCHUNK, (zd, cha, list, cgy) -> cha.applyCarvers(zd.getSeed(), zd.getBiomeManager(), cgy, GenerationStep.Carving.AIR));
        LIQUID_CARVERS = registerSimple("liquid_carvers", ChunkStatus.CARVERS, 0, ChunkStatus.POST_FEATURES, ChunkType.PROTOCHUNK, (zd, cha, list, cgy) -> cha.applyCarvers(zd.getSeed(), zd.getBiomeManager(), cgy, GenerationStep.Carving.LIQUID));
        final ProtoChunk chr9;
        WorldGenRegion zj7;
        FEATURES = register("features", ChunkStatus.LIQUID_CARVERS, 8, ChunkStatus.POST_FEATURES, ChunkType.PROTOCHUNK, (chc, zd, cha, cva, zg, function, list, cgy) -> {
            chr9 = cgy;
            chr9.setLightEngine(zg);
            if (!cgy.getStatus().isOrAfter(chc)) {
                Heightmap.primeHeightmaps(cgy, EnumSet.<Heightmap.Types>of(Heightmap.Types.MOTION_BLOCKING, Heightmap.Types.MOTION_BLOCKING_NO_LEAVES, Heightmap.Types.OCEAN_FLOOR, Heightmap.Types.WORLD_SURFACE));
                zj7 = new WorldGenRegion(zd, list);
                cha.applyBiomeDecoration(zj7, zd.structureFeatureManager().forWorldGenRegion(zj7));
                chr9.setStatus(chc);
            }
            return CompletableFuture.<Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure>>completedFuture(Either.left(cgy));
        });
        LIGHT = register("light", ChunkStatus.FEATURES, 1, ChunkStatus.POST_FEATURES, ChunkType.PROTOCHUNK, (chc, zd, cha, cva, zg, function, list, cgy) -> lightChunk(chc, zg, cgy), (chc, zd, cva, zg, function, cgy) -> lightChunk(chc, zg, cgy));
        SPAWN = registerSimple("spawn", ChunkStatus.LIGHT, 0, ChunkStatus.POST_FEATURES, ChunkType.PROTOCHUNK, (zd, cha, list, cgy) -> cha.spawnOriginalMobs(new WorldGenRegion(zd, list)));
        HEIGHTMAPS = registerSimple("heightmaps", ChunkStatus.SPAWN, 0, ChunkStatus.POST_FEATURES, ChunkType.PROTOCHUNK, (zd, cha, list, cgy) -> {});
        FULL = register("full", ChunkStatus.HEIGHTMAPS, 0, ChunkStatus.POST_FEATURES, ChunkType.LEVELCHUNK, (chc, zd, cha, cva, zg, function, list, cgy) -> function.apply(cgy), (chc, zd, cva, zg, function, cgy) -> function.apply(cgy));
        STATUS_BY_RANGE = ImmutableList.<ChunkStatus>of(ChunkStatus.FULL, ChunkStatus.FEATURES, ChunkStatus.LIQUID_CARVERS, ChunkStatus.STRUCTURE_STARTS, ChunkStatus.STRUCTURE_STARTS, ChunkStatus.STRUCTURE_STARTS, ChunkStatus.STRUCTURE_STARTS, ChunkStatus.STRUCTURE_STARTS, ChunkStatus.STRUCTURE_STARTS, ChunkStatus.STRUCTURE_STARTS, ChunkStatus.STRUCTURE_STARTS);
        int integer2;
        int integer3;
        RANGE_BY_STATUS = Util.<IntList>make((IntList)new IntArrayList(getStatusList().size()), intArrayList -> {
            integer2 = 0;
            for (integer3 = getStatusList().size() - 1; integer3 >= 0; --integer3) {
                while (integer2 + 1 < ChunkStatus.STATUS_BY_RANGE.size() && integer3 <= ChunkStatus.STATUS_BY_RANGE.get(integer2 + 1).getIndex()) {
                    ++integer2;
                }
                intArrayList.add(0, integer2);
            }
        });
    }
    
    interface SimpleGenerationTask extends GenerationTask {
        default CompletableFuture<Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure>> doWork(final ChunkStatus chc, final ServerLevel zd, final ChunkGenerator cha, final StructureManager cva, final ThreadedLevelLightEngine zg, final Function<ChunkAccess, CompletableFuture<Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure>>> function, final List<ChunkAccess> list, final ChunkAccess cgy) {
            if (!cgy.getStatus().isOrAfter(chc)) {
                this.doWork(zd, cha, list, cgy);
                if (cgy instanceof ProtoChunk) {
                    ((ProtoChunk)cgy).setStatus(chc);
                }
            }
            return CompletableFuture.<Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure>>completedFuture(Either.left(cgy));
        }
        
        void doWork(final ServerLevel zd, final ChunkGenerator cha, final List<ChunkAccess> list, final ChunkAccess cgy);
    }
    
    public enum ChunkType {
        PROTOCHUNK, 
        LEVELCHUNK;
    }
    
    interface GenerationTask {
        CompletableFuture<Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure>> doWork(final ChunkStatus chc, final ServerLevel zd, final ChunkGenerator cha, final StructureManager cva, final ThreadedLevelLightEngine zg, final Function<ChunkAccess, CompletableFuture<Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure>>> function, final List<ChunkAccess> list, final ChunkAccess cgy);
    }
    
    interface LoadingTask {
        CompletableFuture<Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure>> doWork(final ChunkStatus chc, final ServerLevel zd, final StructureManager cva, final ThreadedLevelLightEngine zg, final Function<ChunkAccess, CompletableFuture<Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure>>> function, final ChunkAccess cgy);
    }
}
