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

package com.hypixel.hytale.server.worldgen.cave.shape;

import com.hypixel.hytale.server.worldgen.cave.CaveNodeType;
import javax.annotation.Nullable;
import java.util.List;
import com.hypixel.hytale.server.worldgen.prefab.PrefabPasteUtil;
import com.hypixel.hytale.server.worldgen.chunk.ChunkGenerator;
import com.hypixel.hytale.math.util.HashUtil;
import com.hypixel.hytale.math.util.ChunkUtil;
import java.util.Random;
import com.hypixel.hytale.server.worldgen.cave.element.CaveNode;
import com.hypixel.hytale.server.worldgen.cave.Cave;
import com.hypixel.hytale.server.worldgen.chunk.ChunkGeneratorExecution;
import com.hypixel.hytale.server.worldgen.util.bounds.IChunkBounds;
import com.hypixel.hytale.server.core.prefab.selection.buffer.impl.IPrefabBuffer;
import com.hypixel.hytale.math.util.MathUtil;
import com.hypixel.hytale.server.worldgen.util.condition.BlockMaskCondition;
import com.hypixel.hytale.server.core.prefab.PrefabRotation;
import com.hypixel.hytale.server.worldgen.loader.WorldGenPrefabSupplier;
import javax.annotation.Nonnull;
import com.hypixel.hytale.math.vector.Vector3d;
import com.hypixel.hytale.server.worldgen.cave.CaveType;
import com.hypixel.hytale.server.worldgen.util.bounds.IWorldBounds;

public class PrefabCaveNodeShape implements CaveNodeShape, IWorldBounds
{
    private final CaveType caveType;
    @Nonnull
    private final Vector3d o;
    private final Vector3d e;
    @Nonnull
    private final WorldGenPrefabSupplier prefabSupplier;
    @Nonnull
    private final PrefabRotation rotation;
    private final BlockMaskCondition configuration;
    private final int lowBoundX;
    private final int lowBoundY;
    private final int lowBoundZ;
    private final int highBoundX;
    private final int highBoundY;
    private final int highBoundZ;
    
    public PrefabCaveNodeShape(final CaveType caveType, @Nonnull final Vector3d o, final Vector3d e, @Nonnull final WorldGenPrefabSupplier prefabSupplier, @Nonnull final PrefabRotation rotation, final BlockMaskCondition configuration) {
        this.caveType = caveType;
        this.o = o;
        this.e = e;
        this.prefabSupplier = prefabSupplier;
        this.rotation = rotation;
        this.configuration = configuration;
        final IPrefabBuffer prefab = prefabSupplier.get();
        final IChunkBounds bounds = prefabSupplier.getBounds(prefab);
        this.lowBoundX = MathUtil.floor(o.x + bounds.getLowBoundX(rotation));
        this.lowBoundY = MathUtil.floor(o.y + prefab.getMinY());
        this.lowBoundZ = MathUtil.floor(o.z + bounds.getLowBoundZ(rotation));
        this.highBoundX = MathUtil.ceil(o.x + bounds.getHighBoundX(rotation));
        this.highBoundY = MathUtil.ceil(o.y + prefab.getMaxY());
        this.highBoundZ = MathUtil.ceil(o.z + bounds.getHighBoundZ(rotation));
    }
    
    public CaveType getCaveType() {
        return this.caveType;
    }
    
    @Nonnull
    public PrefabRotation getPrefabRotation() {
        return this.rotation;
    }
    
    @Nonnull
    public Vector3d getO() {
        return this.o;
    }
    
    @Nonnull
    @Override
    public Vector3d getStart() {
        return this.o.clone();
    }
    
    @Nonnull
    @Override
    public Vector3d getEnd() {
        return this.o.clone().add(this.e);
    }
    
    @Nonnull
    @Override
    public Vector3d getAnchor(@Nonnull final Vector3d vector, final double tx, final double ty, final double tz) {
        final Vector3d anchor = CaveNodeShapeUtils.getBoxAnchor(vector, this, tx, ty, tz);
        final double x = MathUtil.floor(anchor.x) + 0.5;
        final double y = MathUtil.floor(anchor.y) + 0.5;
        final double z = MathUtil.floor(anchor.z) + 0.5;
        return anchor.assign(x, y, z);
    }
    
    @Nonnull
    @Override
    public IWorldBounds getBounds() {
        return this;
    }
    
    @Override
    public int getLowBoundX() {
        return this.lowBoundX;
    }
    
    @Override
    public int getLowBoundZ() {
        return this.lowBoundZ;
    }
    
    @Override
    public int getHighBoundX() {
        return this.highBoundX;
    }
    
    @Override
    public int getHighBoundZ() {
        return this.highBoundZ;
    }
    
    @Override
    public int getLowBoundY() {
        return this.lowBoundY;
    }
    
    @Override
    public int getHighBoundY() {
        return this.highBoundY;
    }
    
    @Override
    public boolean shouldReplace(final int seed, final double x, final double z, final int y) {
        return false;
    }
    
    @Override
    public double getFloorPosition(final int seed, double x, double z) {
        x -= this.o.x;
        z -= this.o.z;
        return this.prefabSupplier.get().getMinYAt(this.rotation, (int)x, (int)z);
    }
    
    @Override
    public double getCeilingPosition(final int seed, double x, double z) {
        x -= this.o.x;
        z -= this.o.z;
        return this.prefabSupplier.get().getMaxYAt(this.rotation, (int)x, (int)z);
    }
    
    @Override
    public void populateChunk(final int seed, @Nonnull final ChunkGeneratorExecution execution, @Nonnull final Cave cave, @Nonnull final CaveNode node, final Random random) {
        final int x = MathUtil.floor(this.o.x);
        final int y = MathUtil.floor(this.o.y);
        final int z = MathUtil.floor(this.o.z);
        final int cx = x - ChunkUtil.minBlock(execution.getX());
        final int cz = z - ChunkUtil.minBlock(execution.getZ());
        final long externalSeed = HashUtil.hash(Double.doubleToLongBits(this.o.x), Double.doubleToLongBits(this.o.z)) * 1406794441L;
        final PrefabPasteUtil.PrefabPasteBuffer buffer = ChunkGenerator.getResource().prefabBuffer;
        buffer.setSeed(seed, externalSeed);
        buffer.execution = execution;
        buffer.blockMask = this.configuration;
        buffer.environmentId = (node.getCaveNodeType().hasEnvironment() ? node.getCaveNodeType().getEnvironment() : this.caveType.getEnvironment());
        buffer.priority = 8;
        if (execution.getChunkGenerator().getBenchmark().isEnabled() && ChunkUtil.isInsideChunkRelative(cx, cz)) {
            execution.getChunkGenerator().getBenchmark().registerPrefab("CaveNode: " + cave.getCaveType().getName() + "\t" + node.getCaveNodeType().getName() + "\t" + this.prefabSupplier.getName());
        }
        PrefabPasteUtil.generate(buffer, this.rotation, this.prefabSupplier, x, y, z, cx, cz);
    }
    
    @Nonnull
    @Override
    public String toString() {
        return "PrefabCaveNodeShape{caveType=" + String.valueOf(this.caveType) + ", o=" + String.valueOf(this.o) + ", e=" + String.valueOf(this.e) + ", prefabSupplier=\"prefab\", rotation=" + String.valueOf(this.rotation) + ", configuration=" + String.valueOf(this.configuration) + ", lowBoundX=" + this.lowBoundX + ", lowBoundY=" + this.lowBoundY + ", lowBoundZ=" + this.lowBoundZ + ", highBoundX=" + this.highBoundX + ", highBoundY=" + this.highBoundY + ", highBoundZ=" + this.highBoundZ;
    }
    
    public static class PrefabCaveNodeShapeGenerator implements CaveNodeShapeEnum.CaveNodeShapeGenerator
    {
        private final List<WorldGenPrefabSupplier> prefabs;
        private final BlockMaskCondition configuration;
        
        public PrefabCaveNodeShapeGenerator(final List<WorldGenPrefabSupplier> prefabs, final BlockMaskCondition configuration) {
            this.prefabs = prefabs;
            this.configuration = configuration;
        }
        
        @Nonnull
        @Override
        public CaveNodeShape generateCaveNodeShape(@Nonnull final Random random, final CaveType caveType, @Nullable final CaveNode parentNode, @Nonnull final CaveNodeType.CaveNodeChildEntry childEntry, @Nonnull final Vector3d origin, final float yaw, final float pitch) {
            final WorldGenPrefabSupplier prefab = this.prefabs.get(random.nextInt(this.prefabs.size()));
            if (parentNode == null) {
                final PrefabRotation rotation = PrefabRotation.VALUES[random.nextInt(PrefabRotation.VALUES.length)];
                return new PrefabCaveNodeShape(caveType, origin, Vector3d.ZERO, prefab, rotation, this.configuration);
            }
            final Vector3d offset = childEntry.getOffset().clone();
            PrefabRotation rotation2 = childEntry.getRotation(random);
            final CaveNodeShape shape = parentNode.getShape();
            if (shape instanceof final PrefabCaveNodeShape parentShape) {
                final PrefabRotation parentRotation = parentShape.getPrefabRotation();
                parentRotation.rotate(offset);
                rotation2 = rotation2.add(parentRotation);
            }
            origin.add(offset);
            return new PrefabCaveNodeShape(caveType, origin, Vector3d.ZERO, prefab, rotation2, this.configuration);
        }
    }
}
