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

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

import org.checkerframework.checker.nullness.compatqual.NonNullDecl;
import java.util.Random;
import com.hypixel.hytale.builtin.hytalegenerator.framework.math.Calculator;
import com.hypixel.hytale.math.util.FastRandom;
import com.hypixel.hytale.builtin.hytalegenerator.datastructures.voxelspace.WindowVoxelSpace;
import com.hypixel.hytale.builtin.hytalegenerator.newsystem.views.EntityContainer;
import java.util.Iterator;
import java.util.List;
import com.hypixel.hytale.builtin.hytalegenerator.threadindexer.WorkerIndexer;
import com.hypixel.hytale.builtin.hytalegenerator.material.Material;
import com.hypixel.hytale.builtin.hytalegenerator.datastructures.voxelspace.VoxelSpace;
import com.hypixel.hytale.math.vector.Vector3i;
import javax.annotation.Nonnull;
import com.hypixel.hytale.builtin.hytalegenerator.bounds.Bounds3i;
import com.hypixel.hytale.builtin.hytalegenerator.scanners.Scanner;
import com.hypixel.hytale.builtin.hytalegenerator.patterns.Pattern;
import com.hypixel.hytale.builtin.hytalegenerator.conveyor.stagedconveyor.ContextDependency;
import com.hypixel.hytale.builtin.hytalegenerator.datastructures.WeightedMap;
import com.hypixel.hytale.builtin.hytalegenerator.framework.math.SeedGenerator;
import it.unimi.dsi.fastutil.doubles.Double2DoubleFunction;

public class ClusterProp extends Prop
{
    private final Double2DoubleFunction weightCurve;
    private final SeedGenerator seedGenerator;
    private final WeightedMap<Prop> propWeightedMap;
    private final int range;
    private final ContextDependency contextDependency;
    private final Pattern pattern;
    private final Scanner scanner;
    private final Bounds3i readBounds_voxelGrid;
    private final Bounds3i writeBounds_voxelGrid;
    
    public ClusterProp(final int range, @Nonnull final Double2DoubleFunction weightCurve, final int seed, @Nonnull final WeightedMap<Prop> propWeightedMap, @Nonnull final Pattern pattern, @Nonnull final Scanner scanner) {
        if (range < 0) {
            throw new IllegalArgumentException("negative range");
        }
        this.range = range;
        this.seedGenerator = new SeedGenerator(seed);
        this.weightCurve = weightCurve;
        this.pattern = pattern;
        this.scanner = scanner;
        this.propWeightedMap = new WeightedMap<Prop>();
        propWeightedMap.forEach((prop, weight) -> {
            final ContextDependency contextDependency = prop.getContextDependency();
            final Vector3i readRange2 = contextDependency.getReadRange();
            final Vector3i writeRange2 = contextDependency.getWriteRange();
            if (readRange2.x > 0 || readRange2.z > 0 || writeRange2.x > 0 || writeRange2.z > 0) {
                return;
            }
            else {
                this.propWeightedMap.add(prop, propWeightedMap.get(prop));
                return;
            }
        });
        final Vector3i readRange = scanner.readSpaceWith(pattern).getRange();
        this.readBounds_voxelGrid = scanner.readSpaceWith(pattern).toBounds3i();
        final Vector3i writeRange = new Vector3i(range + readRange.x, 0, range + readRange.z);
        this.contextDependency = new ContextDependency(readRange, writeRange);
        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.entityBuffer, context.workerId, context.distanceFromBiomeEdge);
        }
    }
    
    private void place(@Nonnull final Vector3i position, @Nonnull final VoxelSpace<Material> materialSpace, @Nonnull final EntityContainer entityBuffer, @Nonnull final WorkerIndexer.Id id, final double distanceFromBiomeEdge) {
        final WindowVoxelSpace<Material> columnSpace = new WindowVoxelSpace<Material>(materialSpace);
        final FastRandom random = new FastRandom(this.seedGenerator.seedAt(position.x, position.z));
        for (int x = position.x - this.range; x < position.x + this.range; ++x) {
            for (int z = position.z - this.range; z < position.z + this.range; ++z) {
                final double distance = Calculator.distance(x, z, position.x, position.z);
                final double density = this.weightCurve.get(distance);
                if (random.nextDouble() <= density) {
                    final Prop pickedProp = this.propWeightedMap.pick(random);
                    if (materialSpace.isInsideSpace(x, materialSpace.minY(), z)) {
                        columnSpace.setWindow(x, materialSpace.minY(), z, x + 1, materialSpace.maxY(), z + 1);
                        final ScanResult propScanResult = pickedProp.scan(new Vector3i(x, position.y, z), columnSpace, id);
                        final Context childContext = new Context(propScanResult, columnSpace, entityBuffer, id, distanceFromBiomeEdge);
                        pickedProp.place(childContext);
                    }
                }
            }
        }
    }
    
    @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;
    }
}
