import nom.tam.fits.*;
import nom.tam.util.*;

public abstract class Model implements Comparable {
    String name;
    Object data;

    int timeConstant = 1;
    int minTimeConstant = 1, maxTimeConstant = 8092;
    int turn = 0;
    int generation = 0;

    boolean isActive = false;
    boolean comparable = false;
    boolean autoTimeScale = false;
    boolean preliminary = true;
    boolean alwaysSolve = false;
    boolean precedesSource = false;

    public Model(String setname) { name = setname; }

    public Model(String setname, boolean isComparable) { name = setname; comparable = isComparable; }

    public void init() {}

    public void extract() {
	if(generation == 0) precedesSource = !ReductionTool.sourceIsActive;
	generation++; 
    }
    
    public void inject() {}

    public void applyChanges() {}

    public abstract double getRMS();

    public double getTypicalFlux() { return getRMS(); }
    
    public double getSourceCorrectionFactor(double FWHM) { return 1.0; }

    public double getScanCorrectionKappa(SharcScan scan, double FWHM) { return 0.0; }

    // model with rms X will create map artefacts on the order of <X> * sqrt(Nmap/(Npixels*Nframes))
    // Npixels * Nframes occurences of X^2 over Nmap pixels.
    // Npixels * Nframes / NMap occurences per pixel.
    //
    // So compare rms  = <X> / Sqrt(Nmap / (Npixel*Nframes));
    // and  rms / (Nmap / (Npixels*Nframes)) for source...
    // 
    public int compareTo(Object o) throws ClassCastException {
	Model model = (Model) o;

	double flux = getTypicalFlux();
	double modelFlux = model.getTypicalFlux();

	return flux < modelFlux ? 1 : (flux > modelFlux ? -1 : 0);
    }

    public abstract boolean key(String key, String value);

    public abstract boolean option(String flag, String value);

    public abstract void usage();
   
    public abstract int getNumberOfParameters();

    public void reset() { isActive = false; generation = 0; }
    
    public void dGain(int i, int t, float[] dmodel) {}

    public void weightAdjust(double factor) {}

    public Object[] getData() { return null; }

    public Samples[] getSpectra(int i, int windowSize) { return null; }

    public void editHeader(Header header) throws FitsException, HeaderCardException {
	// Write always at the end of the header
	Cursor cursor = header.iterator();
	while(cursor.hasNext()) cursor.next();
	editHeader(cursor);
    }

    public void editHeader(Cursor cursor) throws FitsException, HeaderCardException {}

    public int getT(SharcScan scan) { return getT(timeConstant, scan); }
    
    public int getT(int t, SharcScan scan) { return (int)Math.ceil((double) t / scan.downSample); }

    public SharcFrame getFrame(SharcScan scan, int t) {
	SharcFrame frame = scan.frame[t].duplicate();
	frame.readout = new float[scan.pixels];
	return frame;
    }
    
    public void guessTimeConstant(double level) {
	if(isActive) inject();

	timeConstant = minTimeConstant;

	double maxRMS = 0.0;
	boolean done = false;	

	System.out.print("\t" + name);
	System.out.flush();

	if(ReductionTool.debug) System.out.println();
	

	while(!done) {
	    extract();
	    
	    double rms = getRMS();

	    inject();

	    if(ReductionTool.debug) System.out.println("  " + timeConstant + " : " + rms);

	    if(rms > maxRMS) maxRMS = rms;
	    else if(rms < (1.0 - level) * maxRMS) done = true;

	    if(timeConstant > maxTimeConstant) done = true;

	    if(!done) timeConstant *= 2;
	}

	timeConstant /= 2;
	extract();

	System.out.println("\r\t\t\t\t-> " + timeConstant);
    }
}
