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

package com.hypixel.hytale.builtin.hytalegenerator.props;

import org.checkerframework.checker.nullness.compatqual.NonNullDecl;
import com.hypixel.hytale.builtin.hytalegenerator.datastructures.voxelspace.ArrayVoxelSpace;
import java.util.Iterator;
import java.util.List;
import com.hypixel.hytale.builtin.hytalegenerator.threadindexer.WorkerIndexer;
import com.hypixel.hytale.builtin.hytalegenerator.datastructures.voxelspace.VoxelSpace;
import com.hypixel.hytale.builtin.hytalegenerator.bounds.SpaceSize;
import javax.annotation.Nonnull;
import com.hypixel.hytale.builtin.hytalegenerator.bounds.Bounds3i;
import com.hypixel.hytale.builtin.hytalegenerator.BlockMask;
import com.hypixel.hytale.builtin.hytalegenerator.conveyor.stagedconveyor.ContextDependency;
import com.hypixel.hytale.builtin.hytalegenerator.patterns.Pattern;
import com.hypixel.hytale.builtin.hytalegenerator.scanners.Scanner;
import com.hypixel.hytale.builtin.hytalegenerator.material.Material;
import com.hypixel.hytale.builtin.hytalegenerator.materialproviders.MaterialProvider;
import com.hypixel.hytale.builtin.hytalegenerator.density.Density;
import com.hypixel.hytale.math.vector.Vector3i;

public class DensityProp extends Prop
{
    private final Vector3i range;
    private final Density density;
    private final MaterialProvider<Material> materialProvider;
    private final Scanner scanner;
    private final Pattern pattern;
    private final ContextDependency contextDependency;
    private final BlockMask placementMask;
    private final Material defaultMaterial;
    private final Bounds3i readBounds_voxelGrid;
    private final Bounds3i writeBounds_voxelGrid;
    
    public DensityProp(@Nonnull final Vector3i range, @Nonnull final Density density, @Nonnull final MaterialProvider<Material> materialProvider, @Nonnull final Scanner scanner, @Nonnull final Pattern pattern, @Nonnull final BlockMask placementMask, @Nonnull final Material defaultMaterial) {
        this.range = range.clone();
        this.density = density;
        this.materialProvider = materialProvider;
        this.scanner = scanner;
        this.pattern = pattern;
        this.placementMask = placementMask;
        this.defaultMaterial = defaultMaterial;
        SpaceSize writeSpace = new SpaceSize(new Vector3i(-range.x - 1, 0, -range.z - 1), new Vector3i(range.x + 2, 0, range.z + 2));
        writeSpace = SpaceSize.stack(writeSpace, scanner.readSpaceWith(pattern));
        final Vector3i writeRange = writeSpace.getRange();
        final Vector3i readRange = scanner.readSpaceWith(pattern).getRange();
        this.contextDependency = new ContextDependency(readRange, writeRange);
        this.readBounds_voxelGrid = this.contextDependency.getReadBounds_voxelGrid();
        this.writeBounds_voxelGrid = this.contextDependency.getWriteBounds_voxelGrid();
    }
    
    @Override
    public PositionListScanResult scan(@Nonnull final Vector3i position, @Nonnull final VoxelSpace<Material> materialSpace, @Nonnull final WorkerIndexer.Id id) {
        final Scanner.Context scannerContext = new Scanner.Context(position, this.pattern, materialSpace, id);
        final List<Vector3i> validPositions = this.scanner.scan(scannerContext);
        return new PositionListScanResult(validPositions);
    }
    
    @Override
    public void place(@Nonnull final Context context) {
        final List<Vector3i> positions = PositionListScanResult.cast(context.scanResult).getPositions();
        if (positions == null) {
            return;
        }
        for (final Vector3i position : positions) {
            this.place(position, context.materialSpace, context.workerId);
        }
    }
    
    private void place(final Vector3i position, @Nonnull final VoxelSpace<Material> materialSpace, @Nonnull final WorkerIndexer.Id id) {
        final Vector3i min = position.clone().add(-this.range.x, -this.range.y, -this.range.z);
        final Vector3i max = position.clone().add(this.range.x, this.range.y, this.range.z);
        final Vector3i writeMin = Vector3i.max(min, new Vector3i(materialSpace.minX(), materialSpace.minY(), materialSpace.minZ()));
        final Vector3i writeMax = Vector3i.min(max, new Vector3i(materialSpace.maxX(), materialSpace.maxY(), materialSpace.maxZ()));
        final int bottom = min.y;
        final int top = max.y;
        final int height = top - bottom;
        final ArrayVoxelSpace<Boolean> densitySpace = new ArrayVoxelSpace<Boolean>(max.x - min.x + 1, max.y - min.y + 1, max.z - min.z + 1);
        densitySpace.setOrigin(-min.x, -min.y, -min.z);
        final Density.Context childContext = new Density.Context();
        childContext.densityAnchor = position.toVector3d();
        childContext.workerId = id;
        final Vector3i itPosition = new Vector3i(position);
        itPosition.x = min.x;
        while (itPosition.x <= max.x) {
            itPosition.z = min.z;
            while (itPosition.z <= max.z) {
                itPosition.y = min.y;
                while (itPosition.y <= max.y) {
                    if (densitySpace.isInsideSpace(itPosition.x, itPosition.y, itPosition.z)) {
                        childContext.position.x = itPosition.x;
                        childContext.position.y = itPosition.y;
                        childContext.position.z = itPosition.z;
                        final double densityValue = this.density.process(childContext);
                        densitySpace.set(densityValue > 0.0, itPosition.x, itPosition.y, itPosition.z);
                    }
                    final Vector3i vector3i = itPosition;
                    ++vector3i.y;
                }
                final Vector3i vector3i2 = itPosition;
                ++vector3i2.z;
            }
            final Vector3i vector3i3 = itPosition;
            ++vector3i3.x;
        }
        itPosition.x = min.x;
        while (itPosition.x <= max.x) {
            itPosition.z = min.z;
            while (itPosition.z <= max.z) {
                final int[] depthIntoCeiling = new int[height + 1];
                final int[] depthIntoFloor = new int[height + 1];
                final int[] spaceBelowCeiling = new int[height + 1];
                final int[] spaceAboveFloor = new int[height + 1];
                itPosition.y = top;
                while (itPosition.y >= bottom) {
                    final int i = itPosition.y - bottom;
                    final boolean density = densitySpace.getContent(itPosition.x, itPosition.y, itPosition.z);
                    if (itPosition.y == top) {
                        if (density) {
                            depthIntoFloor[i] = 1;
                        }
                        else {
                            depthIntoFloor[i] = 0;
                        }
                        spaceAboveFloor[i] = 1073741823;
                    }
                    else if (density) {
                        depthIntoFloor[i] = depthIntoFloor[i + 1] + 1;
                        spaceAboveFloor[i] = spaceAboveFloor[i + 1];
                    }
                    else {
                        depthIntoFloor[i] = 0;
                        if (densitySpace.getContent(itPosition.x, itPosition.y + 1, itPosition.z)) {
                            spaceAboveFloor[i] = 0;
                        }
                        else {
                            spaceAboveFloor[i] = spaceAboveFloor[i + 1] + 1;
                        }
                    }
                    final Vector3i vector3i4 = itPosition;
                    --vector3i4.y;
                }
                itPosition.y = bottom;
                while (itPosition.y < top) {
                    final int i = itPosition.y - bottom;
                    final boolean density = densitySpace.getContent(itPosition.x, itPosition.y, itPosition.z);
                    if (itPosition.y == bottom) {
                        if (density) {
                            depthIntoCeiling[i] = 1;
                        }
                        else {
                            depthIntoCeiling[i] = 0;
                        }
                        spaceBelowCeiling[i] = Integer.MAX_VALUE;
                    }
                    else if (density) {
                        depthIntoCeiling[i] = depthIntoCeiling[i - 1] + 1;
                        spaceBelowCeiling[i] = spaceBelowCeiling[i - 1];
                    }
                    else {
                        depthIntoCeiling[i] = 0;
                        if (densitySpace.getContent(itPosition.x, itPosition.y - 1, itPosition.z)) {
                            spaceBelowCeiling[i] = 0;
                        }
                        else {
                            spaceBelowCeiling[i] = spaceBelowCeiling[i - 1] + 1;
                        }
                    }
                    final Vector3i vector3i5 = itPosition;
                    ++vector3i5.y;
                }
                itPosition.y = top;
                while (itPosition.y >= bottom) {
                    if (itPosition.x >= writeMin.x && itPosition.y >= writeMin.y && itPosition.z >= writeMin.z && itPosition.x < writeMax.x && itPosition.y < writeMax.y) {
                        if (itPosition.z < writeMax.z) {
                            final int i = itPosition.y - bottom;
                            final MaterialProvider.Context materialContext = new MaterialProvider.Context(position, 0.0, depthIntoFloor[i], depthIntoCeiling[i], spaceAboveFloor[i], spaceBelowCeiling[i], id, (functionPosition, workerId) -> {
                                childContext.position = functionPosition.toVector3d();
                                return this.density.process(childContext);
                            }, childContext.distanceToBiomeEdge);
                            Material material = this.materialProvider.getVoxelTypeAt(materialContext);
                            if (material == null) {
                                material = this.defaultMaterial;
                            }
                            if (this.placementMask.canPlace(material)) {
                                final Material worldMaterial = materialSpace.getContent(itPosition.x, itPosition.y, itPosition.z);
                                final int worldMaterialHash = worldMaterial.hashMaterialIds();
                                if (this.placementMask.canReplace(material.hashCode(), worldMaterialHash)) {
                                    materialSpace.set(material, itPosition.x, itPosition.y, itPosition.z);
                                }
                            }
                        }
                    }
                    final Vector3i vector3i6 = itPosition;
                    --vector3i6.y;
                }
                final Vector3i vector3i7 = itPosition;
                ++vector3i7.z;
            }
            final Vector3i vector3i8 = itPosition;
            ++vector3i8.x;
        }
    }
    
    @Override
    public ContextDependency getContextDependency() {
        return this.contextDependency.clone();
    }
    
    @NonNullDecl
    @Override
    public Bounds3i getReadBounds_voxelGrid() {
        return this.readBounds_voxelGrid;
    }
    
    @Nonnull
    @Override
    public Bounds3i getWriteBounds_voxelGrid() {
        return this.writeBounds_voxelGrid;
    }
}
