/*
 * Copyright (c) 2003, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */
package com.sun.corba.se.spi.monitoring;

import java.util.*;

/**
 * <p>
 *
 * @author Hemanth Puttaswamy
 * </p>
 * <p>
 * StatisticsAccumulator accumulates the samples provided by the user and
 * computes the value of minimum, maximum, sum and sample square sum. When
 * the StatisticMonitoredAttribute calls getValue(), it will compute all
 * the statistics for the collected samples (Which are Minimum, Maximum,
 * Average, StandardDeviation) and provides a nice printable record as a
 * String.
 *
 * Users can easily extend this class and provide the implementation of
 * toString() method to format the stats as desired. By default all the stats
 * are printed in a single line.
 * </p>
 */
public class StatisticsAccumulator {

  ///////////////////////////////////////
  // attributes


    // Users can extend this class to get access to current Max value
    protected double max = Double.MIN_VALUE;

    // Users can extend this class to get access to current Min value
    protected double min = Double.MAX_VALUE;

    private double sampleSum;

    private double sampleSquareSum;

    private long sampleCount;

    protected String unit;



  ///////////////////////////////////////
  // operations



/**
 * <p>
 * User will use this method to just register a sample with the
 * StatisticsAccumulator. This is the only method that User will use to
 * expose the statistics, internally the StatisticMonitoredAttribute will
 * collect the information when requested from the ASAdmin.
 * </p>
 * <p>
 *
 * </p>
 * <p>
 *
 * @param value a double value to make it more precise
 * </p>
 */
    public void sample(double value) {
        sampleCount++;
        if( value < min )  min = value;
        if( value > max) max = value;
        sampleSum += value;
        sampleSquareSum += (value * value);
    } // end sample



    /**
     *  Computes the Standard Statistic Results based on the samples collected
     *  so far and provides the complete value as a formatted String
     */
    public String getValue( ) {
        return toString();
    }

    /**
     *  Users can extend StatisticsAccumulator to provide the complete
     *  Stats in the format they prefer, if the default format doesn't suffice.
     */
    public String toString( ) {
        return "Minimum Value = " + min + " " + unit + " " +
            "Maximum Value = " + max + " " + unit + " " +
            "Average Value = " + computeAverage() + " " +  unit + " " +
            "Standard Deviation = " + computeStandardDeviation() + " " + unit +
            " " + "Samples Collected = " + sampleCount;
    }

    /**
     *  If users choose to custom format the stats.
     */
    protected double computeAverage( ) {
        return (sampleSum / sampleCount);
    }


    /**
     * We use a derived Standard Deviation formula to compute SD. This way
     * there is no need to hold on to all the samples provided.
     *
     * The method is protected to let users extend and format the results.
     */
    protected double computeStandardDeviation( ) {
        double sampleSumSquare = sampleSum * sampleSum;
        return Math.sqrt(
            (sampleSquareSum-((sampleSumSquare)/sampleCount))/(sampleCount-1));
    }

/**
 * <p>
 * Construct the Statistics Accumulator by providing the unit as a String.
 * The examples of units are &quot;Hours&quot;, &quot;Minutes&quot;,
 * &quot;Seconds&quot;, &quot;MilliSeconds&quot;, &quot;Micro Seconds&quot;
 * etc.,
 * </p>
 * <p>
 *
 * @return a StatisticsAccumulator with ...
 * </p>
 * <p>
 * @param unit a String representing the units for the samples collected
 * </p>
 */
    public StatisticsAccumulator( String unit ) {
        this.unit = unit;
        sampleCount = 0;
        sampleSum = 0;
        sampleSquareSum = 0;
    }


    /**
     *  Clears the samples and starts fresh on new samples.
     */
    void clearState( ) {
        min = Double.MAX_VALUE;
        max = Double.MIN_VALUE;
        sampleCount = 0;
        sampleSum = 0;
        sampleSquareSum = 0;
    }

    /**
     *  This is an internal API to test StatisticsAccumulator...
     */
    public void unitTestValidate( String expectedUnit, double expectedMin,
        double expectedMax, long expectedSampleCount, double expectedAverage,
        double expectedStandardDeviation )
    {
        if( !expectedUnit.equals( unit ) ){
            throw new RuntimeException(
                "Unit is not same as expected Unit" +
                "\nUnit = " + unit + "ExpectedUnit = " + expectedUnit );
        }
        if( min != expectedMin ) {
            throw new RuntimeException(
                "Minimum value is not same as expected minimum value" +
                "\nMin Value = " + min + "Expected Min Value = " + expectedMin);
        }
        if( max != expectedMax ) {
            throw new RuntimeException(
                "Maximum value is not same as expected maximum value" +
                "\nMax Value = " + max + "Expected Max Value = " + expectedMax);
        }
        if( sampleCount != expectedSampleCount ) {
            throw new RuntimeException(
                "Sample count is not same as expected Sample Count" +
                "\nSampleCount = " + sampleCount + "Expected Sample Count = " +
                expectedSampleCount);
        }
        if( computeAverage() != expectedAverage ) {
            throw new RuntimeException(
                "Average is not same as expected Average" +
                "\nAverage = " + computeAverage() + "Expected Average = " +
                expectedAverage);
        }
        // We are computing Standard Deviation from two different methods
        // for comparison. So, the values will not be the exact same to the last
        // few digits. So, we are taking the difference and making sure that
        // the difference is not greater than 1.
        double difference = Math.abs(
            computeStandardDeviation() - expectedStandardDeviation);
        if( difference > 1 ) {
            throw new RuntimeException(
                "Standard Deviation is not same as expected Std Deviation" +
                "\nStandard Dev = " + computeStandardDeviation() +
                "Expected Standard Dev = " + expectedStandardDeviation);
        }
    }


} // end StatisticsAccumulator
