/*
 * FitData.java
 *
 * Created on August 16, 2002, 10:44 AM
 */

package hep.aida.ref.fitter.fitData;
import hep.aida.*;
import hep.aida.ref.function.RangeSet;
import hep.aida.ref.tuple.Tuple;
import hep.aida.dev.*;
import hep.aida.ref.fitter.fitData.RangeSetFilter;
import hep.aida.ref.tuple.TupleFactory;

/**
 *
 * @author  The AIDA team @ SLAC.
 *
 */
public class FitData implements IDevFitData {
    
    private ITuple tup;
    private int dimension;
    private int fitType;
    private IRangeSet[] rangeSet;
    private String dataDescription = "";
    
    /** Creates a new instance of FitData */
    public FitData() {
        reset();
    }
    
    
    public void create1DConnection(Object data) {
        if ( data instanceof IHistogram1D )
            create1DConnection((IHistogram1D)data);
        else if ( data instanceof ICloud1D )
            create1DConnection((ICloud1D)data);
        else if ( data instanceof IProfile1D )
            create1DConnection((IProfile1D)data);
        else if ( data instanceof IDataPointSet )
            create1DConnection((IDataPointSet)data);
        else
            throw new IllegalArgumentException("Cannot create 1D connection with object of type "+data.getClass());
    }
    
    /**
     * 1D connections.
     */
    public void create1DConnection(IHistogram1D hist) throws IllegalArgumentException {
        dataDescription = "IHistogram1D "+((IManagedObject)hist).name();
        prepareConnections(1,IDevFitter.BINNED_FIT);
        
        double[] vals = new double[1];
        int bins = hist.axis().bins();
        for ( int i = 0; i < bins; i++ ) {
            double e = hist.binError(i);
            double v = hist.binHeight(i);
            vals[0] = hist.binMean(i);
            if ( e != 0 )
                fillConnections(v,e,e,vals);
        }
        rangeSet[0].includeAll();        
//        rangeSet[0].excludeAll();        
//        rangeSet[0].include( hist.axis().lowerEdge(), hist.axis().upperEdge() );        
    }
    public void create1DConnection(IProfile1D profile) throws IllegalArgumentException {
        dataDescription = "IProfile1D "+((IManagedObject)profile).name();
        prepareConnections(1,IDevFitter.BINNED_FIT);
        
        double[] vals = new double[1];
        int bins = profile.axis().bins();
        for ( int i = 0; i < bins; i++ ) {
            double e = profile.binError(i);
            double v = profile.binHeight(i);
            vals[0] = profile.binMean(i);
            if ( e != 0 )
                fillConnections(v,e,e,vals);
        }
        rangeSet[0].includeAll();        
//        rangeSet[0].excludeAll();        
//        rangeSet[0].include( profile.axis().lowerEdge(), profile.axis().upperEdge() );        
    }
    public void create1DConnection(ICloud1D cloud) throws IllegalArgumentException {
        dataDescription = "ICloud1D "+((IManagedObject)cloud).name();
        if ( cloud.isConverted() ) throw new IllegalArgumentException("This ICloud is converted. You should explicitely fit the IHistogram inside the ICloud, if that is what you want to do");
        else {
            prepareConnections(1,IDevFitter.UNBINNED_FIT);
            
            double[] vals = new double[1];
            int entries = cloud.entries();
            for ( int i = 0; i < entries; i++ ) {
                vals[0] = cloud.value(i);
                fillConnections(0.,0.,0.,vals);
            }
        }
        rangeSet[0].includeAll();        
//        rangeSet[0].excludeAll();        
//        rangeSet[0].include(cloud.lowerEdge(), cloud.upperEdge() );        
    }
    public void create1DConnection(IDataPointSet dataPointSet, int xIndex, int valIndex) throws IllegalArgumentException {
        int[] indeces = {xIndex};
        createConnection(dataPointSet, indeces, valIndex);
    }
    
    
    /**
     * 2D connections.
     */
    public void create2DConnection(IHistogram2D hist) throws IllegalArgumentException {
        create2DConnection(hist,0,1);
    }
    public void create2DConnection(IHistogram2D hist, int xIndex, int yIndex) throws IllegalArgumentException {
        if ( xIndex*yIndex != 0 || xIndex+yIndex != 1 ) throw new IllegalArgumentException("Illegal values for the axis "+xIndex+" "+yIndex);
        
        dataDescription = "IHistogram2D "+((IManagedObject)hist).name();
        prepareConnections(2,IDevFitter.BINNED_FIT);
        
        double[] vals = new double[2];
        int xBins = hist.xAxis().bins();
        int yBins = hist.yAxis().bins();
        for ( int i = 0; i < xBins; i++ ) {
            for ( int j = 0; j < yBins; j++ ) {
                double e = hist.binError(i,j);
                double v = hist.binHeight(i,j);
                vals[xIndex] = hist.binMeanX(i,j);
                vals[yIndex] = hist.binMeanY(i,j);
                if ( e != 0 )
                    fillConnections(v,e,e,vals);
            }
        }
	rangeSet[xIndex].includeAll();
	rangeSet[yIndex].includeAll();
////	rangeSet[xIndex].excludeAll();
//        rangeSet[xIndex].include( hist.xAxis().lowerEdge(), hist.xAxis().upperEdge() );        
//	rangeSet[yIndex].excludeAll();
//        rangeSet[yIndex].include( hist.yAxis().lowerEdge(), hist.yAxis().upperEdge() );        
    }
    public void create2DConnection(IProfile2D profile) throws IllegalArgumentException {
        create2DConnection(profile,0,1);
    }
    public void create2DConnection(IProfile2D profile, int xIndex, int yIndex) throws IllegalArgumentException {
        if ( xIndex*yIndex != 0 || xIndex+yIndex != 1 ) throw new IllegalArgumentException("Illegal values for the axis "+xIndex+" "+yIndex);
        
        dataDescription = "IProfile2D "+((IManagedObject)profile).name();
        prepareConnections(2,IDevFitter.BINNED_FIT);
        
        double[] vals = new double[2];
        int xBins = profile.xAxis().bins();
        int yBins = profile.yAxis().bins();
        for ( int i = 0; i < xBins; i++ ) {
            for ( int j = 0; j < yBins; j++ ) {
                double e = profile.binError(i,j);
                double v = profile.binHeight(i,j);
                vals[xIndex] = profile.binMeanX(i,j);
                vals[yIndex] = profile.binMeanY(i,j);
                if ( e != 0 )
                    fillConnections(v,e,e,vals);
            }
        }
	rangeSet[xIndex].includeAll();
	rangeSet[yIndex].includeAll();
//	rangeSet[xIndex].excludeAll();
//        rangeSet[xIndex].include( profile.xAxis().lowerEdge(), profile.xAxis().upperEdge() );        
//	rangeSet[yIndex].excludeAll();
//        rangeSet[yIndex].include( profile.yAxis().lowerEdge(), profile.yAxis().upperEdge() );        
    }
    public void create2DConnection(ICloud2D cloud) throws IllegalArgumentException {
        create2DConnection(cloud,0,1);
    }
    public void create2DConnection(ICloud2D cloud, int xIndex, int yIndex) throws IllegalArgumentException {
        if ( cloud.isConverted() ) throw new IllegalArgumentException("This ICloud is converted. You should explicitely fit the IHistogram inside the ICloud, if that is what you want to do");
        else {
            if ( xIndex*yIndex != 0 || xIndex+yIndex != 1 ) throw new IllegalArgumentException("Illegal values for the axis "+xIndex+" "+yIndex);

            dataDescription = "ICloud2D "+((IManagedObject)cloud).name();
            prepareConnections(2,IDevFitter.UNBINNED_FIT);
            
            double[] vals = new double[2];
            int entries = cloud.entries();
            for ( int i = 0; i < entries; i++ ) {
                vals[xIndex] = cloud.valueX(i);
                vals[yIndex] = cloud.valueY(i);
                fillConnections(0.,0.,0.,vals);
            }
        }
	rangeSet[xIndex].includeAll();
	rangeSet[yIndex].includeAll();
//	rangeSet[xIndex].excludeAll();
//        rangeSet[xIndex].include( cloud.lowerEdgeX(), cloud.upperEdgeX() );        
//	rangeSet[yIndex].excludeAll();
//        rangeSet[yIndex].include( cloud.lowerEdgeY(), cloud.upperEdgeY() );        
    }
    public void create2DConnection(IDataPointSet dataPointSet, int xIndex, int yIndex, int valIndex) throws IllegalArgumentException {
        int[] indeces = {xIndex,yIndex};
        createConnection(dataPointSet, indeces, valIndex);
    }
    
    
    /**
     * 3D connections.
     */
    public void create3DConnection(IHistogram3D hist) throws IllegalArgumentException {
        create3DConnection(hist,0,1,2);
    }
    public void create3DConnection(IHistogram3D hist, int xIndex, int yIndex, int zIndex) throws IllegalArgumentException {
        if ( xIndex*yIndex*zIndex != 0 || xIndex+yIndex+zIndex != 3 ) throw new IllegalArgumentException("Illegal values for the axis "+xIndex+" "+yIndex+" "+zIndex);
        if ( xIndex > 2 || yIndex > 2 || zIndex>2 ) throw new IllegalArgumentException("Illegal values for the axis "+xIndex+" "+yIndex+" "+zIndex);
        
        dataDescription = "IHistogram3D "+((IManagedObject)hist).name();
        prepareConnections(3,IDevFitter.BINNED_FIT);
        
        double[] vals = new double[3];
        int xBins = hist.xAxis().bins();
        int yBins = hist.yAxis().bins();
        int zBins = hist.zAxis().bins();
        for ( int i = 0; i < xBins; i++ ) {
            for ( int j = 0; j < yBins; j++ ) {
                for ( int k = 0; k < zBins; k++ ) {
                    double e = hist.binError(i,j,k);
                    double v = hist.binHeight(i,j,k);
                    vals[xIndex] = hist.binMeanX(i,j,k);
                    vals[yIndex] = hist.binMeanY(i,j,k);
                    vals[zIndex] = hist.binMeanZ(i,j,k);
                    if ( e != 0 )
                        fillConnections(v,e,e,vals);
                }
            }
        }
	rangeSet[xIndex].includeAll();
	rangeSet[yIndex].includeAll();
	rangeSet[zIndex].includeAll();
//	rangeSet[xIndex].excludeAll();
//        rangeSet[xIndex].include( hist.xAxis().lowerEdge(), hist.xAxis().upperEdge() );        
//	rangeSet[yIndex].excludeAll();
//        rangeSet[yIndex].include( hist.yAxis().lowerEdge(), hist.yAxis().upperEdge() );        
//	rangeSet[zIndex].excludeAll();
//        rangeSet[zIndex].include( hist.zAxis().lowerEdge(), hist.zAxis().upperEdge() );        
    }
    public void create3DConnection(ICloud3D cloud) throws IllegalArgumentException {
        create3DConnection(cloud,0,1,2);
    }
    public void create3DConnection(ICloud3D cloud, int xIndex, int yIndex, int zIndex) throws IllegalArgumentException {
        if ( cloud.isConverted() ) throw new IllegalArgumentException("This ICloud is converted. You should explicitely fit the IHistogram inside the ICloud, if that is what you want to do");
        else {
            if ( xIndex*yIndex*zIndex != 0 || xIndex+yIndex+zIndex != 3 ) throw new IllegalArgumentException("Illegal values for the axis "+xIndex+" "+yIndex+" "+zIndex);
            if ( xIndex > 2 || yIndex > 2 || zIndex>2 ) throw new IllegalArgumentException("Illegal values for the axis "+xIndex+" "+yIndex+" "+zIndex);
            dataDescription = "ICloud3D "+((IManagedObject)cloud).name();
            prepareConnections(3,IDevFitter.UNBINNED_FIT);
            
            double[] vals = new double[3];
            int entries = cloud.entries();
            for ( int i = 0; i < entries; i++ ) {
                vals[xIndex] = cloud.valueX(i);
                vals[yIndex] = cloud.valueY(i);
                vals[zIndex] = cloud.valueZ(i);
                fillConnections(0.,0.,0.,vals);
            }
        }
	rangeSet[xIndex].includeAll();
	rangeSet[yIndex].includeAll();
	rangeSet[zIndex].includeAll();
        /*
	rangeSet[xIndex].excludeAll();
        rangeSet[xIndex].include( cloud.lowerEdgeX(), cloud.upperEdgeX() );        
	rangeSet[yIndex].excludeAll();
        rangeSet[yIndex].include( cloud.lowerEdgeY(), cloud.upperEdgeY() );        
	rangeSet[zIndex].excludeAll();
        rangeSet[zIndex].include( cloud.lowerEdgeZ(), cloud.upperEdgeZ() );        
         */
    }
    
    public void create3DConnection(IDataPointSet dataPointSet, int xIndex, int yIndex, int zIndex, int valIndex) throws IllegalArgumentException {
        int[] indeces = {xIndex,yIndex,zIndex};
        createConnection(dataPointSet, indeces, valIndex);
    }
    
    /**
     * Generic connections.
     */
    public void createConnection(IDataPointSet dataPointSet, int[] indeces, int valIndex) throws IllegalArgumentException {
        int dimension = indeces.length;
        for ( int i = 0; i<dimension; i++ ) {
            if ( indeces[i] > dataPointSet.dimension()-1 ) throw new IllegalArgumentException("Variable index "+indeces[i]+" cannot be greater than the dataPointSet dimension "+dataPointSet.dimension());
            if ( indeces[i] == valIndex ) throw new IllegalArgumentException("Variable index cannot be the same as the value index "+valIndex);
            for ( int j = i+1; j<dimension; j++ )
                if ( indeces[i] == indeces[j] ) throw new IllegalArgumentException("Two indeces are identical! Impossible configuration");
        }
        dataDescription = "IDataPointSet "+((IManagedObject)dataPointSet).name();
        prepareConnections(dimension,IDevFitter.BINNED_FIT);
        
        double[] vals = new double[dimension];
        for ( int i = 0; i < dataPointSet.size(); i++ ) {
            double v = dataPointSet.point(i).coordinate(valIndex).value();
            double ep = dataPointSet.point(i).coordinate(valIndex).errorPlus();
            double em = dataPointSet.point(i).coordinate(valIndex).errorMinus();
            if ( ep != 0 || em != 0 ) {
                for ( int j = 0; j < dimension; j++ )
                    vals[j] = dataPointSet.point(i).coordinate(indeces[j]).value();
                fillConnections(v,ep,em,vals);
            }
        }
        for ( int i = 0; i < dimension; i++ ) {
	    rangeSet[i].includeAll();        
////	    rangeSet[i].excludeAll();        
//	    rangeSet[i].include(dataPointSet.lowerExtent(indeces[i]), dataPointSet.upperExtent(indeces[i]));
	}
    }
    public void createConnection(ITuple tuple, IEvaluator[] evals) {
        int dimension = evals.length;
        dataDescription = "ITuple "+((IManagedObject)tuple).name();
        prepareConnections(dimension,IDevFitter.UNBINNED_FIT);
        
        for ( int i = 0; i<dimension; i++ )
            evals[i].initialize(tuple);

        double[] upperEdges = new double[dimension];
        double[] lowerEdges = new double[dimension];
        for ( int i = 0; i<dimension; i++ ) {
            lowerEdges[i] = Double.NaN;
            upperEdges[i] = Double.NaN;
        }
        
        double[] vals = new double[dimension];
        
        tuple.start();
        while ( tuple.next() ) {
            for ( int i = 0; i<dimension; i++ ) {
                vals[i] = evals[i].evaluateDouble();
                if ( Double.isNaN(upperEdges[i]) || vals[i] > upperEdges[i] ) upperEdges[i] = vals[i];
                if ( Double.isNaN(lowerEdges[i]) || vals[i] < lowerEdges[i] ) lowerEdges[i] = vals[i];
            }
            fillConnections(0,0,0,vals);
        }
        for ( int i = 0; i<dimension; i++ ) {
	    rangeSet[i].includeAll();    
//	    rangeSet[i].excludeAll();    
//	    rangeSet[i].include(lowerEdges[i], upperEdges[i] );
	}
    }
    public void createConnection(ITuple tuple, String[] colNames) {
        int dimension = colNames.length;
        dataDescription = "ITuple "+((IManagedObject)tuple).name();
        prepareConnections(dimension,IDevFitter.UNBINNED_FIT);

        int[] indeces = new int[dimension];
        for ( int i = 0; i<dimension; i++ )
            indeces[i] = tuple.findColumn(colNames[i]);
        
        double[] vals = new double[dimension];
        tuple.start();
        while ( tuple.next() ) {
            for ( int i = 0; i<dimension; i++ )
                vals[i] = tuple.getDouble(indeces[i]);
            fillConnections(0,0,0,vals);
        }
        for ( int i = 0; i<dimension; i++ ) {
	    rangeSet[i].includeAll();        
//	    rangeSet[i].excludeAll();        
//	    rangeSet[i].include(tuple.columnMin(indeces[i]), tuple.columnMax(indeces[i]) );
	}
    }
    public IRangeSet range(int index) throws IllegalArgumentException {
        if ( rangeSet == null ) throw new RuntimeException("RangeSet have not been initialized!!");
        if ( index < 0 ) throw new IllegalArgumentException("The index cannot be negative!!");
        if ( index > (rangeSet.length-1) ) throw new IllegalArgumentException("Wrong index "+index+". It exceeds the number of RangeSets "+rangeSet.length+".");
        return rangeSet[index];
    }
    public IDevFitDataIterator dataIterator() {
        TupleFactory tupFactory = new TupleFactory(null);
        return new FitDataIterator(tupFactory.createFiltered("",tup,new RangeSetFilter(rangeSet)));
    }
    public int dimension() {
        return dimension;
    }
    public String dataDescription() {
        return dataDescription;
    }
    public void reset() {
        tup = null;
        dimension = -1;
        fitType = -1;
        rangeSet = null;
    }
    public int fitType() {
        return fitType;
    }
    private void prepareConnections( int dimension, int fitType ) {
        reset();
        this.dimension = dimension;
        this.fitType = fitType;
        String tupString = "double value, double error, double minusError";
        for ( int i = 0; i<dimension; i++ )
            tupString += ", double x"+i;
        tup = new Tuple("","", tupString,"");
        rangeSet = new RangeSet[dimension];
        for ( int i = 0; i<dimension; i++ ) 
            rangeSet[i] = new RangeSet();
    }
    private void fillConnections( double value, double error, double minusError, double[] vals ) {
        tup.fill(0,value);
        tup.fill(1,error);
        tup.fill(2,minusError);
        for ( int i = 0; i<vals.length; i++ )
            tup.fill(3+i,vals[i]);
        tup.addRow();
    }
}
