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

package com.hypixel.hytale.common.benchmark;

import java.util.Formatter;
import javax.annotation.Nonnull;
import com.hypixel.hytale.math.util.MathUtil;

public class TimeDistributionRecorder extends TimeRecorder
{
    protected int minLogRange;
    protected int maxLogRange;
    protected int logSteps;
    protected long[] valueBins;
    
    public TimeDistributionRecorder(final double maxSecs, final double minSecs, final int logSteps) {
        if (maxSecs < 1.0E-6 || maxSecs > 0.1) {
            throw new IllegalArgumentException("Max seconds must be between 100 milli secs and 1 micro sec");
        }
        if (minSecs < 1.0E-6 || minSecs > 0.1) {
            throw new IllegalArgumentException("Min seconds must be between 100 milli secs and 1 micro sec");
        }
        if (maxSecs <= minSecs) {
            throw new IllegalArgumentException("Max seconds must be larger than min seconds");
        }
        if (logSteps < 2 || logSteps > 10) {
            throw new IllegalArgumentException("LogSteps must be between 2 and 10");
        }
        this.maxLogRange = MathUtil.ceil(Math.log10(maxSecs));
        this.minLogRange = MathUtil.floor(Math.log10(minSecs));
        this.logSteps = MathUtil.clamp(logSteps, 2, 10);
        this.valueBins = new long[(this.maxLogRange - this.minLogRange) * this.logSteps + 2];
        for (int i = 0, length = this.valueBins.length; i < length; ++i) {
            this.valueBins[i] = 0L;
        }
    }
    
    public TimeDistributionRecorder(final double maxSecs, final double minSecs) {
        this(maxSecs, minSecs, 5);
    }
    
    public TimeDistributionRecorder() {
        this(0.1, 1.0E-5);
    }
    
    @Override
    public void reset() {
        super.reset();
        for (int i = 0, length = this.valueBins.length; i < length; ++i) {
            this.valueBins[i] = 0L;
        }
    }
    
    @Override
    public double recordNanos(final long nanos) {
        final double secs = super.recordNanos(nanos);
        final long[] valueBins = this.valueBins;
        final int timeToIndex = this.timeToIndex(secs);
        ++valueBins[timeToIndex];
        return secs;
    }
    
    public int timeToIndex(final double secs) {
        final double logSecs = Math.log10(secs);
        final double indexDbl = (this.maxLogRange - logSecs) * this.logSteps;
        int index = MathUtil.ceil(indexDbl);
        if (index < 0) {
            index = 0;
        }
        else if (index >= this.valueBins.length) {
            index = this.valueBins.length - 1;
        }
        return index;
    }
    
    public double indexToTime(int index) {
        if (index < 0) {
            index = 0;
        }
        else if (index >= this.valueBins.length) {
            index = this.valueBins.length - 1;
        }
        if (index == this.valueBins.length - 1) {
            return 0.0;
        }
        final double exp = this.maxLogRange - index / (double)this.logSteps;
        return Math.pow(10.0, exp);
    }
    
    public int size() {
        return this.valueBins.length;
    }
    
    public long get(final int index) {
        return this.valueBins[index];
    }
    
    @Nonnull
    @Override
    public String toString() {
        final StringBuilder stringBuilder = new StringBuilder(12 * this.size());
        stringBuilder.append("Cnt=").append(this.getCount());
        for (int i = 0; i < this.size(); ++i) {
            stringBuilder.append(' ').append(TimeRecorder.formatTime(this.indexToTime(i))).append('=').append(this.get(i));
        }
        return super.toString() + " " + String.valueOf(stringBuilder);
    }
    
    @Override
    public void formatHeader(@Nonnull final Formatter formatter, @Nonnull final String columnFormatHeader) {
        super.formatHeader(formatter, columnFormatHeader);
        for (int i = 0; i < this.size(); ++i) {
            formatter.format(columnFormatHeader, TimeRecorder.formatTime(this.indexToTime(i)));
        }
    }
    
    @Override
    public void formatValues(@Nonnull final Formatter formatter, @Nonnull final String columnFormatValue) {
        this.formatValues(formatter, 0L, columnFormatValue);
    }
    
    public void formatValues(@Nonnull final Formatter formatter, final long normalValue) {
        this.formatValues(formatter, normalValue, "|%6.6s");
    }
    
    public void formatValues(@Nonnull final Formatter formatter, final long normalValue, @Nonnull final String columnFormatValue) {
        super.formatValues(formatter, columnFormatValue);
        final double norm = (this.count > 0L && normalValue > 1L) ? (normalValue / (double)this.count) : 1.0;
        for (int i = 0; i < this.size(); ++i) {
            formatter.format(columnFormatValue, (int)Math.round(this.get(i) * norm));
        }
    }
}
