import java.text.*;

public class SkyDipTool {
    static SharcScan scan = new SharcScan();


    public static void main(String[] args) {
	Crush.parseConfig(Crush.defaultConfig);

	try {
	    scan.taufit=true;
	    scan.read(args[0]);
	    scan.totalPower();
	    scan.getGainCoefficients();
	    scan.init();
	    
	    for(int bol=0; bol<scan.pixels; bol++) {
		Bolometer pixel = scan.pixel[bol];
		pixel.G0 = 1.0F;
		pixel.eps0 = 0.0F;
		pixel.weight = 1.0F;
	    }

	    float T = (float) (scan.temperature + 273.16);
	    float min = T, max = 0.0F;
	    int bol0 = (5*32) + 15;

	    float minI = scan.frame[0].readout[bol0];
	    float maxI = minI;

	    
	    for(int t=0; t<scan.frames; t++) {
		SharcFrame frame = scan.frame[t];
		frame.offset = (1.0F - frame.transmission) * T;

		if(frame.offset < min) min = frame.offset;
		if(frame.offset > max) max = frame.offset;

		if(frame.readout[bol0] < minI) minI = frame.readout[bol0];
		if(frame.readout[bol0] > maxI) maxI = frame.readout[bol0];
	    }

	    float V0 = (maxI - minI) / (1.0F - minI / maxI);

	    DecimalFormat tf = new DecimalFormat("0.0");
	    System.out.println("# Emissivity Range is " + tf.format(min) + " - " + tf.format(max) + " K");    

	    DecimalFormat df = new DecimalFormat("0.000E0");
	    System.out.println("# Signal Range is " + df.format(minI) + " - " + df.format(maxI) + " V");	    

	    //System.out.println(" T0 = " + tf.format(T0) + " K");
	    //for(int t=0; t<scan.frames; t++) scan.frame[t].offset -= T0;

	    System.out.println("# " + scan.activePixels + " Active Bolometers.");

	    for(int i=0; i<25; i++) {
		getGain();
		getNonLinearity();
		weight();
		getCorrelated();
	    }

	    print();
	}
	catch(Exception e) { e.printStackTrace(); }
	
    }


    public static double gain(int t, int bol) {
	Bolometer pixel = scan.pixel[bol];
	return pixel.G0 + pixel.eps0 * scan.frame[t].readout[bol];
    }

    public static double smallGain(int t, int bol) {
	Bolometer pixel = scan.pixel[bol];
	return pixel.G0 + 2.0 * pixel.eps0 * scan.frame[t].readout[bol];
    }
    
    public static void getGain() {
	for(int bol=0; bol<scan.pixels; bol++) {
	    Bolometer pixel = scan.pixel[bol];
	    double sum = 0.0, norm = 0.0;

	    for(int t=0; t<scan.frames; t++) {
		SharcFrame frame = scan.frame[t];
		double I = frame.readout[bol];
		double C = frame.offset;

		sum += (I - pixel.eps0 * C * C) * C;
		norm += C * C;
	    }
	    
	    if(norm == 0.0) System.out.println("# Zero Gain Norm.");
	    pixel.G0 = (float) (sum / norm);
	}

	/*
	double aveG0 = 0.0;
	Bolometer[] pixel = scan.pixel;
	for(int bol=0; bol<scan.pixels; bol++) if(pixel[bol].isActive) aveG0 += pixel[bol].G0;
	aveG0 /= scan.activePixels;
	*/
    }

     public static void getNonLinearity() {
	for(int bol=0; bol<scan.pixels; bol++) {
	    Bolometer pixel = scan.pixel[bol];
	    double sum = 0.0, norm = 0.0;

	    for(int t=0; t<scan.frames; t++) {
		SharcFrame frame = scan.frame[t];
		double I = frame.readout[bol];
		double C = frame.offset;
		double C2 = C * C;
		
		sum += (I - pixel.G0 * C) * C2;
		norm += C2 * C2;
	    }

	    if(norm == 0.0) System.out.println("# Zero NL Norm.");

	    pixel.eps0 = (float) (sum / norm);
	}
    }

    public static void getCorrelated() {
	Bolometer[] pixel = scan.pixel;

	for(int t=0; t<scan.frames; t++) {
	    SharcFrame frame = scan.frame[t];
	    double sum = 0.0, norm = 0.0;

	    for(int bol=0; bol<scan.pixels; bol++) if(pixel[bol].isActive) {
		double G = gain(t, bol);
		double g = smallGain(t, bol);

		sum += pixel[bol].weight * g * frame.readout[bol];
		norm += pixel[bol].weight * G * g;
	    }

	    if(norm == 0.0) System.out.println("# No data in frame. ");
	    frame.offset = (float) (sum / norm);
	}
    }
	
    public static void weight() {
	SharcFrame[] frame = scan.frame;

	double sumpw = 0.0;
	for(int bol=0; bol<scan.pixels; bol++) if(scan.pixel[bol].isActive) sumpw += scan.pixel[bol].weight;

	if(sumpw == 0.0) System.out.println("# Zero PW Sum. ");

	double sumvar = 0.0;

	for(int bol=0; bol<scan.pixels; bol++) {
	    Bolometer pixel = scan.pixel[bol];
	    double var = 0.0;

	    for(int t=0; t<scan.frames; t++) {
		double value = frame[t].readout[bol] - gain(t, bol) * frame[t].offset; 
		var += value * value;
	    }
	    
	    var /= scan.frames * (1.0 - pixel.weight / sumpw);
	    pixel.weight = (float) Math.sqrt(1.0 / var);

	    if(pixel.isActive) sumvar += pixel.weight * var;
	}

	System.out.println("# Chi-Squared is " + new DecimalFormat("0.000E0").format(sumvar/sumpw));
    }

    public static void print() {
	DecimalFormat df = new DecimalFormat("0.000000E0"); 
	for(int bol=0; bol<scan.pixels; bol++) {
	    Bolometer pixel = scan.pixel[bol];
	    System.out.println((bol/32+1) + "\t" + (bol%32+1) + "\t" + df.format(pixel.G0) + "\t" + df.format(pixel.eps0));
	}
    }
}
