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

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

import com.hypixel.hytale.builtin.hytalegenerator.framework.math.Interpolation;
import java.util.function.BiFunction;
import com.hypixel.hytale.builtin.hytalegenerator.ArrayUtil;
import java.util.ArrayList;
import java.util.Comparator;
import javax.annotation.Nonnull;
import java.util.List;
import com.hypixel.hytale.builtin.hytalegenerator.density.Density;

public class MultiMixDensity extends Density
{
    @Nonnull
    private final List<Segment> segments;
    private final double min;
    private final double max;
    private final Density firstDensity;
    private final Density lastDensity;
    private Density influenceDensity;
    
    public MultiMixDensity(@Nonnull final List<Key> keys, @Nonnull final Density influenceDensity) {
        if (keys.size() < 2) {
            throw new IllegalArgumentException("must have at least two keys");
        }
        keys.sort(Comparator.comparingDouble(element -> element.value));
        if (!isKeysUnique(keys)) {
            throw new IllegalArgumentException("Duplicate keys provided.");
        }
        this.segments = new ArrayList<Segment>(keys.size() - 1);
        for (int i = 1; i < keys.size(); ++i) {
            final Key key0 = keys.get(i - 1);
            final Key key2 = keys.get(i);
            this.segments.add(new Segment(key0, key2));
        }
        this.min = keys.getFirst().value;
        this.max = keys.getLast().value;
        this.firstDensity = keys.getFirst().density;
        this.lastDensity = keys.getLast().density;
        this.influenceDensity = influenceDensity;
    }
    
    @Override
    public double process(@Nonnull final Context context) {
        final double influence = this.influenceDensity.process(context);
        if (influence <= this.min) {
            return this.firstDensity.process(context);
        }
        if (influence >= this.max) {
            return this.lastDensity.process(context);
        }
        final int index = ArrayUtil.sortedSearch(this.segments, influence, Segment.GaugeSegmentComparator.INSTANCE);
        if (index != -1) {
            return this.segments.get(index).getValue(context, influence);
        }
        assert false : "should never get here";
        return 0.0;
    }
    
    @Override
    public void setInputs(@Nonnull final Density[] inputs) {
        if (inputs.length != 1) {
            throw new IllegalArgumentException("inputs.length != 1");
        }
        this.influenceDensity = inputs[0];
    }
    
    public static boolean isKeysUnique(@Nonnull final List<Key> keys) {
        for (int i = 1; i < keys.size(); ++i) {
            if (keys.get(i).value == keys.get(i - 1).value) {
                return false;
            }
        }
        return true;
    }
    
    record Key(double value, Density density) {}
    
    private static class Segment
    {
        @Nonnull
        private final Key key0;
        @Nonnull
        private final Key key1;
        private final double magnitude;
        
        public Segment(@Nonnull final Key key0, @Nonnull final Key key1) {
            assert key0.value < key1.value : "key0 should be smaller than key1";
            this.key0 = key0;
            this.key1 = key1;
            this.magnitude = key1.value - key0.value;
        }
        
        public boolean contains(final double gauge) {
            return gauge >= this.key0.value && gauge <= this.key1.value;
        }
        
        public double getValue(@Nonnull final Context context, final double gauge) {
            assert gauge >= this.key0.value && gauge <= this.key1.value : "mix outside range";
            final double THRESHOLD_INPUT_0 = 0.0;
            final double THRESHOLD_INPUT_2 = 1.0;
            final double weight = (gauge - this.key0.value) / this.magnitude;
            if (weight == 0.0) {
                return this.key0.density.process(context);
            }
            if (weight == 1.0) {
                return this.key1.density.process(context);
            }
            if (this.key0.density == this.key1.density) {
                return this.key0.density.process(context);
            }
            final double value0 = this.key0.density.process(context);
            final double value2 = this.key1.density.process(context);
            return Interpolation.linear(value0, value2, weight);
        }
        
        public static class GaugeSegmentComparator implements BiFunction<Double, Segment, Integer>
        {
            public static final GaugeSegmentComparator INSTANCE;
            
            @Nonnull
            @Override
            public Integer apply(final Double gauge, @Nonnull final Segment segment) {
                if (gauge < segment.key0.value) {
                    return -1;
                }
                if (gauge >= segment.key1.value) {
                    return 1;
                }
                return 0;
            }
            
            static {
                INSTANCE = new GaugeSegmentComparator();
            }
        }
    }
}
