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

package com.hypixel.hytale.builtin.hytalegenerator.fields.noise;

import java.util.Random;
import javax.annotation.Nonnull;

public class SimplexNoiseField extends NoiseField
{
    private final long seed;
    @Nonnull
    private final double[] offsetX;
    @Nonnull
    private final double[] offsetY;
    @Nonnull
    private final double[] offsetZ;
    @Nonnull
    private final double[] offsetW;
    private final int numberOfOctaves;
    @Nonnull
    private final double[] octaveFrequency;
    @Nonnull
    private final double[] octaveAmplitude;
    private final double normalizer;
    
    public SimplexNoiseField(final long seed, final double octaveAmplitudeMultiplier, final double octaveFrequencyMultiplier, final int numberOfOctaves) {
        if (numberOfOctaves <= 0) {
            throw new IllegalArgumentException("octaves can't be smaller than 1");
        }
        this.seed = seed;
        this.numberOfOctaves = numberOfOctaves;
        final Random rand = new Random(seed);
        this.offsetX = new double[numberOfOctaves];
        this.offsetY = new double[numberOfOctaves];
        this.offsetZ = new double[numberOfOctaves];
        this.offsetW = new double[numberOfOctaves];
        for (int i = 0; i < numberOfOctaves; ++i) {
            this.offsetX[i] = rand.nextDouble() * 256.0;
            this.offsetY[i] = rand.nextDouble() * 256.0;
            this.offsetZ[i] = rand.nextDouble() * 256.0;
            this.offsetW[i] = rand.nextDouble() * 256.0;
        }
        this.octaveAmplitude = new double[numberOfOctaves];
        this.octaveFrequency = new double[numberOfOctaves];
        double frequency = 1.0;
        double amplitude = 1.0;
        double maxAmplitude = 0.0;
        for (int j = 0; j < numberOfOctaves; ++j) {
            this.octaveAmplitude[j] = amplitude;
            this.octaveFrequency[j] = frequency;
            maxAmplitude += amplitude;
            amplitude *= octaveAmplitudeMultiplier;
            frequency *= octaveFrequencyMultiplier;
        }
        this.normalizer = 1.0 / maxAmplitude;
    }
    
    @Nonnull
    public static Builder builder() {
        return new Builder();
    }
    
    @Override
    public double valueAt(double x, double y, double z, double w) {
        x /= this.scaleX;
        y /= this.scaleY;
        z /= this.scaleZ;
        w /= this.scaleW;
        double octaveX = 0.0;
        double octaveY = 0.0;
        double octaveZ = 0.0;
        double octaveW = 0.0;
        double value = 0.0;
        for (int i = 0; i < this.numberOfOctaves; ++i) {
            octaveX = x + this.offsetX[i];
            octaveY = y + this.offsetY[i];
            octaveZ = z + this.offsetZ[i];
            octaveW = w + this.offsetW[i];
            value += Simplex.noise(octaveX * this.octaveFrequency[i], octaveY * this.octaveFrequency[i], octaveZ * this.octaveFrequency[i], octaveW * this.octaveFrequency[i]) * this.octaveAmplitude[i];
        }
        value *= this.normalizer;
        return value;
    }
    
    @Override
    public double valueAt(double x, double y, double z) {
        x /= this.scaleX;
        y /= this.scaleY;
        z /= this.scaleZ;
        double octaveX = 0.0;
        double octaveY = 0.0;
        double octaveZ = 0.0;
        double value = 0.0;
        for (int i = 0; i < this.numberOfOctaves; ++i) {
            octaveX = x + this.offsetX[i];
            octaveY = y + this.offsetY[i];
            octaveZ = z + this.offsetZ[i];
            value += Simplex.noise(octaveX * this.octaveFrequency[i], octaveY * this.octaveFrequency[i], octaveZ * this.octaveFrequency[i]) * this.octaveAmplitude[i];
        }
        value *= this.normalizer;
        return value;
    }
    
    @Override
    public double valueAt(double x, double y) {
        x /= this.scaleX;
        y /= this.scaleY;
        double octaveX = 0.0;
        double octaveY = 0.0;
        double value = 0.0;
        for (int i = 0; i < this.numberOfOctaves; ++i) {
            octaveX = x + this.offsetX[i];
            octaveY = y + this.offsetY[i];
            value += Simplex.noise(octaveX * this.octaveFrequency[i], octaveY * this.octaveFrequency[i]) * this.octaveAmplitude[i];
        }
        value *= this.normalizer;
        return value;
    }
    
    @Override
    public double valueAt(double x) {
        x /= this.scaleX;
        double octaveX = 0.0;
        double value = 0.0;
        for (int i = 0; i < this.numberOfOctaves; ++i) {
            octaveX = x + this.offsetX[i];
            value += Simplex.noise(octaveX * this.octaveFrequency[i], 0.0) * this.octaveAmplitude[i];
        }
        value *= this.normalizer;
        return value;
    }
    
    public long getSeed() {
        return this.seed;
    }
    
    public static class Builder
    {
        private long seed;
        private double octaveAmplitudeMultiplier;
        private double octaveFrequencyMultiplier;
        private int numberOfOctaves;
        private double scaleX;
        private double scaleY;
        private double scaleZ;
        private double scaleW;
        
        private Builder() {
            this.seed = 1L;
            this.octaveAmplitudeMultiplier = 0.5;
            this.octaveFrequencyMultiplier = 2.0;
            this.numberOfOctaves = 4;
        }
        
        @Nonnull
        public SimplexNoiseField build() {
            final SimplexNoiseField g = new SimplexNoiseField(this.seed, this.octaveAmplitudeMultiplier, this.octaveFrequencyMultiplier, this.numberOfOctaves);
            g.setScale(this.scaleX, this.scaleY, this.scaleZ, this.scaleW);
            return g;
        }
        
        @Nonnull
        public Builder withScale(final double s) {
            this.scaleX = s;
            this.scaleY = s;
            this.scaleZ = s;
            this.scaleW = s;
            return this;
        }
        
        @Nonnull
        public Builder withScale(final double x, final double y, final double z, final double w) {
            this.scaleX = x;
            this.scaleY = y;
            this.scaleZ = z;
            this.scaleW = w;
            return this;
        }
        
        @Nonnull
        public Builder withNumberOfOctaves(final int n) {
            if (n <= 0) {
                throw new IllegalArgumentException("invalid number");
            }
            this.numberOfOctaves = n;
            return this;
        }
        
        @Nonnull
        public Builder withFrequencyMultiplier(final double f) {
            this.octaveFrequencyMultiplier = f;
            return this;
        }
        
        @Nonnull
        public Builder withAmplitudeMultiplier(final double a) {
            this.octaveAmplitudeMultiplier = a;
            return this;
        }
        
        @Nonnull
        public Builder withSeed(final long s) {
            this.seed = s;
            return this;
        }
    }
}
