package simulate;

import java.awt.Component;
import java.awt.Graphics;

/**
 * Superclass of all meter types.
 * A meter is responsible for measuring and averaging properties during a simulation.
 * Normally a meter is placed in a phase, and it does is measurements exclusively on that phase, but
 * there are exceptions.
 * Meters do not carry any ability to display their measurements.  Display of meter data is accomplished
 * by connecting a meter to an appropriate Display object.
 * All meter measurements are performed in simulation units.  Conversion to other units is completed
 * by the Display object that is presenting the meter data.<br>
 * The abstract meter is subclassed into a Meter and a MeterFunction.  An extension of the Meter subclass 
 * measures a scalar property (such as the energy), while an extension of the MeterFunction subclass
 * measures a vector or function property (such as the radial distribution function).
 * A meter updates its averages after receiving some number of interval events from an integrator.
 * All of the manipulations involving the calculation of averages and error bars are handled by
 * the Accumulator inner class of MeterAbstract.  Each meter contains one (or more, for a MeterFunction)
 * instance of an accumulator.
 *
 * @author David Kofke
 * @see Meter
 * @see MeterFunction
 * @see MeterMultiFunction
 * @see MeterAbstract.Accumulator
 */
public abstract class MeterAbstract extends Component implements Integrator.IntervalListener, Simulation.Element
{
    /**
     * Number of integration interval events received before another call to updateSums
     */
    protected int updateInterval;
    /**
     * Counter that keeps track of the number of interval events received since last call to updateSums
     */
    protected int iieCount;
    /**
     * The phase in which this meter is performing its measurements
     */
    protected Phase phase;
    /**
     * A string describing the property measured by the meter
     */
    protected String label;
    /**
     * Flag specifying whether the meter responds to integrator events
     * If false, the meter does not perform regular measurements or keep averages
     * In this situation the meter is probably measuring a property for use by some other object
     * Default is <code>true</code> for a Meter, but <code>false</code> for a MeterFunction.
     */
    protected boolean active;

	public MeterAbstract() {
	    setUpdateInterval(1);
	    label = "Property";
	    Simulation.register(this);
	}
	
	/**
	 * Method to set reset (discard accumulated values for) all averages and other sums
	 */
	public abstract void reset();
	/**
	 * Method called upon receiving updateInterval interval-events from the integrator
	 */
	public abstract void updateSums();

    /**
     * Method to render an image of the meter to the screen.  
     * Not commonly used, and default is to perform no action
     */
    public void draw(Graphics g, int[] origin, double scale) {} 
    /**
     * Sets the phase in which the meter is performing its measurements
     * May override in subclasses to include set up of iterators
     */
	public void setPhase(Phase p) {phase = p;}
	
	/**
	 * Accessor method for the meter's label
	 */
	public String getLabel() {return label;}
	/**
	 * Accessor method for the meter's label
	 */
	public void setLabel(String s) {label = s;}

    /**
     * Accessor method for the updateInterval
     * @see #updateInterval
     */
    public final int getUpdateInterval() {return updateInterval;}
    /**
     * Accessor method for the updateInterval
     * Sets to given value and resets count of interval events
     * @see #updateInterval
     */
    public final void setUpdateInterval(int i) {
        if(i > 0) {
            updateInterval = i;
            iieCount = updateInterval;
        }
    }
    
    /**
     * Sets whether the meter responds to integrator events
     * If false, the meter does not perform regular measurements or keep averages
     */
    public void setActive(boolean b) {active = b;}
    /**
     * Returns status of active flag
     * @see setActive
     */
    public final boolean isActive() {return active;}
    
    /**
     * Integrator.IntervalListenter interface method
     * Counts number of events received and calls updateSums after receiving event updateInterval times
     */
    public void intervalAction(Integrator.IntervalEvent evt) {
        if(!active) return;
	    if(--iieCount == 0) {
	        iieCount = updateInterval;
	        updateSums();
	    }
    }
    
    /**
     * Class for accumulation of averages and error statistics
     * One or more instances of this class is present in every meter.
     * Presently error value is not computed well (assumes independent measurements)
     * Future development project: include block averaging, add a plot display to show error as a function of block size
     */
    public final static class Accumulator {
        private double sum, sumSquare, mostRecent;
        private int count;
        
        public Accumulator() {
            reset();
        }
        
        /**
         * Add the given value to the sum and sum of squares
         */
        public void add(double value) {
            mostRecent = value;
            if(Double.isNaN(value)) return;
	        sum += value;
	        sumSquare += value*value;
	        count++;
        }
        
        /**
         * Compute and return the average from the present value of the accumulation sum
         */
	    public double average() {
	        return (count>0) ? sum/(double)count : Double.NaN;
	    }
    	
    	/**
    	 * Computer and return the 67% confidence limits of the average
    	 *   Needs more development
    	 */
	    public double error() {    //temporary---needs to be rewritten to do block averaging
	        double avg = average();
	        return (count>1) ? Math.sqrt((sumSquare/(double)count - avg*avg)/(double)(count-1)) : Double.NaN;
	    }
	    
	    /**
	     * Returns the value last passed to the add method
	     */
	    public double mostRecent() {return mostRecent;}
    	
    	/**
    	 * Resets all sums to zero
    	 */
	    public void reset() {
	        count = 0;
	        sum = 0;
	        sumSquare = 0;
	    }
	}
}	 
