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

import java.io.*;

public class Bolometer implements Cloneable {
    int flag = 0;
    int excludeFlags = 0x03;

    boolean isActive = true;
    boolean isFiltered13Hz = false;
    
    float weight = 1.0F;
    float rms = 0.0F;
    float gain = 1.0F;
    float offset = 0.0F;
    float G0 = 0.0F, I0, T0;
    float biasV = 1.0F * Unit.VOLT;

    Vector2D position;

    private boolean initialized = false;

    public Bolometer() {}

    public Bolometer(double dx, double dy) { position = new Vector2D(dx, dy); }

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

    public Bolometer duplicate() { return (Bolometer) clone(); }

    public void init(double samplingTime) {
	flag &= excludeFlags | SOFTWARE_FLAGS;
	isActive = !isFlagged();

	if(!initialized) {
	    defaultWeight = (float) samplingTime / (defaultNEV * defaultNEV);
	    weight = defaultWeight;
	    rms = (float) Math.sqrt(1.0/weight);
	    initialized = true;
	}
    }
    
    public void reset() { 
	weight = defaultWeight; 
	rms = (float) Math.sqrt(1.0/weight);
	gain = defaultGain;
	offset = 0.0F;
	flag &= HARDWARE_FLAGS;
    }
    
    public double getDirectTransmission(SharcScan scan) { return getDirectTransmission(scan, 0.0); }

    // Reminder: eps_obs = eps * areaFactor
    public double getDirectTransmission(SharcScan scan, double doffset) {
	double opticalLoadT = ((1.0 - (offset+doffset) / I0) * T0 - scan.excessLoadT);
	// The optical load must be referred to the pixel area that was
	// used for measuring the load parameters, which was in the
	// Cassegrain focus, in Jan 2004.
	opticalLoadT *= SharcHeader.pixelArea / scan.pixelArea;
	double eps = opticalLoadT / (scan.temperature + 273.16);
	return 1.0 - eps; 
    }

    public double getLoadTemperature() {
	return (1.0 - offset / I0) * T0; 
    }
    

    public boolean isExcluded() { return (flag & excludeFlags) != 0; }

    public boolean isFlagged() { return (flag & (excludeFlags | SOFTWARE_FLAGS)) != 0; }

    public static void editHeader(Cursor cursor) throws FitsException, HeaderCardException, IOException {
	cursor.add(new HeaderCard("FLG_HDWR", HARDWARE_FLAGS, "Flag pattern for hardware flags."));
	cursor.add(new HeaderCard("FLG_STWR", SOFTWARE_FLAGS, "Flag pattern for hardware flags."));
	cursor.add(new HeaderCard("FLG_DEAD", DEAD, "Flag pattern for dead pixels."));
	cursor.add(new HeaderCard("FLG_GAIN", INVALID_GAIN, "Flag pattern for pixels with invalid gain."));
	cursor.add(new HeaderCard("FLG_SPKE", TOO_MANY_SPIKES, "Flag pattern for spiky pixels."));
	cursor.add(new HeaderCard("FLG_IDOF", INSUFFICIENT_DOF, "Flag pattern for pixels with insufficient degrees of freedom."));
	cursor.add(new HeaderCard("FLG_FLMS", FLIMSY_PIXEL, "Flag pattern for flimsy pixels with residual structure."));
	cursor.add(new HeaderCard("FLG_EDGE", EDGE, "Flag pattern for array edge removed pixels."));
	cursor.add(new HeaderCard("FLG_NOIZ", NOISY, "Flag pattern for pixels with excess noise."));
	cursor.add(new HeaderCard("FLG_SPEC", SPECTRAL_FEATURE, "Flag pattern for pixels with significant spectral features."));
	cursor.add(new HeaderCard("FLG_TIME", TEMPORAL_FEATURE, "Flag pattern for pixels with significant temporal features."));
    }

    // The default Bolometer weight, given by 11.0 nV s1/2 NEP
    static float defaultNEV = (float) (11.0e-9 * Unit.VOLT * Math.sqrt(Unit.SECOND));
    float defaultWeight = 36.0F*Unit.MILLISECOND / (defaultNEV * defaultNEV);
    float defaultGain = 1.0F;

    final static int[] noisy13HzColumn = { 3, 11, 16, 24 };

    final static int HARDWARE_FLAGS = 0x000F;
    final static int SOFTWARE_FLAGS = 0xFFF0;

    final static int DEAD = 0x03;
    final static int IRRECOVERABLE = 0x10;
    final static int INVALID_GAIN = 0x100;
    final static int TOO_MANY_SPIKES = 0x200;
    final static int INSUFFICIENT_DOF = 0x400;
    final static int FLIMSY_PIXEL = 0x800;
    final static int EDGE = 0x1000;
    final static int NOISY = 0x2000;
    final static int SPECTRAL_FEATURE = 0x4000;
    final static int TEMPORAL_FEATURE = 0x8000;

}
