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

package com.hypixel.hytale.procedurallib.json;

import java.util.Arrays;
import javax.annotation.Nullable;
import com.google.gson.JsonArray;
import com.hypixel.hytale.procedurallib.NoiseFunction;
import com.hypixel.hytale.procedurallib.random.CoordinateRotator;
import com.hypixel.hytale.procedurallib.supplier.IDoubleRange;
import com.hypixel.hytale.procedurallib.property.SingleNoiseProperty;
import com.hypixel.hytale.procedurallib.property.FractalNoiseProperty;
import com.hypixel.hytale.procedurallib.property.RotateNoiseProperty;
import com.hypixel.hytale.procedurallib.property.OffsetNoiseProperty;
import com.hypixel.hytale.procedurallib.property.InvertNoiseProperty;
import com.hypixel.hytale.procedurallib.property.NormalizeNoiseProperty;
import com.hypixel.hytale.procedurallib.property.DistortedNoiseProperty;
import com.hypixel.hytale.procedurallib.property.MultiplyNoiseProperty;
import com.hypixel.hytale.procedurallib.property.NoiseFormulaProperty;
import com.hypixel.hytale.procedurallib.property.ScaleNoiseProperty;
import com.hypixel.hytale.procedurallib.property.SumNoiseProperty;
import com.hypixel.hytale.procedurallib.property.MinNoiseProperty;
import com.hypixel.hytale.procedurallib.property.MaxNoiseProperty;
import com.hypixel.hytale.procedurallib.property.NoisePropertyType;
import com.google.gson.JsonElement;
import java.nio.file.Path;
import javax.annotation.Nonnull;
import com.hypixel.hytale.procedurallib.property.NoiseProperty;

public class NoisePropertyJsonLoader<K extends SeedResource> extends JsonLoader<K, NoiseProperty>
{
    public NoisePropertyJsonLoader(@Nonnull final SeedString<K> seed, final Path dataFolder, final JsonElement json) {
        super(seed.append(".NoiseProperty"), dataFolder, json);
    }
    
    @Nonnull
    @Override
    public NoiseProperty load() {
        NoisePropertyType type = null;
        NoiseProperty noiseProperty = null;
        if (this.has("Type")) {
            type = NoisePropertyType.valueOf(this.get("Type").getAsString());
            switch (type) {
                case MAX: {
                    if (!this.has("Noise")) {
                        throw new IllegalStateException("Could not find noise map data. Keyword: Noise");
                    }
                    final NoiseProperty[] noiseProperties = this.loadNoiseProperties(this.get("Noise"));
                    noiseProperty = new MaxNoiseProperty(noiseProperties);
                    break;
                }
                case MIN: {
                    if (!this.has("Noise")) {
                        throw new IllegalStateException("Could not find noise map data. Keyword: Noise");
                    }
                    final NoiseProperty[] noiseProperties = this.loadNoiseProperties(this.get("Noise"));
                    noiseProperty = new MinNoiseProperty(noiseProperties);
                    break;
                }
                case SUM: {
                    if (!this.has("Noise")) {
                        throw new IllegalStateException("Could not find noise map data. Keyword: Noise");
                    }
                    if (!this.has("Factors")) {
                        throw new IllegalStateException("Could not find factors for sum composed noise map. Keyword: Factors");
                    }
                    final NoiseProperty[] noiseProperties = this.loadNoiseProperties(this.get("Noise"));
                    final double[] factors = this.loadDoubleArray(this.get("Factors"), noiseProperties.length);
                    final SumNoiseProperty.Entry[] entries = new SumNoiseProperty.Entry[noiseProperties.length];
                    for (int i = 0; i < entries.length; ++i) {
                        entries[i] = new SumNoiseProperty.Entry(noiseProperties[i], factors[i]);
                    }
                    noiseProperty = new SumNoiseProperty(entries);
                    break;
                }
                case SCALE: {
                    if (!this.has("Noise")) {
                        throw new IllegalStateException("Could not find noise map data. Keyword: Noise");
                    }
                    if (!this.has("Scale")) {
                        throw new IllegalStateException("Could not find scale data for scaled noise map. Keyword: Scale");
                    }
                    final NoiseProperty noise = new NoisePropertyJsonLoader(this.seed, this.dataFolder, this.get("Noise")).load();
                    final double scale = this.get("Scale").getAsDouble();
                    noiseProperty = new ScaleNoiseProperty(noise, scale);
                    break;
                }
                case FORMULA: {
                    if (!this.has("Noise")) {
                        throw new IllegalStateException("Could not find noise map data. Keyword: Noise");
                    }
                    if (!this.has("Formula")) {
                        throw new IllegalStateException("Could not find formula type for noise map. Keyword: Formula");
                    }
                    final NoiseProperty noise = new NoisePropertyJsonLoader(this.seed, this.dataFolder, this.get("Noise")).load();
                    final NoiseFormulaProperty.NoiseFormula noiseFormula = NoiseFormulaProperty.NoiseFormula.valueOf(this.get("Formula").getAsString());
                    noiseProperty = new NoiseFormulaProperty(noise, noiseFormula.getFormula());
                    break;
                }
                case MULTIPLY: {
                    if (!this.has("Noise")) {
                        throw new IllegalStateException("Could not find noise map data. Keyword: Noise");
                    }
                    final NoiseProperty[] noiseProperties = this.loadNoiseProperties(this.get("Noise"));
                    noiseProperty = new MultiplyNoiseProperty(noiseProperties);
                    break;
                }
                case DISTORTED: {
                    if (!this.has("Noise")) {
                        throw new IllegalStateException("Could not find noise map data. Keyword: Noise");
                    }
                    if (!this.has("Randomizer")) {
                        throw new IllegalStateException("Could not find randomizer for distorted noise map. Keyword: Randomizer");
                    }
                    final NoiseProperty noise = new NoisePropertyJsonLoader(this.seed, this.dataFolder, this.get("Noise")).load();
                    noiseProperty = new DistortedNoiseProperty(noise, new CoordinateRandomizerJsonLoader(this.seed, this.dataFolder, this.get("Randomizer")).load());
                    break;
                }
                case NORMALIZE: {
                    if (!this.has("Noise")) {
                        throw new IllegalStateException("Could not find noise map data. Keyword: Noise");
                    }
                    if (!this.has("Range")) {
                        throw new IllegalStateException("Could not find range data for normalized noise map. Keyword: Range");
                    }
                    final NoiseProperty noise = new NoisePropertyJsonLoader(this.seed, this.dataFolder, this.get("Noise")).load();
                    final IDoubleRange range = new DoubleRangeJsonLoader(this.seed, this.dataFolder, this.get("Range")).load();
                    noiseProperty = new NormalizeNoiseProperty(noise, range.getValue(0.0), range.getValue(1.0) - range.getValue(0.0));
                    break;
                }
                case INVERT: {
                    if (!this.has("Noise")) {
                        throw new IllegalStateException("Could not find noise map data. Keyword: Noise");
                    }
                    final NoiseProperty noise = new NoisePropertyJsonLoader(this.seed, this.dataFolder, this.get("Noise")).load();
                    noiseProperty = new InvertNoiseProperty(noise);
                    break;
                }
                case OFFSET: {
                    if (!this.has("Noise")) {
                        throw new IllegalStateException("Could not find noise map data. Keyword: Noise");
                    }
                    final NoiseProperty noise = new NoisePropertyJsonLoader(this.seed, this.dataFolder, this.get("Noise")).load();
                    final double offset = this.has("Offset") ? this.get("Offset").getAsDouble() : 0.0;
                    final double offsetX = this.has("OffsetX") ? this.get("OffsetX").getAsDouble() : offset;
                    final double offsetY = this.has("OffsetY") ? this.get("OffsetY").getAsDouble() : offset;
                    final double offsetZ = this.has("OffsetZ") ? this.get("OffsetZ").getAsDouble() : offset;
                    noiseProperty = new OffsetNoiseProperty(noise, offsetX, offsetY, offsetZ);
                    break;
                }
                case ROTATE: {
                    if (!this.has("Noise")) {
                        throw new IllegalStateException("Could not find noise map data. Keyword: Noise");
                    }
                    if (!this.has("Rotate")) {
                        throw new IllegalStateException("Could not find noise map data. Keyword: Noise");
                    }
                    final NoiseProperty noise = new NoisePropertyJsonLoader(this.seed, this.dataFolder, this.get("Noise")).load();
                    final CoordinateRotator rotation = new CoordinateRotatorJsonLoader(this.seed, this.dataFolder, this.get("Rotate")).load();
                    noiseProperty = new RotateNoiseProperty(noise, rotation);
                    break;
                }
                case GRADIENT: {
                    if (!this.has("Noise")) {
                        throw new IllegalStateException("Could not find noise map data. Keyword: Noise");
                    }
                    final NoiseProperty noise = new NoisePropertyJsonLoader(this.seed, this.dataFolder, this.get("Noise")).load();
                    noiseProperty = new GradientNoisePropertyJsonLoader(this.seed, this.dataFolder, this.json, noise).load();
                    break;
                }
                case CURVE: {
                    noiseProperty = new CurveNoisePropertyJsonLoader(this.seed, this.dataFolder, this.json, null).load();
                    break;
                }
                case BLEND: {
                    noiseProperty = new BlendNoisePropertyJsonLoader(this.seed, this.dataFolder, this.json).load();
                    break;
                }
                default: {
                    throw new Error(String.format("Could not find instructions for noise property type: %s", type));
                }
            }
        }
        else {
            final NoiseFunction noiseFunction = this.newNoiseFunctionJsonLoader(this.seed, this.dataFolder, this.json).load();
            if (this.has("Octaves")) {
                final FractalNoiseProperty.FractalMode fractalMode = this.has("FractalMode") ? FractalNoiseProperty.FractalMode.valueOf(this.get("FractalMode").getAsString()) : Constants.DEFAULT_FRACTAL_MODE;
                final int octaves = this.get("Octaves").getAsInt();
                final double lacunarity = this.has("Lacunarity") ? this.get("Lacunarity").getAsDouble() : 2.0;
                final double persistence = this.has("Persistence") ? this.get("Persistence").getAsDouble() : 0.5;
                noiseProperty = new FractalNoiseProperty(this.loadSeed(), noiseFunction, fractalMode.getFunction(), octaves, lacunarity, persistence);
            }
            else {
                noiseProperty = new SingleNoiseProperty(this.loadSeed(), noiseFunction);
            }
        }
        if (type != NoisePropertyType.FORMULA && this.has("Formula")) {
            final NoiseFormulaProperty.NoiseFormula noiseFormula2 = NoiseFormulaProperty.NoiseFormula.valueOf(this.get("Formula").getAsString());
            noiseProperty = new NoiseFormulaProperty(noiseProperty, noiseFormula2.getFormula());
        }
        if (type != NoisePropertyType.CURVE && this.has("Curve")) {
            noiseProperty = new CurveNoisePropertyJsonLoader(this.seed, this.dataFolder, this.get("Curve"), noiseProperty).load();
        }
        if (type != NoisePropertyType.SCALE && this.has("Scale")) {
            final double scale2 = this.get("Scale").getAsDouble();
            noiseProperty = new ScaleNoiseProperty(noiseProperty, scale2);
        }
        if (type != NoisePropertyType.NORMALIZE && this.has("Normalize") && type != NoisePropertyType.GRADIENT) {
            final IDoubleRange range2 = new DoubleRangeJsonLoader(this.seed, this.dataFolder, this.get("Normalize")).load();
            noiseProperty = new NormalizeNoiseProperty(noiseProperty, range2.getValue(0.0), range2.getValue(1.0) - range2.getValue(0.0));
        }
        if (type != NoisePropertyType.OFFSET && (this.has("Offset") || this.has("OffsetX") || this.has("OffsetY") || this.has("OffsetZ"))) {
            final double offset2 = this.has("Offset") ? this.get("Offset").getAsDouble() : 0.0;
            final double offsetX2 = this.has("OffsetX") ? this.get("OffsetX").getAsDouble() : offset2;
            final double offsetY2 = this.has("OffsetY") ? this.get("OffsetY").getAsDouble() : offset2;
            final double offsetZ2 = this.has("OffsetZ") ? this.get("OffsetZ").getAsDouble() : offset2;
            noiseProperty = new OffsetNoiseProperty(noiseProperty, offsetX2, offsetY2, offsetZ2);
        }
        if (type != NoisePropertyType.ROTATE && (this.has("Pitch") || this.has("Yaw"))) {
            final CoordinateRotator rotation2 = new CoordinateRotatorJsonLoader(this.seed, this.dataFolder, this.json).load();
            noiseProperty = new RotateNoiseProperty(noiseProperty, rotation2);
        }
        if (type != NoisePropertyType.GRADIENT && this.has("Gradient")) {
            noiseProperty = new GradientNoisePropertyJsonLoader(this.seed, this.dataFolder, this.get("Gradient"), noiseProperty).load();
        }
        return noiseProperty;
    }
    
    protected int loadSeed() {
        int seedVal = this.seed.hashCode();
        if (this.has("Seed")) {
            final SeedString<?> overwritten = this.seed.appendToOriginal(this.get("Seed").getAsString());
            seedVal = overwritten.hashCode();
            this.seed.get().reportSeeds(seedVal, this.seed.original, this.seed.seed, overwritten.seed);
        }
        else {
            this.seed.get().reportSeeds(seedVal, this.seed.original, this.seed.seed, null);
        }
        return seedVal;
    }
    
    @Nonnull
    protected NoiseProperty[] loadNoiseProperties(@Nonnull final JsonElement element) {
        if (element.isJsonArray()) {
            final JsonArray array = element.getAsJsonArray();
            final NoiseProperty[] noiseProperties = new NoiseProperty[array.size()];
            for (int i = 0; i < noiseProperties.length; ++i) {
                noiseProperties[i] = new NoisePropertyJsonLoader(this.seed.append(String.format("-#%s", i)), this.dataFolder, array.get(i)).load();
            }
            return noiseProperties;
        }
        return new NoiseProperty[0];
    }
    
    protected double[] loadDoubleArray(@Nullable final JsonElement element, final int size) {
        final double[] values = new double[size];
        if (element == null || element.isJsonNull()) {
            Arrays.fill(values, 1.0 / size);
        }
        else if (element.isJsonArray()) {
            final JsonArray array = element.getAsJsonArray();
            for (int i = 0; i < size; ++i) {
                values[i] = array.get(i).getAsDouble();
            }
        }
        return values;
    }
    
    @Nonnull
    protected NoiseFunctionJsonLoader newNoiseFunctionJsonLoader(@Nonnull final SeedString<K> seed, final Path dataFolder, final JsonElement json) {
        return new NoiseFunctionJsonLoader((SeedString<K>)seed, dataFolder, json);
    }
    
    public interface Constants
    {
        public static final String KEY_SEED = "Seed";
        public static final String KEY_SUM_FACTORS = "Factors";
        public static final String KEY_NORMALIZE_RANGE = "Range";
        public static final String KEY_DISTORTED_RANDOMIZER = "Randomizer";
        public static final String KEY_TYPE = "Type";
        public static final String KEY_NOISE = "Noise";
        public static final String KEY_FRACTAL_MODE = "FractalMode";
        public static final String KEY_OCTAVES = "Octaves";
        public static final String KEY_LACUNARITY = "Lacunarity";
        public static final String KEY_PERSISTENCE = "Persistence";
        public static final String KEY_FORMULA = "Formula";
        public static final String KEY_CURVE = "Curve";
        public static final String KEY_SCALE = "Scale";
        public static final String KEY_NORMALIZE = "Normalize";
        public static final String KEY_OFFSET = "Offset";
        public static final String KEY_OFFSET_X = "OffsetX";
        public static final String KEY_OFFSET_Y = "OffsetY";
        public static final String KEY_OFFSET_Z = "OffsetZ";
        public static final String KEY_GRADIENT = "Gradient";
        public static final String ERROR_NO_NOISE = "Could not find noise map data. Keyword: Noise";
        public static final String ERROR_SUM_NO_FACTORS = "Could not find factors for sum composed noise map. Keyword: Factors";
        public static final String ERROR_NO_FORMULA = "Could not find formula type for noise map. Keyword: Formula";
        public static final String ERROR_NO_SCALE = "Could not find scale data for scaled noise map. Keyword: Scale";
        public static final String ERROR_DISTORTED_RANDOMIZER = "Could not find randomizer for distorted noise map. Keyword: Randomizer";
        public static final String ERROR_NORMALIZE_NO_RANGE = "Could not find range data for normalized noise map. Keyword: Range";
        public static final String ERROR_UNKOWN_TYPE = "Could not find instructions for noise property type: %s";
        public static final FractalNoiseProperty.FractalMode DEFAULT_FRACTAL_MODE = FractalNoiseProperty.FractalMode.FBM;
        public static final double DEFAULT_LACUNARITY = 2.0;
        public static final double DEFAULT_PERSISTENCE = 0.5;
    }
}
