/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to you under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.apache.jmeter.reporters;

import java.text.DecimalFormat;

import org.apache.jmeter.samplers.SampleResult;

/**
 * <p>
 * Running sample data container. Just instantiate a new instance of this
 * class, and then call {@link #addSample(SampleResult)} a few times, and pull
 * the stats out with whatever methods you prefer.
 * </p>
 * <p>
 * Please note that this class is not thread-safe.
 * The calling class is responsible for ensuring thread safety if required.
 * The caller needs to synchronize access in order to ensure that variables are consistent.
 * </p>
 * @since 2.13
 */
class SummariserRunningSample {

    private final DecimalFormat errorFormatter = new DecimalFormat("#0.00%"); // $NON-NLS-1$

    private long counter;

    private long runningSum;

    private long max;

    private long min;

    private long errorCount;

    private long startTime;

    private long endTime;

    private final String label;

    /**
     * @param label the label of this component
     */
    public SummariserRunningSample(String label) {
        this.label = label;
        init();
    }

    /**
     * Copy constructor
     * @param src the instance to copy
     */
    public SummariserRunningSample(SummariserRunningSample src) {
        label = src.label;
        counter = src.counter;
        errorCount = src.errorCount;
        startTime = src.startTime;
        endTime = src.endTime;
        max = src.max;
        min = src.min;
        runningSum = src.runningSum;
    }

    private void init() { // WARNING: called from ctor so must not be overridden (i.e. must be private or final)
        counter = 0L;
        runningSum = 0L;
        max = Long.MIN_VALUE;
        min = Long.MAX_VALUE;
        errorCount = 0L;
        startTime = System.currentTimeMillis();
        endTime = startTime;
    }

    /**
     * Clear at every interval
     */
    public void clear() {
        init();
    }

    /**
     * Used for delta
     * @param rs {@link SummariserRunningSample}
     */
    public void addSample(SummariserRunningSample rs) {
        counter += rs.counter;
        errorCount += rs.errorCount;
        runningSum += rs.runningSum;
        if (max < rs.max) {
            max = rs.max;
        }
        if (min > rs.min) {
            min = rs.min;
        }
        // We want end time to be current time so sample rates reflect real time
        endTime = System.currentTimeMillis();
    }

    /**
     * Used for each SampleResult
     * @param res {@link SampleResult}
     */
    public void addSample(SampleResult res) {
        counter += res.getSampleCount();
        errorCount += res.getErrorCount();
        long aTimeInMillis = res.getTime();
        runningSum += aTimeInMillis;
        if (aTimeInMillis > max) {
            max = aTimeInMillis;
        }
        if (aTimeInMillis < min) {
            min = aTimeInMillis;
        }
        // We want end time to be current time so sample rates reflect real time
        endTime = System.currentTimeMillis();
    }

    /**
     * Returns the number of samples that have been recorded by this instance of
     * the {@link SummariserRunningSample} class.
     *
     * @return the number of samples that have been recorded by this instance of
     *         the {@link SummariserRunningSample} class.
     */
    public long getNumSamples() {
        return counter;
    }

    /**
     * Get the elapsed time for the samples
     *
     * @return how long the samples took
     */
    public long getElapsed() {
        if (counter == 0) {
            return 0;// No samples collected ...
        }
        return endTime - startTime;
    }

    /**
     * Returns the throughput associated to this sampler in requests per second.
     *
     * @return throughput associated to this sampler
     */
    public double getRate() {
        if (counter == 0) {
            return 0.0;// No samples collected ...
        }

        long howLongRunning = endTime - startTime;

        if (howLongRunning == 0) {
            return Double.MAX_VALUE;
        }

        return (double) counter / howLongRunning * 1000.0;
    }

    /**
     * Returns the average time in milliseconds that samples ran in.
     *
     * @return the average time in milliseconds that samples ran in.
     */
    public long getAverage() {
        if (counter == 0) {
            return 0;
        }
        return runningSum / counter;
    }

    /**
     * @return errorCount
     */
    public long getErrorCount() {
        return errorCount;
    }

    /**
     * Returns a String which represents the percentage of sample errors that
     * have occurred. ("0.00%" through "100.00%")
     *
     * @return a String which represents the percentage of sample errors that
     *         have occurred.
     */
    public String getErrorPercentageString() {
        return errorFormatter.format(getErrorPercentage());
    }

    /**
     * Returns the raw double value of the percentage of samples with errors
     * that were recorded. (Between 0.0 and 1.0) If you want a nicer return
     * format, see {@link #getErrorPercentageString()}.
     *
     * @return the raw double value of the percentage of samples with errors
     *         that were recorded. Returns 0.0 if there are no samples
     */
    public double getErrorPercentage() {
        if (counter == 0) {
            return 0.0;
        }
        double rval = (double) errorCount / (double) counter;
        return rval;
    }

    /**
     * Returns the time in milliseconds of the slowest sample.
     *
     * @return the time in milliseconds of the slowest sample.
     */
    public long getMax() {
        return max;
    }

    /**
     * Returns the time in milliseconds of the quickest sample.
     *
     * @return the time in milliseconds of the quickest sample.
     */
    public long getMin() {
        return min;
    }

    /**
     * Set end time
     */
    public void setEndTime() {
        endTime = System.currentTimeMillis();
    }

}
