package hep.aida.ref;

import hep.aida.IAxis;
import hep.aida.IHistogram;
import hep.aida.IHistogram2D;
import hep.aida.IHistogram3D;
/**
Abstract base class extracting and implementing most of the redundancy of the interface.

@author Wolfgang Hoschek, Tony Johnson, and others.
@version 1.0, 23/03/2000
*/
abstract class AbstractHistogram3D extends Histogram implements IHistogram3D
{
	protected IAxis xAxis, yAxis, zAxis;
	AbstractHistogram3D(String title)
	{
		super(title);
	}
	public int allEntries()
	{
		int n = 0;
		for (int i=xAxis.bins(); --i >= -2;)
			for (int j=yAxis.bins(); --j >= -2;)
				for (int k=zAxis.bins(); --k >= -2;)
		{
			n += binEntries(i,j,k);	 
		}
		return n;
	}
	public int dimensions()
	{
		return 3;
	}
	public int entries()
	{
		int n = 0;
		for (int i=0; i<xAxis.bins(); i++)
			for (int j=0; j<yAxis.bins(); j++)
				for (int k=0;k<zAxis.bins(); k++)
		{
			n += binEntries(i,j,k);	
		}
		return n;
	}
	public int extraEntries()
	{
		return allEntries() - entries();
	}
	public void fill(double x, double y, double z)
	{
		fill(x,y,z,1);
	}
	/**
	 * The precise meaning of the arguments to the public slice
	 * methods is somewhat ambiguous, so we define this internal
	 * slice method and clearly specify its arguments.
	 * <p>
	 * <b>Note 0</b>indexX1 and indexX2 use our INTERNAL bin numbering scheme
	 * <b>Note 1</b>The slice is done between indexX1 and indexX2 INCLUSIVE
	 * <b>Note 2</b>indexX1 and indexX2 may include the use of under and over flow bins
	 * <b>Note 3</b>There is no note 3 (yet)
	 */
	protected abstract IHistogram2D internalSliceXY(String title, int indexZ1, int indexZ2);
	/**
	 * The precise meaning of the arguments to the public slice
	 * methods is somewhat ambiguous, so we define this internal
	 * slice method and clearly specify its arguments.
	 * <p>
	 * <b>Note 0</b>indexY1 and indexY2 use our INTERNAL bin numbering scheme
	 * <b>Note 1</b>The slice is done between indexY1 and indexY2 INCLUSIVE
	 * <b>Note 2</b>indexY1 and indexY2 may include the use of under and over flow bins
	 * <b>Note 3</b>There is no note 3 (yet)
	 */
	protected abstract IHistogram2D internalSliceXZ(String title, int indexY1, int indexY2);
	/**
	 * The precise meaning of the arguments to the public slice
	 * methods is somewhat ambiguous, so we define this internal
	 * slice method and clearly specify its arguments.
	 * <p>
	 * <b>Note 0</b>indexX1 and indexX2 use our INTERNAL bin numbering scheme
	 * <b>Note 1</b>The slice is done between indexX1 and indexX2 INCLUSIVE
	 * <b>Note 2</b>indexX1 and indexX2 may include the use of under and over flow bins
	 * <b>Note 3</b>There is no note 3 (yet)
	 */
	protected abstract IHistogram2D internalSliceYZ(String title, int indexX1, int indexX2);
	/**
	 * Package private method to map from the external representation of bin
	 * number to our internal representation of bin number
	 */
	int mapX(int index)
	{
		int bins = xAxis.bins() + 2;
		if (index >= bins) throw new IllegalArgumentException("bin="+index);
		if (index >= 0) return index+1;
		if (index == IHistogram.UNDERFLOW) return 0;
		if (index == IHistogram.OVERFLOW) return bins-1;
		throw new IllegalArgumentException("bin="+index);
	}
	/**
	 * Package private method to map from the external representation of bin
	 * number to our internal representation of bin number
	 */
	int mapY(int index)
	{
		int bins = yAxis.bins() + 2;
		if (index >= bins) throw new IllegalArgumentException("bin="+index);
		if (index >= 0) return index+1;
		if (index == IHistogram.UNDERFLOW) return 0;
		if (index == IHistogram.OVERFLOW) return bins-1;
		throw new IllegalArgumentException("bin="+index);
	}
	/**
	 * Package private method to map from the external representation of bin
	 * number to our internal representation of bin number
	 */
	int mapZ(int index)
	{
		int bins = zAxis.bins() + 2;
		if (index >= bins) throw new IllegalArgumentException("bin="+index);
		if (index >= 0) return index+1;
		if (index == IHistogram.UNDERFLOW) return 0;
		if (index == IHistogram.OVERFLOW) return bins-1;
		throw new IllegalArgumentException("bin="+index);
	}
	public int[] minMaxBins()
	{
		double minValue = Double.MAX_VALUE;
		double maxValue = Double.MIN_VALUE;
		int minBinX = -1;
		int minBinY = -1;
		int minBinZ = -1;
		int maxBinX = -1;
		int maxBinY = -1;
		int maxBinZ = -1;
		for (int i=xAxis.bins(); --i >= 0; ) {
			for (int j=yAxis.bins(); --j >= 0; ) {
				for (int k=zAxis.bins(); --k >= 0; ) {
					double value = binHeight(i,j,k);
					if (value < minValue) {
						minValue = value;
						minBinX = i;
						minBinY = j;
						minBinZ = k;
					}
					if (value > maxValue) {
						maxValue = value;
						maxBinX = i;
						maxBinY = j;
						maxBinZ = k;
					}
				}
			}
		}
		int[] result = {minBinX,minBinY,minBinZ, maxBinX,maxBinY,maxBinZ};
		return result;
	}
	public IHistogram2D projectionXY()
	{
		String newTitle = title() + " (projectionXY)";
		return internalSliceXY(newTitle,mapZ(IHistogram.UNDERFLOW),mapZ(IHistogram.OVERFLOW));
	}
	public IHistogram2D projectionXZ()
	{
		String newTitle = title() + " (projectionXZ)";
		return internalSliceXZ(newTitle,mapY(IHistogram.UNDERFLOW),mapY(IHistogram.OVERFLOW));
	}
	public IHistogram2D projectionYZ()
	{
		String newTitle = title() + " (projectionYZ)";
		return internalSliceYZ(newTitle,mapX(IHistogram.UNDERFLOW),mapX(IHistogram.OVERFLOW));
	}
	public IHistogram2D sliceXY(int indexZ )
	{
		return sliceXY(indexZ,indexZ);
	}
	public IHistogram2D sliceXY(int indexZ1, int indexZ2)
	{
		int start = mapZ(indexZ1);
		int stop = mapZ(indexZ2);
		String newTitle = title() + " (sliceXY ["+indexZ1+":"+indexZ2+"])";
		return internalSliceXY(newTitle,start,stop);
	}
	public IHistogram2D sliceXZ(int indexY )
	{
		return sliceXZ(indexY,indexY);
	}
	public IHistogram2D sliceXZ(int indexY1, int indexY2)
	{
		int start = mapY(indexY1);
		int stop = mapY(indexY2);
		String newTitle = title() + " (sliceXZ ["+indexY1+":"+indexY2+"])";
		return internalSliceXY(newTitle,start,stop);
	}
	public IHistogram2D sliceYZ(int indexX )
	{
		return sliceYZ(indexX,indexX);
	}
	public IHistogram2D sliceYZ(int indexX1, int indexX2)
	{
		int start = mapX(indexX1);
		int stop = mapX(indexX2);
		String newTitle = title() + " (sliceYZ ["+indexX1+":"+indexX2+"])";
		return internalSliceYZ(newTitle,start,stop);
	}
	public double sumAllBinHeights()
	{
		double n = 0;
		for (int i=xAxis.bins(); --i >= -2;)
			for (int j=yAxis.bins(); --j >= -2;)
				for (int k=zAxis.bins(); --k >= -2;)
		{
			n += binHeight(i,j,k);	 
		}
		return n;
	}
	public double sumBinHeights()
	{
		double n = 0;
		for (int i=0; i<xAxis.bins(); i++)
			for (int j=0; j<yAxis.bins(); j++)
				for (int k=0; k<zAxis.bins(); k++)
		{
			n += binHeight(i,j,k);	 
		}
		return n;
	}
	public double sumExtraBinHeights()
	{
		return sumAllBinHeights() - sumBinHeights();
	}
	public IAxis xAxis()
	{
		return xAxis;
	}
	public IAxis yAxis()
	{
		return yAxis;
	}
	public IAxis zAxis()
	{
		return zAxis;
	}
}
