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

package com.hypixel.hytale.server.core.prefab;

import com.hypixel.hytale.codec.validation.ValidationResults;
import com.hypixel.hytale.common.util.ArrayUtil;
import com.hypixel.hytale.codec.validation.LegacyValidator;
import com.hypixel.hytale.codec.validation.Validator;
import com.hypixel.hytale.codec.validation.Validators;
import com.hypixel.hytale.codec.KeyedCodec;
import com.hypixel.hytale.codec.builder.BuilderCodec;
import com.hypixel.hytale.codec.codecs.map.Object2DoubleMapCodec;
import it.unimi.dsi.fastutil.objects.Object2DoubleOpenHashMap;
import java.util.Set;
import it.unimi.dsi.fastutil.objects.ObjectIterator;
import it.unimi.dsi.fastutil.objects.Object2DoubleMaps;
import javax.annotation.Nullable;
import java.util.Random;
import java.util.function.Function;
import javax.annotation.Nonnull;
import com.hypixel.hytale.codec.Codec;
import it.unimi.dsi.fastutil.objects.Object2DoubleMap;
import java.util.function.Supplier;

public class PrefabWeights
{
    public static final Supplier<Object2DoubleMap<String>> MAP_SUPPLIER;
    public static final Codec<Object2DoubleMap<String>> MAP_CODEC;
    public static final Codec<PrefabWeights> CODEC;
    public static final PrefabWeights NONE;
    public static final double DEFAULT_WEIGHT = 1.0;
    public static final char DELIMITER_CHAR = ',';
    public static final char ASSIGNMENT_CHAR = '=';
    private double defaultWeight;
    private Object2DoubleMap<String> weightsLookup;
    protected double sum;
    protected double[] weights;
    protected volatile boolean initialized;
    
    public PrefabWeights() {
        this(PrefabWeights.MAP_SUPPLIER.get());
    }
    
    private PrefabWeights(final Object2DoubleMap<String> weights) {
        this.weightsLookup = weights;
        this.defaultWeight = 1.0;
    }
    
    public int size() {
        return this.weightsLookup.size();
    }
    
    @Nullable
    public <T> T get(@Nonnull final T[] elements, @Nonnull final Function<T, String> nameFunc, @Nonnull final Random random) {
        return this.get(elements, nameFunc, random.nextDouble());
    }
    
    @Nullable
    public <T> T get(@Nonnull final T[] elements, @Nonnull final Function<T, String> nameFunc, final double value) {
        if (value < 0.0) {
            return null;
        }
        this.initialize(elements, nameFunc);
        if (this.weights.length != elements.length) {
            return null;
        }
        final double weightedValue = Math.min(value, 0.99999) * this.sum;
        for (int i = 0; i < this.weights.length; ++i) {
            if (weightedValue <= this.weights[i]) {
                return elements[i];
            }
        }
        return null;
    }
    
    public double getWeight(final String prefab) {
        return this.weightsLookup.getOrDefault(prefab, this.defaultWeight);
    }
    
    public void setWeight(final String prefab, final double weight) {
        if (this == PrefabWeights.NONE) {
            return;
        }
        checkWeight(prefab, weight);
        this.weightsLookup.put(prefab, weight);
    }
    
    public void removeWeight(final String prefab) {
        if (this == PrefabWeights.NONE) {
            return;
        }
        this.weightsLookup.removeDouble(prefab);
    }
    
    public double getDefaultWeight() {
        return this.defaultWeight;
    }
    
    public void setDefaultWeight(final double defaultWeight) {
        if (this == PrefabWeights.NONE) {
            return;
        }
        this.defaultWeight = Math.max(0.0, defaultWeight);
    }
    
    @Nonnull
    public String getMappingString() {
        if (this.weightsLookup.isEmpty()) {
            return "";
        }
        final StringBuilder sb = new StringBuilder();
        for (final Object2DoubleMap.Entry<String> entry : Object2DoubleMaps.fastIterable(this.weightsLookup)) {
            if (!sb.isEmpty()) {
                sb.append(',').append(' ');
            }
            sb.append(entry.getKey()).append('=').append(entry.getDoubleValue());
        }
        return sb.toString();
    }
    
    @Nonnull
    @Override
    public String toString() {
        return "PrefabWeights{default=" + this.defaultWeight + ", weights=" + this.getMappingString();
    }
    
    private <T> void initialize(@Nonnull final T[] elements, @Nonnull final Function<T, String> nameFunc) {
        if (this.initialized) {
            return;
        }
        synchronized (this) {
            if (this.initialized) {
                return;
            }
            double sum = 0.0;
            final double[] weights = new double[elements.length];
            for (int i = 0; i < elements.length; ++i) {
                final String name = nameFunc.apply(elements[i]);
                sum += this.getWeight(name);
                weights[i] = sum;
            }
            this.sum = sum;
            this.weights = weights;
            this.initialized = true;
        }
    }
    
    @Nonnull
    public static PrefabWeights parse(@Nonnull final String mappingString) {
        Object2DoubleMap<String> map = null;
        int endPoint;
        for (int startPoint = 0; startPoint < mappingString.length(); startPoint = endPoint, ++startPoint) {
            endPoint = mappingString.indexOf(44, startPoint);
            if (endPoint == -1) {
                endPoint = mappingString.length();
            }
            final int equalsPoint = mappingString.indexOf(61, startPoint);
            if (equalsPoint <= startPoint) {
                break;
            }
            final String name = mappingString.substring(startPoint, equalsPoint).trim();
            final String value = mappingString.substring(equalsPoint + 1, endPoint).trim();
            final double weight = Double.parseDouble(value);
            if (map == null) {
                map = PrefabWeights.MAP_SUPPLIER.get();
            }
            map.put(name, weight);
        }
        if (map == null) {
            return PrefabWeights.NONE;
        }
        return new PrefabWeights(map);
    }
    
    public Set<Object2DoubleMap.Entry<String>> entrySet() {
        return this.weightsLookup.object2DoubleEntrySet();
    }
    
    private static void checkWeight(final String prefab, final double weight) {
        if (weight < 0.0) {
            throw new IllegalArgumentException(String.format("Negative weight %.5f assigned to prefab %s", weight, prefab));
        }
    }
    
    static {
        MAP_SUPPLIER = Object2DoubleOpenHashMap::new;
        MAP_CODEC = new Object2DoubleMapCodec<String>(Codec.STRING, PrefabWeights.MAP_SUPPLIER, false);
        CODEC = BuilderCodec.builder(PrefabWeights.class, PrefabWeights::new).append(new KeyedCodec<Double>("Default", Codec.DOUBLE), (weights, def) -> weights.defaultWeight = def, weights -> weights.defaultWeight).documentation("The default weight to use for entries that are not specifically mapped to a weight value.").addValidator((Validator<? super Double>)Validators.greaterThanOrEqual(0.0)).add().append(new KeyedCodec("Weights", PrefabWeights.MAP_CODEC), (weights, map) -> weights.weightsLookup = map, weights -> weights.weightsLookup).documentation("The mapping of prefab names to weight values.").addValidator(new WeightMapValidator()).add().build();
        NONE = new PrefabWeights() {
            {
                this.sum = 0.0;
                this.weights = ArrayUtil.EMPTY_DOUBLE_ARRAY;
                this.initialized = true;
            }
        };
    }
    
    private static class WeightMapValidator implements LegacyValidator<Object2DoubleMap<String>>
    {
        @Override
        public void accept(@Nonnull final Object2DoubleMap<String> stringObject2DoubleMap, final ValidationResults results) {
            for (final Object2DoubleMap.Entry<String> entry : Object2DoubleMaps.fastIterable(stringObject2DoubleMap)) {
                PrefabWeights.checkWeight(entry.getKey(), entry.getDoubleValue());
            }
        }
    }
}
