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

package com.hypixel.hytale.builtin.hytalegenerator.density.nodes;

import java.util.function.Consumer;
import com.hypixel.hytale.builtin.hytalegenerator.VectorUtil;
import java.util.ArrayList;
import javax.annotation.Nonnull;
import com.hypixel.hytale.math.vector.Vector3d;
import it.unimi.dsi.fastutil.doubles.Double2DoubleFunction;
import com.hypixel.hytale.builtin.hytalegenerator.positionproviders.PositionProvider;
import javax.annotation.Nullable;
import com.hypixel.hytale.builtin.hytalegenerator.density.Density;

public class PositionsTwistDensity extends Density
{
    @Nullable
    private Density input;
    @Nullable
    private PositionProvider positions;
    private Double2DoubleFunction twistCurve;
    private Vector3d twistAxis;
    private double maxDistance;
    private boolean distanceNormalized;
    private boolean zeroPositionsY;
    
    public PositionsTwistDensity(@Nullable final Density input, @Nullable final PositionProvider positions, @Nonnull final Double2DoubleFunction twistCurve, @Nonnull Vector3d twistAxis, final double maxDistance, final boolean distanceNormalized, final boolean zeroPositionsY) {
        if (maxDistance < 0.0) {
            throw new IllegalArgumentException();
        }
        if (twistAxis.length() < 1.0E-9) {
            twistAxis = new Vector3d(0.0, 1.0, 0.0);
        }
        this.input = input;
        this.positions = positions;
        this.twistCurve = twistCurve;
        this.twistAxis = twistAxis;
        this.maxDistance = maxDistance;
        this.distanceNormalized = distanceNormalized;
        this.zeroPositionsY = zeroPositionsY;
    }
    
    @Override
    public double process(@Nonnull final Context context) {
        if (this.input == null) {
            return 0.0;
        }
        if (this.positions == null) {
            return this.input.process(context);
        }
        final Vector3d min = new Vector3d(context.position.x - this.maxDistance, context.position.y - this.maxDistance, context.position.z - this.maxDistance);
        final Vector3d max = new Vector3d(context.position.x + this.maxDistance, context.position.y + this.maxDistance, context.position.z + this.maxDistance);
        final Vector3d samplePoint = context.position.clone();
        final Vector3d queryPosition = context.position.clone();
        if (this.zeroPositionsY) {
            queryPosition.y = 0.0;
            min.y = -1.0;
            max.y = 1.0;
        }
        final ArrayList<Vector3d> warpVectors = new ArrayList<Vector3d>(10);
        final ArrayList<Double> warpDistances = new ArrayList<Double>(10);
        final Consumer<Vector3d> consumer = p -> {
            final double distance2 = p.distanceTo(queryPosition);
            if (distance2 > this.maxDistance) {
                return;
            }
            else {
                final double normalizedDistance = distance2 / this.maxDistance;
                final Vector3d warpVector3 = samplePoint.clone();
                double twistAngle;
                if (this.distanceNormalized) {
                    twistAngle = this.twistCurve.applyAsDouble(normalizedDistance);
                }
                else {
                    twistAngle = this.twistCurve.applyAsDouble(distance2);
                }
                final double twistAngle2 = twistAngle / 180.0;
                final double twistAngle3 = twistAngle2 * 3.141592653589793;
                warpVector3.subtract(p);
                VectorUtil.rotateAroundAxis(warpVector3, this.twistAxis, twistAngle3);
                warpVector3.add(p);
                warpVector3.subtract(samplePoint);
                warpVectors.add(warpVector3);
                if (this.distanceNormalized) {
                    warpDistances.add(normalizedDistance);
                }
                else {
                    warpDistances.add(distance2);
                }
                return;
            }
        };
        final PositionProvider.Context positionsContext = new PositionProvider.Context(min, max, consumer, null, context.workerId);
        this.positions.positionsIn(positionsContext);
        if (warpVectors.isEmpty()) {
            return this.input.process(context);
        }
        if (warpVectors.size() == 1) {
            final Vector3d warpVector = warpVectors.getFirst();
            samplePoint.add(warpVector);
            final Context childContext = new Context(context);
            childContext.position = samplePoint;
            return this.input.process(childContext);
        }
        final int possiblePointsSize = warpVectors.size();
        final ArrayList<Double> weights = new ArrayList<Double>(warpDistances.size());
        double totalWeight = 0.0;
        for (int i = 0; i < possiblePointsSize; ++i) {
            final double distance = warpDistances.get(i);
            final double weight = 1.0 - distance;
            weights.add(weight);
            totalWeight += weight;
        }
        for (int i = 0; i < possiblePointsSize; ++i) {
            final double weight2 = weights.get(i) / totalWeight;
            final Vector3d warpVector2 = warpVectors.get(i);
            warpVector2.scale(weight2);
            samplePoint.add(warpVector2);
        }
        final Context childContext2 = new Context(context);
        childContext2.position = samplePoint;
        return this.input.process(childContext2);
    }
    
    @Override
    public void setInputs(@Nonnull final Density[] inputs) {
        if (inputs.length == 0) {
            this.input = null;
        }
        this.input = inputs[0];
    }
}
