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

public abstract class Model implements Cloneable {
    SharcScan scan;
    Tread runner;

    Object data;

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

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

    boolean verbose = false;    

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

    public Object clone() { 
	try { return super.clone(); }
	catch(CloneNotSupportedException e) { return null; }
    }

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

    // Make local shadows of useful scan fields.
    int pixels, frames, nT, T;
    SharcFrame[] frame;
    Bolometer pixel;

    public void init() {
	// Locally shadowed scan fields:
	pixels = scan.pixels;
	frames = scan.frames;
	frame = scan.frame;
	pixel = scan.pixel;

	// Calculate Model constants
	T = (int) Math.ceil((double) timeConstant / scan.downSample);
	nT = (int) Math.ceil((double) frames / T);	
    }

    public void report() {}

    // wait only if running thread is currently active
    public synchronized void wait() {
	if(runner != null) if(runner.isActive()) super.wait();
    }
	
    // All threaded functions should call notifyAll() for possibly waiting threads.
    public synchronized void extract() { notifyAll(); }
    
    public synchronized void inject() { notifyAll(); }
    
    public synchronized double getRMS() { notifyAll(); }

    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 synchronized void weightAdjust(double factor) { notifyAll(); }

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