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 cae, final ThreadedLevelLightEngine xg, final ChunkAccess bzy) {
        final boolean boolean4 = isLighted(cae, bzy);
        if (!bzy.getStatus().isOrAfter(cae)) {
            ((ProtoChunk)bzy).setStatus(cae);
        }
        return xg.lightChunk(bzy, boolean4).<Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure>>thenApply(Either::left);
    }
    
    private static ChunkStatus registerSimple(final String string, @Nullable final ChunkStatus cae, final int integer, final EnumSet<Heightmap.Types> enumSet, final ChunkType a, final SimpleGenerationTask d) {
        return register(string, cae, integer, enumSet, a, d);
    }
    
    private static ChunkStatus register(final String string, @Nullable final ChunkStatus cae, final int integer, final EnumSet<Heightmap.Types> enumSet, final ChunkType a, final GenerationTask b) {
        return register(string, cae, integer, enumSet, a, b, ChunkStatus.PASSTHROUGH_LOAD_TASK);
    }
    
    private static ChunkStatus register(final String string, @Nullable final ChunkStatus cae, 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, cae, integer, enumSet, a, b, c));
    }
    
    public static List<ChunkStatus> getStatusList() {
        final List<ChunkStatus> list1 = Lists.newArrayList();
        ChunkStatus cae2;
        for (cae2 = ChunkStatus.FULL; cae2.getParent() != cae2; cae2 = cae2.getParent()) {
            list1.add(cae2);
        }
        list1.add(cae2);
        Collections.reverse(list1);
        return list1;
    }
    
    private static boolean isLighted(final ChunkStatus cae, final ChunkAccess bzy) {
        return bzy.getStatus().isOrAfter(cae) && bzy.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 cae) {
        return ChunkStatus.RANGE_BY_STATUS.getInt(cae.getIndex());
    }
    
    ChunkStatus(final String string, @Nullable final ChunkStatus cae, final int integer, final EnumSet<Heightmap.Types> enumSet, final ChunkType a, final GenerationTask b, final LoadingTask c) {
        this.name = string;
        this.parent = ((cae == null) ? this : cae);
        this.generationTask = b;
        this.loadingTask = c;
        this.range = integer;
        this.chunkType = a;
        this.heightmapsAfter = enumSet;
        this.index = ((cae == null) ? 0 : (cae.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 xd, final ChunkGenerator<?> caa, final StructureManager cmo, final ThreadedLevelLightEngine xg, final Function<ChunkAccess, CompletableFuture<Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure>>> function, final List<ChunkAccess> list) {
        return this.generationTask.doWork(this, xd, caa, cmo, xg, function, list, list.get(list.size() / 2));
    }
    
    public CompletableFuture<Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure>> load(final ServerLevel xd, final StructureManager cmo, final ThreadedLevelLightEngine xg, final Function<ChunkAccess, CompletableFuture<Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure>>> function, final ChunkAccess bzy) {
        return this.loadingTask.doWork(this, xd, cmo, xg, function, bzy);
    }
    
    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 cae) {
        return this.getIndex() >= cae.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 = ((cae, xd, cmo, xg, function, bzy) -> {
            if (bzy instanceof ProtoChunk && !bzy.getStatus().isOrAfter(cae)) {
                bzy.setStatus(cae);
            }
            return CompletableFuture.<Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure>>completedFuture(Either.left(bzy));
        });
        EMPTY = registerSimple("empty", null, -1, ChunkStatus.PRE_FEATURES, ChunkType.PROTOCHUNK, (xd, caa, list, bzy) -> {});
        STRUCTURE_STARTS = register("structure_starts", ChunkStatus.EMPTY, 0, ChunkStatus.PRE_FEATURES, ChunkType.PROTOCHUNK, (cae, xd, caa, cmo, xg, function, list, bzy) -> {
            if (!bzy.getStatus().isOrAfter(cae)) {
                if (xd.getLevelData().isGenerateMapFeatures()) {
                    caa.createStructures(xd.getBiomeManager().withDifferentSource(caa.getBiomeSource()), bzy, caa, cmo);
                }
                if (bzy instanceof ProtoChunk) {
                    bzy.setStatus(cae);
                }
            }
            return CompletableFuture.<Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure>>completedFuture(Either.left(bzy));
        });
        STRUCTURE_REFERENCES = registerSimple("structure_references", ChunkStatus.STRUCTURE_STARTS, 8, ChunkStatus.PRE_FEATURES, ChunkType.PROTOCHUNK, (xd, caa, list, bzy) -> caa.createReferences(new WorldGenRegion(xd, list), bzy));
        BIOMES = registerSimple("biomes", ChunkStatus.STRUCTURE_REFERENCES, 0, ChunkStatus.PRE_FEATURES, ChunkType.PROTOCHUNK, (xd, caa, list, bzy) -> caa.createBiomes(bzy));
        NOISE = registerSimple("noise", ChunkStatus.BIOMES, 8, ChunkStatus.PRE_FEATURES, ChunkType.PROTOCHUNK, (xd, caa, list, bzy) -> caa.fillFromNoise(new WorldGenRegion(xd, list), bzy));
        SURFACE = registerSimple("surface", ChunkStatus.NOISE, 0, ChunkStatus.PRE_FEATURES, ChunkType.PROTOCHUNK, (xd, caa, list, bzy) -> caa.buildSurfaceAndBedrock(new WorldGenRegion(xd, list), bzy));
        CARVERS = registerSimple("carvers", ChunkStatus.SURFACE, 0, ChunkStatus.PRE_FEATURES, ChunkType.PROTOCHUNK, (xd, caa, list, bzy) -> caa.applyCarvers(xd.getBiomeManager().withDifferentSource(caa.getBiomeSource()), bzy, GenerationStep.Carving.AIR));
        LIQUID_CARVERS = registerSimple("liquid_carvers", ChunkStatus.CARVERS, 0, ChunkStatus.POST_FEATURES, ChunkType.PROTOCHUNK, (xd, caa, list, bzy) -> caa.applyCarvers(xd.getBiomeManager().withDifferentSource(caa.getBiomeSource()), bzy, GenerationStep.Carving.LIQUID));
        final ProtoChunk cat9;
        FEATURES = register("features", ChunkStatus.LIQUID_CARVERS, 8, ChunkStatus.POST_FEATURES, ChunkType.PROTOCHUNK, (cae, xd, caa, cmo, xg, function, list, bzy) -> {
            cat9 = bzy;
            cat9.setLightEngine(xg);
            if (!bzy.getStatus().isOrAfter(cae)) {
                Heightmap.primeHeightmaps(bzy, EnumSet.<Heightmap.Types>of(Heightmap.Types.MOTION_BLOCKING, Heightmap.Types.MOTION_BLOCKING_NO_LEAVES, Heightmap.Types.OCEAN_FLOOR, Heightmap.Types.WORLD_SURFACE));
                caa.applyBiomeDecoration(new WorldGenRegion(xd, list));
                cat9.setStatus(cae);
            }
            return CompletableFuture.<Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure>>completedFuture(Either.left(bzy));
        });
        LIGHT = register("light", ChunkStatus.FEATURES, 1, ChunkStatus.POST_FEATURES, ChunkType.PROTOCHUNK, (cae, xd, caa, cmo, xg, function, list, bzy) -> lightChunk(cae, xg, bzy), (cae, xd, cmo, xg, function, bzy) -> lightChunk(cae, xg, bzy));
        SPAWN = registerSimple("spawn", ChunkStatus.LIGHT, 0, ChunkStatus.POST_FEATURES, ChunkType.PROTOCHUNK, (xd, caa, list, bzy) -> caa.spawnOriginalMobs(new WorldGenRegion(xd, list)));
        HEIGHTMAPS = registerSimple("heightmaps", ChunkStatus.SPAWN, 0, ChunkStatus.POST_FEATURES, ChunkType.PROTOCHUNK, (xd, caa, list, bzy) -> {});
        FULL = register("full", ChunkStatus.HEIGHTMAPS, 0, ChunkStatus.POST_FEATURES, ChunkType.LEVELCHUNK, (cae, xd, caa, cmo, xg, function, list, bzy) -> function.apply(bzy), (cae, xd, cmo, xg, function, bzy) -> function.apply(bzy));
        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 cae, final ServerLevel xd, final ChunkGenerator<?> caa, final StructureManager cmo, final ThreadedLevelLightEngine xg, final Function<ChunkAccess, CompletableFuture<Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure>>> function, final List<ChunkAccess> list, final ChunkAccess bzy) {
            if (!bzy.getStatus().isOrAfter(cae)) {
                this.doWork(xd, caa, list, bzy);
                if (bzy instanceof ProtoChunk) {
                    ((ProtoChunk)bzy).setStatus(cae);
                }
            }
            return CompletableFuture.<Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure>>completedFuture(Either.left(bzy));
        }
        
        void doWork(final ServerLevel xd, final ChunkGenerator<?> caa, final List<ChunkAccess> list, final ChunkAccess bzy);
    }
    
    public enum ChunkType {
        PROTOCHUNK, 
        LEVELCHUNK;
    }
    
    interface GenerationTask {
        CompletableFuture<Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure>> doWork(final ChunkStatus cae, final ServerLevel xd, final ChunkGenerator<?> caa, final StructureManager cmo, final ThreadedLevelLightEngine xg, final Function<ChunkAccess, CompletableFuture<Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure>>> function, final List<ChunkAccess> list, final ChunkAccess bzy);
    }
    
    interface LoadingTask {
        CompletableFuture<Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure>> doWork(final ChunkStatus cae, final ServerLevel xd, final StructureManager cmo, final ThreadedLevelLightEngine xg, final Function<ChunkAccess, CompletableFuture<Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure>>> function, final ChunkAccess bzy);
    }
}
