// 
// Decompiled by Procyon v0.6.0
// 

package com.hypixel.hytale.server.worldgen.chunk.populator;

import com.hypixel.hytale.server.worldgen.util.NoiseBlockArray;
import com.hypixel.hytale.server.core.universe.world.worldgen.GeneratedBlockChunk;
import com.hypixel.hytale.procedurallib.condition.IBlockFluidCondition;
import com.hypixel.hytale.procedurallib.condition.ConstantIntCondition;
import com.hypixel.hytale.server.worldgen.container.WaterContainer;
import com.hypixel.hytale.server.worldgen.container.CoverContainer;
import com.hypixel.hytale.server.worldgen.util.BlockFluidEntry;
import com.hypixel.hytale.server.worldgen.container.LayerContainer;
import com.hypixel.hytale.server.worldgen.biome.Biome;
import it.unimi.dsi.fastutil.ints.IntList;
import com.hypixel.hytale.server.worldgen.chunk.HeightThresholdInterpolator;
import com.hypixel.hytale.server.worldgen.chunk.ChunkGenerator;
import java.util.Random;
import com.hypixel.hytale.math.util.FastRandom;
import com.hypixel.hytale.math.util.HashUtil;
import javax.annotation.Nonnull;
import com.hypixel.hytale.server.worldgen.chunk.ChunkGeneratorExecution;

public class BlockPopulator
{
    public static void populate(final int seed, @Nonnull final ChunkGeneratorExecution execution) {
        final Random random = new FastRandom(HashUtil.hash(seed, execution.getX(), execution.getZ(), 5647422603192711886L));
        for (int cx = 0; cx < 32; ++cx) {
            for (int cz = 0; cz < 32; ++cz) {
                generateBlockColumn(seed, execution, cx, cz, random);
            }
        }
    }
    
    private static void generateBlockColumn(final int seed, @Nonnull final ChunkGeneratorExecution execution, final int cx, final int cz, @Nonnull final Random random) {
        final HeightThresholdInterpolator interpolator = execution.getInterpolator();
        final IntList surfaceBlockList = ChunkGenerator.getResource().coverArray;
        final Biome biome = execution.zoneBiomeResult(cx, cz).getBiome();
        final LayerContainer layerContainer = biome.getLayerContainer();
        final int x = execution.globalX(cx);
        final int z = execution.globalZ(cz);
        final double heightmapNoise = interpolator.getHeightNoise(cx, cz);
        final BlockFluidEntry filling = layerContainer.getFilling();
        final int fillingEnvironment = layerContainer.getFillingEnvironment();
        int highest = 0;
        final int min = interpolator.getLowestNonOne(cx, cz);
        int y = interpolator.getHighestNonZero(cx, cz);
        boolean empty = true;
        while (y >= min) {
            final double threshold = interpolator.getHeightThreshold(seed, x, z, y);
            if (threshold > heightmapNoise || threshold == 1.0) {
                if (y > highest) {
                    highest = y;
                }
                execution.setBlock(cx, y, cz, (byte)1, filling, fillingEnvironment);
                if (empty) {
                    surfaceBlockList.add(y);
                    empty = false;
                }
            }
            else {
                empty = true;
            }
            --y;
        }
        if (empty) {
            surfaceBlockList.add(y);
        }
        if (y > highest) {
            highest = y;
        }
        while (y >= 0) {
            execution.setBlock(cx, y, cz, (byte)1, filling);
            --y;
        }
        execution.getChunkGenerator().putHeight(seed, x, z, highest);
        LayerPopulator.generateLayers(seed, execution, cx, cz, x, z, biome, surfaceBlockList);
        generateCovers(seed, execution, cx, cz, x, z, random, biome, surfaceBlockList);
        surfaceBlockList.clear();
    }
    
    private static void generateCovers(final int seed, @Nonnull final ChunkGeneratorExecution execution, final int cx, final int cz, final int x, final int z, @Nonnull final Random random, @Nonnull final Biome biome, @Nonnull final IntList surfaceBlockList) {
        final CoverContainer coverContainer = biome.getCoverContainer();
        final int size = surfaceBlockList.size();
        if (size == 0) {
            return;
        }
        for (final WaterContainer.Entry waterContainer : biome.getWaterContainer().getEntries()) {
            for (final CoverContainer.CoverContainerEntry coverContainerEntry : coverContainer.getEntries()) {
                if (coverContainerEntry.isOnWater()) {
                    if (isMatchingCoverColumn(seed, coverContainerEntry, random, x, z)) {
                        final int y = waterContainer.getMax(seed, x, z) + 1;
                        if (isMatchingCoverHeight(seed, coverContainerEntry, random, x, y, z)) {
                            if (isMatchingParentCover(execution, coverContainerEntry, cx, cz, y, waterContainer.getBlock(), waterContainer.getFluid())) {
                                final CoverContainer.CoverContainerEntry.CoverContainerEntryPart coverEntry = coverContainerEntry.get(random);
                                execution.setBlock(cx, y + coverEntry.getOffset(), cz, (byte)3, coverEntry.getEntry());
                                execution.setFluid(cx, y + coverEntry.getOffset(), cz, (byte)3, coverEntry.getEntry().fluidId());
                            }
                        }
                    }
                }
            }
        }
        for (int i = 0; i < size; ++i) {
            final int y2 = surfaceBlockList.getInt(i) + 1;
            for (final CoverContainer.CoverContainerEntry coverContainerEntry2 : coverContainer.getEntries()) {
                if (!coverContainerEntry2.isOnWater() && isMatchingParentCover(execution, coverContainerEntry2, cx, cz, y2, 0, 0) && isMatchingCoverColumn(seed, coverContainerEntry2, random, x, z) && isMatchingCoverHeight(seed, coverContainerEntry2, random, x, y2, z)) {
                    final CoverContainer.CoverContainerEntry.CoverContainerEntryPart coverEntry2 = coverContainerEntry2.get(random);
                    execution.setBlock(cx, y2 + coverEntry2.getOffset(), cz, (byte)3, coverEntry2.getEntry());
                    execution.setFluid(cx, y2 + coverEntry2.getOffset(), cz, (byte)3, coverEntry2.getEntry().fluidId());
                }
            }
        }
    }
    
    private static boolean isMatchingParentCover(@Nonnull final ChunkGeneratorExecution execution, @Nonnull final CoverContainer.CoverContainerEntry coverContainerEntry, final int cx, final int cz, final int y, final int defaultId, final int defaultFluidId) {
        if (y <= 0 || y >= 320) {
            return false;
        }
        final IBlockFluidCondition parentCondition = coverContainerEntry.getParentCondition();
        if (parentCondition == ConstantIntCondition.DEFAULT_TRUE) {
            return true;
        }
        if (parentCondition == ConstantIntCondition.DEFAULT_FALSE) {
            return false;
        }
        final GeneratedBlockChunk chunk = execution.getChunk();
        int block = chunk.getBlock(cx, y - 1, cz);
        if (block == 0) {
            block = defaultId;
        }
        int fluid = execution.getFluid(cx, y - 1, cz);
        if (fluid == 0) {
            fluid = defaultFluidId;
        }
        return parentCondition.eval(block, fluid);
    }
    
    private static boolean isMatchingCoverColumn(final int seed, @Nonnull final CoverContainer.CoverContainerEntry coverContainerEntry, @Nonnull final Random random, final int x, final int z) {
        return random.nextDouble() < coverContainerEntry.getCoverDensity() && coverContainerEntry.getMapCondition().eval(seed, x, z);
    }
    
    private static boolean isMatchingCoverHeight(final int seed, @Nonnull final CoverContainer.CoverContainerEntry coverContainerEntry, final Random random, final int x, final int y, final int z) {
        return coverContainerEntry.getHeightCondition().eval(seed, x, z, y, random);
    }
    
    private static class LayerPopulator
    {
        static void generateLayers(final int seed, @Nonnull final ChunkGeneratorExecution execution, final int cx, final int cz, final int x, final int z, @Nonnull final Biome biome, @Nonnull final IntList surfaceBlockList) {
            generateStaticLayers(seed, execution, cx, cz, x, z, biome);
            generateDynamicLayers(seed, execution, cx, cz, x, z, biome, surfaceBlockList);
        }
        
        private static void generateDynamicLayers(final int seed, @Nonnull final ChunkGeneratorExecution execution, final int cx, final int cz, final int x, final int z, @Nonnull final Biome biome, @Nonnull final IntList surfaceBlockList) {
            final LayerContainer layers = biome.getLayerContainer();
            for (int i = 0, size = surfaceBlockList.size(); i < size; ++i) {
                int y;
                int maxY;
                final int surfaceY = maxY = (y = surfaceBlockList.getInt(i));
            Label_0264:
                for (final LayerContainer.DynamicLayer layer : layers.getDynamicLayers()) {
                    final LayerContainer.DynamicLayerEntry entry = layer.getActiveEntry(seed, x, z);
                    if (entry != null) {
                        final int environmentId = layer.getEnvironmentId();
                        y += layer.getOffset(seed, x, z);
                        maxY = Math.max(maxY, y);
                        final NoiseBlockArray blockArray = entry.getBlockArray();
                        for (final NoiseBlockArray.Entry blockArrayEntry : blockArray.getEntries()) {
                            for (int repetitions = blockArrayEntry.getRepetitions(seed, x, z), j = 0; j < repetitions; ++j) {
                                if (y <= surfaceY && execution.getBlock(cx, y, cz) == 0) {
                                    break Label_0264;
                                }
                                execution.setBlock(cx, y, cz, (byte)2, blockArrayEntry.getBlockEntry(), environmentId);
                                execution.setFluid(cx, y, cz, (byte)2, blockArrayEntry.getBlockEntry().fluidId(), environmentId);
                                --y;
                            }
                        }
                    }
                }
                if (maxY > surfaceY) {
                    surfaceBlockList.set(i, maxY);
                }
            }
        }
        
        private static void generateStaticLayers(final int seed, @Nonnull final ChunkGeneratorExecution execution, final int cx, final int cz, final int x, final int z, @Nonnull final Biome biome) {
            final LayerContainer layers = biome.getLayerContainer();
            for (final LayerContainer.StaticLayer layer : layers.getStaticLayers()) {
                final LayerContainer.StaticLayerEntry entry = layer.getActiveEntry(seed, x, z);
                if (entry != null) {
                    final int environmentId = layer.getEnvironmentId();
                    final NoiseBlockArray.Entry[] blockEntries = entry.getBlockArray().getEntries();
                    final int min = Math.max(entry.getMinInt(seed, x, z), 0);
                    final int max = Math.min(entry.getMaxInt(seed, x, z), 320);
                    int layerY = entry.getMaxInt(seed, x, z);
                    BlockFluidEntry lastBlock = null;
                    for (final NoiseBlockArray.Entry blockEntry : blockEntries) {
                        final int repetitions = blockEntry.getRepetitions(seed, x, z);
                        if (repetitions > 0) {
                            final BlockFluidEntry block = lastBlock = blockEntry.getBlockEntry();
                            for (int i = 0; i < repetitions; ++i) {
                                final int currentBlock = execution.getBlock(cx, --layerY, cz);
                                if (currentBlock != 0) {
                                    execution.setBlock(cx, layerY, cz, (byte)2, block, environmentId);
                                    execution.setFluid(cx, layerY, cz, (byte)2, block.fluidId(), environmentId);
                                }
                                if (layerY <= min) {
                                    return;
                                }
                            }
                        }
                    }
                    if (blockEntries.length == 0 && environmentId != Integer.MIN_VALUE) {
                        for (int y = max - 1; y >= min; --y) {
                            execution.setEnvironment(cx, y, cz, environmentId);
                        }
                    }
                    if (lastBlock != null) {
                        while (layerY > min) {
                            final int currentBlock2 = execution.getBlock(cx, --layerY, cz);
                            if (currentBlock2 != 0) {
                                execution.setBlock(cx, layerY, cz, (byte)2, lastBlock);
                                execution.setFluid(cx, layerY, cz, (byte)2, lastBlock.fluidId());
                            }
                        }
                    }
                }
            }
        }
    }
}
