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

public abstract class ModelSet {
    String name;
    String message = "Estimating " + name + ".";    

    SharcScan scan;
    Thread runner;

    Object data;

    int timeConstant = 1;
    int minTimeConstant = -1, maxTimeConstant = -1;
    int turn = 0;

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

    boolean verbose = false;    

    Hashtable model;

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

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

    public void setScan(SharcScan aScan) { scan = aScan; }

    public void add(Model modelTemplate, SharcScan scan) {
	Model model = (Model) modelTemplate.clone();
	model.setScan(scan);
	model.add(model, scan);
    }

    ThreadGroup extraction = new ThreadGroup(name + "extraction");
    public void extract() {
	System.out.print("   " + message + " ");
	System.out.flush();

	// Start each model extraction in a separate thread...
	for(int i=0; i<ReductionTool.scans; i++) {
	    Model model = (Model) model.get(ReductionTool.scan[i]);
	    model.runner = new Thread(extraction) {
		    public void run() {
			model.extract();
		    }
		};
	    model.runner.start();
	}

	// Wait for each model to complete, then report on the results.
	for(int i=0; i<ReductionTool.scans; i++) {
	    Model model = (Model) model.get(ReductionTool.scan[i]);
	    model.wait();
	    model.report();
	}
    }

    public void postProcess() {
	System.out.println();
    }
    
    public abstract inject() {
	// Start each model extraction in a separate thread...
	for(int i=0; i<ReductionTool.scans; i++) {
	    Model model = (Model) model.get(ReductionTool.scan[i]);
	    model.runner = new Thread(extraction) {
		    public void run() {
			model.extract();
		    }
		};
	    model.runner.start();
	}

	// Wait for each model to complete, then report on the results.
	for(int i=0; i<ReductionTool.scans; i++) {
	    Model model = (Model) model.get(ReductionTool.scan[i]);
	    model.wait();
	    model.report();
	}}
    
    public abstract double getRMS();

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

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

    public abstract void usage();
    
    public abstract long getMemoryEstimate();

    public void reset() { isActive = false; }
    
    public void weightAdjust(double factor) {}

    public Object[] getData() { 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 (int)Math.ceil((double) timeConstant / scan.downSample); }
    
    public void guessTimeConstant(double level) {
	if(isActive) inject();
	
	timeConstant = minTimeConstant;

	double maxRMS = 0.0;
	boolean done = false;	

	System.out.println();

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

	    inject();

	    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();
    }
}
