package hep.aida.ref.fitter;

import hep.aida.*;
import hep.aida.dev.*;
import java.util.Hashtable;

/**
 * @author The AIDA team @ SLAC.
 *
 */

public class FitResult implements IDevFitResult {
    
    private String[] constraints = null;
    private String dataDescription = "";
    private String engineName = "";
    private String fitMethod = "";
    private Hashtable parSetHash = new Hashtable();
    private int fitStatus = -1;
    private IFunction function = null;
    private boolean isValid = false;
    private int nDof = -1;
    private double quality = Double.NaN;
    private double[][] covMatrix;
    private double seconds;
    
    public FitResult( int dim ) {
        this( dim, Double.NaN );
    }
    
    public FitResult( int dim, double seconds ) {
        covMatrix = new double[dim][dim];
        this.seconds = seconds;
    }
    
    public String[] constraints() {
        return constraints;
    }
    
    public double covMatrixElement(int i, int j) {
        return covMatrix[i][j];
    }
    
    public String dataDescription() {
        return dataDescription;
    }
    
    public String engineName() {
        return engineName;
    }
    
    public String fitMethodName() {
        return fitMethod;
    }
    
    public IFitParameterSettings fitParameterSettings(String parName) {
        return (IFitParameterSettings) parSetHash.get(parName);
    }
    
    public int fitStatus() {
        return fitStatus;
    }
    
    public IFunction fittedFunction() {
        return function;
    }
    
    public boolean isValid() {
        return isValid;
    }
    
    public int ndf() {
        return nDof;
    }
    
    public double quality() {
        return quality;
    }
    
    public void setConstraints(String[] constraints) {
        this.constraints = constraints;
    }
    
    public void setCovMatrixElement(int xCol, int yCol, double covEl) {
        covMatrix[xCol][yCol] = covEl;
    }
    
    public void setDataDescription(String dataDescription) {
        this.dataDescription = dataDescription;
    }
    
    public void setEngineName(String engineName) {
        this.engineName = engineName;
    }
    
    public void setFitMethodName(String fitMethod) {
        this.fitMethod = fitMethod;
    }
    
    public void setFitParameterSettings(String parName, IFitParameterSettings parSetting) {
        parSetHash.put(parName,parSetting);
    }
    
    public void setFitStatus(int fitStatus) {
        this.fitStatus = fitStatus;
    }
    
    public void setFittedFunction(IFunction function) {
        this.function = function;
    }
    
    public void setIsValid(boolean isValid) {
        this.isValid = isValid;
    }
    
    public void setNdf(int nDof) {
        this.nDof = nDof;
    }
    
    public void setQuality(double quality) {
        this.quality = quality;
    }

    
    
    public double[] fittedParameters() {
        return function.parameters();
    }
    
    public String[] fittedParameterNames() {
        return function.parameterNames();
    }
    
    public double fittedParameter(String name) {
        return function.parameter(name);
    }
    
    public double[] errors() {
        int dim = function.numberOfParameters();
        double[] errors = new double[dim];
        for( int i = 0; i < dim; i++ ) 
            errors[i] = Math.sqrt(covMatrixElement(i,i));
        return errors;
    }
        
    public double[] errorsPlus() {
        return errors();
    }
    
    public double[] errorsMinus() {
        return errors();
    }
    
    private String fitStatusToString() {
        switch ( fitStatus ) {
            case 1 :
                return "Diagonal approximation";
            case 2 :
                return "Converged with non positive definite matrix";
            case 3 :
                return "Converged";
            case 4 :
                return "Converged with small gradient";
            case 5 :
                return "Converged with small step size";
            case 6 :
                return "Not Converged";
            case 7 :
                return "Stopped, reached max iterations";
            case 8 :
                return "Stopped, too many large steps. Function might be unbound.";
            default :
                return "Undefined";                
        }
    }
    
    public void printResult() {
        String[] parNames = function.parameterNames();

        System.out.println("************************************************");
        System.out.println("*  Performed "+fitMethodName()+" fit using "+engineName());
        System.out.println("*  Data Set "+dataDescription());
        System.out.println("*  Function "+function.codeletString());
        System.out.println("************************************************");
        System.out.println("*  Performance  *");
        System.out.println("*****************");        
        System.out.println("*  Status  : "+fitStatusToString());
        System.out.println("*  Quality : "+quality());
        System.out.println("*  nDoF    : "+ndf());        
        if ( ! Double.isNaN(seconds) ) System.out.println("*  Time    : "+seconds+" seconds");
        System.out.println("************************************************");
        System.out.println("*  Parameters  *");
        System.out.println("****************");
                
        for( int i = 0; i < parNames.length; i++ ) {
            String parName = parNames[i];
            double parVal = function.parameter(parName);
            IFitParameterSettings fitPar = fitParameterSettings(parName);
            if ( ! fitPar.isFixed() ) {
                double err = Math.sqrt(covMatrixElement(i,i));
                if ( err != 0 ) System.out.println("*  "+parName+" : \t "+parVal+"\t "+err);
                else  System.out.println("*  "+parName+" : \t "+parVal+"\t Constrained");
            } else 
                System.out.println("*  "+parName+" : \t "+parVal+"\t Fixed");
        }
        System.out.println("************************************************");
        System.out.println("*  Covariance Matrix  *");
        System.out.println("***********************");
        System.out.print("*  \t");
        for( int i = 0; i < parNames.length; i++ ) System.out.print( parNames[i]+"\t");
        System.out.println();
        for( int i = 0; i < parNames.length; i++ ) {
            System.out.print("*  "+parNames[i]+"\t");
            for ( int j = 0; j < parNames.length; j++ ) {
                double cov = covMatrixElement(i,j);
                if ( cov == 0 ) System.out.print("\t");
                else System.out.print(cov+" ");
            }
            System.out.println();
        }
        System.out.println("************************************************\n\n");
        
    }
}

