/*
 * OneDBinner.java
 *
 * Created on July 18, 2002, 5:02 PM
 */

package hep.aida.ref.histogram.binner;

/**
 *
 * @author  The AIDA team at SLAC
 *
 */
public class BasicBinner2D implements Binner2D{
    
    private int[][] entries;
    private double[][] heights;
    private double[][] plusErrors;
    private double[][] meansX;
    private double[][] rmssX;
    private double[][] meansY;
    private double[][] rmssY;
    
    private int xBins;
    private int yBins;
    
    /**
     * Creates a new instance of OneDBinner.
     *
     */
    public BasicBinner2D(int xBins, int yBins) {
        if (xBins < 0) throw new IllegalArgumentException("Number of bins cannot be negative!!! "+xBins);
        if (yBins < 0) throw new IllegalArgumentException("Number of bins cannot be negative!!! "+yBins);
        setNumberOfBins(xBins, yBins);
    }
    
    public void fill( int xBin, int yBin, double x, double y, double weight) {
        //        if ( weight < 0 || weight > 1 ) throw new IllegalArgumentException("Wrong weight "+weight+" !! It has to be between 0 and 1");
        entries[xBin][yBin]++;
        heights[xBin][yBin]     += weight;
        plusErrors[xBin][yBin]  += weight*weight;
        meansX[xBin][yBin]      += x*weight;
        rmssX[xBin][yBin]       += x*x*weight;
        meansY[xBin][yBin]      += y*weight;
        rmssY[xBin][yBin]       += y*y*weight;
    }
    
    public void clear() {
        createArrays( xBins, yBins );
    }
    
    private void setNumberOfBins(int xBins, int yBins) {
        this.xBins = xBins;
        this.yBins = yBins;
        createArrays(xBins, yBins);
    }
    
    public int entries(int xBin, int yBin) {
        return entries[xBin][yBin];
    }
    
    public double height(int xBin, int yBin) {
        return heights[xBin][yBin];
    }
    
    public double plusError(int xBin, int yBin) {
        return Math.sqrt(plusErrors[xBin][yBin]);
    }
    
    public double minusError(int xBin, int yBin) {
        return plusError(xBin,yBin);
    }
    
    public double meanX(int xBin, int yBin) {
        double h = height(xBin,yBin);
        if ( h != 0 ) return meansX[xBin][yBin]/h;
        return 0;
    }
    
    public double meanY(int xBin, int yBin) {
        double h = height(xBin,yBin);
        if ( h != 0 ) return meansY[xBin][yBin]/h;
        return 0;
    }
    
    public double rmsX(int xBin, int yBin) {
        double h = height(xBin,yBin);
        double m = meanX(xBin,yBin);
        if ( h != 0 ) return Math.sqrt( Math.abs(rmssX[xBin][yBin]/h - m*m) );
        return 0;
    }
    
    public double rmsY(int xBin, int yBin) {
        double h = height(xBin,yBin);
        double m = meanY(xBin,yBin);
        if ( h != 0 ) return Math.sqrt( Math.abs(rmssY[xBin][yBin]/h - m*m) );
        return 0;
    }
    
    public void setBinContent(int xBin, int yBin, int entries, double height, double plusError, double minusError, double meanX, double rmsX, double meanY, double rmsY) {
        heights[xBin][yBin] = height;
        this.entries[xBin][yBin] = entries;
        plusErrors[xBin][yBin] = plusError*plusError;
        meansX[xBin][yBin] = 0;
        rmssX[xBin][yBin] = 0;
        meansY[xBin][yBin] = 0;
        rmssY[xBin][yBin] = 0;
        if ( height != 0 ) {
            meansX[xBin][yBin] = meanX*height;
            rmssX[xBin][yBin] = rmsX*rmsX*height + meanX*meanX*height;
            meansY[xBin][yBin] = meanY*height;
            rmssY[xBin][yBin] = rmsY*rmsY*height + meanY*meanY*height;
        }
    }
    
    public void scale( double scaleFactor ) {
        for ( int xBin = 0; xBin < xBins; xBin++ ) {
            for ( int yBin = 0; yBin < yBins; yBin++ ) {
                heights[xBin][yBin]     *= scaleFactor;
                plusErrors[xBin][yBin]  *= scaleFactor*scaleFactor;
                meansX[xBin][yBin]      *= scaleFactor;
                rmssX[xBin][yBin]       *= scaleFactor;
                meansY[xBin][yBin]      *= scaleFactor;
                rmssY[xBin][yBin]       *= scaleFactor;
            }
        }
    }
    
    private void createArrays(int xBins, int yBins) {
        entries = new int[xBins][yBins];
        heights = new double[xBins][yBins];
        plusErrors = new double[xBins][yBins];
        meansX = new double[xBins][yBins];
        rmssX = new double[xBins][yBins];
        meansY = new double[xBins][yBins];
        rmssY = new double[xBins][yBins];
    }
    
}
