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

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

import com.hypixel.hytale.procedurallib.condition.ICoordinateCondition;
import com.hypixel.hytale.procedurallib.condition.DefaultCoordinateCondition;
import com.hypixel.hytale.server.worldgen.prefab.PrefabPasteUtil;
import com.hypixel.hytale.math.util.HashUtil;
import com.hypixel.hytale.server.core.prefab.PrefabRotation;
import com.hypixel.hytale.server.worldgen.loader.WorldGenPrefabSupplier;
import com.hypixel.hytale.server.worldgen.util.condition.BlockMaskCondition;
import com.hypixel.hytale.server.worldgen.cave.element.CavePrefab;
import com.hypixel.hytale.server.worldgen.cave.element.CaveNode;
import com.hypixel.hytale.server.worldgen.chunk.BlockPriorityModifier;
import com.hypixel.hytale.server.worldgen.cave.CaveBlockPriorityModifier;
import java.util.Random;
import com.hypixel.hytale.server.worldgen.cave.Cave;
import com.hypixel.hytale.server.worldgen.chunk.ZoneBiomeResult;
import com.hypixel.hytale.server.worldgen.chunk.ChunkGenerator;
import com.hypixel.hytale.math.util.MathUtil;
import com.hypixel.hytale.procedurallib.logic.point.IPointGenerator;
import com.hypixel.hytale.server.worldgen.cave.CaveType;
import com.hypixel.hytale.server.worldgen.zone.Zone;
import com.hypixel.hytale.math.util.ChunkUtil;
import javax.annotation.Nonnull;
import com.hypixel.hytale.server.worldgen.chunk.ChunkGeneratorExecution;

public class CavePopulator
{
    public static void populate(final int seed, @Nonnull final ChunkGeneratorExecution execution) {
        for (final Zone zone : execution.getChunkGenerator().getZonePatternProvider().getZones()) {
            if (zone.caveGenerator() != null) {
                for (final CaveType caveType : zone.caveGenerator().getCaveTypes()) {
                    final IPointGenerator cavePointGenerator = caveType.getEntryPointGenerator();
                    cavePointGenerator.collect(seed, ChunkUtil.minBlock(execution.getX()) - caveType.getMaximumSize(), ChunkUtil.minBlock(execution.getZ()) - caveType.getMaximumSize(), ChunkUtil.maxBlock(execution.getX()) + caveType.getMaximumSize(), ChunkUtil.maxBlock(execution.getZ()) + caveType.getMaximumSize(), (x, z) -> run(seed, x, z, execution, zone, caveType));
                }
            }
        }
    }
    
    private static void run(final int seed, final double dx, final double dz, @Nonnull final ChunkGeneratorExecution execution, final Zone zone, @Nonnull final CaveType caveType) {
        final ChunkGenerator chunkGenerator = execution.getChunkGenerator();
        final int x = MathUtil.floor(dx);
        final int z = MathUtil.floor(dz);
        final ZoneBiomeResult result = chunkGenerator.getZoneBiomeResultAt(seed, x, z);
        if (result.getZoneResult().getZone() == zone && caveType.isEntryThreshold(seed, x, z) && isMatchingHeightThreshold(seed, x, z, chunkGenerator, caveType)) {
            populate(seed, execution, execution.getChunkGenerator().getCave(caveType, seed, x, z));
        }
    }
    
    private static void populate(final int seed, @Nonnull final ChunkGeneratorExecution execution, @Nonnull final Cave cave) {
        final long chunkIndex = execution.getIndex();
        if (!cave.contains(chunkIndex)) {
            return;
        }
        final int chunkX = execution.getX();
        final int chunkZ = execution.getZ();
        final Random random = new Random();
        final int environment = cave.getCaveType().getEnvironment();
        execution.setPriorityModifier(CaveBlockPriorityModifier.INSTANCE);
        for (final CaveNode node : cave.getCaveNodes(chunkIndex)) {
            populateCaveNode(seed, execution, cave, node, random);
        }
        execution.setPriorityModifier(BlockPriorityModifier.NONE);
        for (final CaveNode node : cave.getCaveNodes(chunkIndex)) {
            for (final CavePrefab prefab : node.getCavePrefabs()) {
                if (prefab.getBounds().intersectsChunk(chunkX, chunkZ)) {
                    populatePrefab(seed, environment, execution, cave, node, prefab);
                }
            }
        }
    }
    
    private static void populateCaveNode(final int seed, @Nonnull final ChunkGeneratorExecution execution, @Nonnull final Cave cave, @Nonnull final CaveNode caveNode, @Nonnull final Random random) {
        random.setSeed(seed + caveNode.getSeedOffset());
        caveNode.getShape().populateChunk(seed, execution, cave, caveNode, random);
        if (execution.getChunkGenerator().getBenchmark().isEnabled()) {
            final int minX = caveNode.getBounds().getLowBoundX();
            final int minZ = caveNode.getBounds().getLowBoundZ();
            if (ChunkUtil.isInsideChunk(execution.getX(), execution.getZ(), minX, minZ)) {
                execution.getChunkGenerator().getBenchmark().registerCaveNode("Cave\t" + cave.getCaveType().getName() + "\t" + caveNode.getCaveNodeType().getName());
            }
        }
    }
    
    private static void populatePrefab(final int seed, final int environment, @Nonnull final ChunkGeneratorExecution execution, @Nonnull final Cave cave, @Nonnull final CaveNode node, @Nonnull final CavePrefab prefab) {
        generatePrefabAt(seed, prefab.getX(), prefab.getZ(), prefab.getY(), environment, execution, cave, node, prefab.getConfiguration(), prefab.getPrefab(), prefab.getRotation());
    }
    
    private static void generatePrefabAt(final int seed, final int x, final int z, final int y, final int environment, @Nonnull final ChunkGeneratorExecution execution, @Nonnull final Cave cave, @Nonnull final CaveNode node, final BlockMaskCondition configuration, @Nonnull final WorldGenPrefabSupplier supplier, final PrefabRotation rotation) {
        final int cx = x - ChunkUtil.minBlock(execution.getX());
        final int cz = z - ChunkUtil.minBlock(execution.getZ());
        final long externalSeed = HashUtil.rehash(x, z) * -99562191L;
        final boolean submerge = cave.getCaveType().isSubmerge();
        final PrefabPasteUtil.PrefabPasteBuffer buffer = ChunkGenerator.getResource().prefabBuffer;
        buffer.setSeed(seed, externalSeed);
        buffer.blockMask = configuration;
        buffer.execution = execution;
        buffer.environmentId = environment;
        buffer.priority = (byte)(submerge ? 39 : 7);
        if (execution.getChunkGenerator().getBenchmark().isEnabled() && ChunkUtil.isInsideChunkRelative(cx, cz)) {
            execution.getChunkGenerator().getBenchmark().registerPrefab("CavePrefab: " + cave.getCaveType().getName() + "\t" + node.getCaveNodeType().getName() + "\t" + supplier.getName());
        }
        PrefabPasteUtil.generate(buffer, rotation, supplier, x, y, z, cx, cz);
    }
    
    private static boolean isMatchingHeightThreshold(final int seed, final int x, final int z, @Nonnull final ChunkGenerator chunkGenerator, @Nonnull final CaveType caveType) {
        final ICoordinateCondition heightCondition = caveType.getHeightCondition();
        if (heightCondition == DefaultCoordinateCondition.DEFAULT_TRUE) {
            return true;
        }
        if (heightCondition == DefaultCoordinateCondition.DEFAULT_FALSE) {
            return false;
        }
        final int height = chunkGenerator.getHeight(seed, x, z);
        return heightCondition.eval(seed - 173220171, x, height, z);
    }
}
