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

import java.awt.*;
import java.awt.event.*;
import java.io.*;
import java.util.*;
import java.text.*;
import javax.swing.*;

public class ImageJiggler {
    static ReducedData[] regrid;
    static ReducedData coadded;
    static int N;
    static String fileName;
    
    static int[] di, dj;
    
    static double beam = 4.0 * Unit.ArcSECOND;
    static double minExposure = 0.1;
    static double maxRMS = -1.0;
    static double scalingFactor = 1.0;
    
    static String version = "13 May 2005";

    static DecimalFormat of = new DecimalFormat("0.0");

    public static void main(String[] args) {
	versionInfo();	
	if(args.length == 0) usage(); 
	
	N=0;
	for(int i=0; i<args.length; i++) if(!isOption(args[i])) N++;
	ReducedData[] maps = new ReducedData[N];
	
	for(int i=0,k=0; i<args.length; i++) {
	    if(isOption(args[i])) option(args[i]);
	    else {
		try {	
		    String name = args[i]; 
		    System.out.print(" Reading " + name + "\t");
		    System.out.flush();		    
		    maps[k] = new ReducedData(name);
		    maps[k].scale(scalingFactor);
		    k++;
		}
		catch(Exception e) { e.printStackTrace(); }
	    } 
	}

	if(fileName == null) fileName = Crush.workPath + maps[0].sourceName + ".coadded.fits"; 
	
	regrid = ReducedCoadder.getIdenticalGridMaps(maps);

	System.out.println(beam/Unit.ArcSECOND);

	for(int k=0; k<N; k++) regrid[k].convolveTo(beam);

	di = new int[N];
	dj = new int[N];

	coadded = (ReducedData) regrid[0].clone();
	coadded.setSize(coadded.sizeX, coadded.sizeY);

	coadded.show();
	add();
	coadded.setDefaults();
	coadded.display.repaint();
	
	coadded.fileName = fileName;
	
	coadded.frame.addKeyListener(new KeyAdapter() {
		public void keyPressed(KeyEvent evt) {
		    if(evt.getKeyCode() == KeyEvent.VK_RIGHT) { di[activeMap]++; }
		    else if(evt.getKeyCode() == KeyEvent.VK_LEFT) { di[activeMap]--; }
		    else if(evt.getKeyCode() == KeyEvent.VK_UP) { dj[activeMap]--; }
		    else if(evt.getKeyCode() == KeyEvent.VK_DOWN) { dj[activeMap]++; }
		    else return;
		    
		    System.out.print("\r  RAO : " + of.format(di[activeMap] * coadded.delta / Unit.ArcSECOND) +
				     "   DECO : " + of.format(dj[activeMap] * coadded.delta / Unit.ArcSECOND));

		    add();
		    coadded.display.repaint();
		}
		
		public void keyTyped(KeyEvent evt) {
		    if(evt.getKeyChar() == '+') activeMap++;
		    else if(evt.getKeyChar() == '-') activeMap--;
		    else return;
		    
		    activeMap %= N;
		    if(activeMap < 0) activeMap += N;
		    
		    System.out.println("\nAdjusting Map " + (activeMap + 1) + ":");
		}
	    });
				     
	//try { coadded.write(); } catch(Exception e) { e.printStackTrace(); }
    }

    public static void add() {
	coadded.clear();

	for(int k=0; k<N; k++) {
	    for(int i=0, i1=di[k]; i<coadded.sizeX; i++, i1++) if(i1 >= 0 && i1 < regrid[k].sizeX)
		for(int j=0, j1=dj[k]; j<coadded.sizeY; j++, j1++) if(j1 >= 0 && j1 < regrid[k].sizeY) {
		    if(regrid[k].flag[i1][j1] == 0) {
			coadded.clean[i][j] += regrid[k].weight[i1][j1] * regrid[k].clean[i1][j1];
			coadded.weight[i][j] += regrid[k].weight[i1][j1];
			coadded.count[i][j] += regrid[k].count[i1][j1];
			coadded.flag[i][j] = 0;
		    }
		}
	}
	
	coadded.normalize();

	if(maxRMS > 0.0) for(int i=0; i<coadded.sizeX; i++) for(int j=0; j<coadded.sizeY; j++)
	    if(coadded.flag[i][j] == 0) if(coadded.rms[i][j] > maxRMS) coadded.flag[i][j] = 0;

	final double minCount = minExposure * coadded.getMax(coadded.count);
	for(int i=0; i<coadded.sizeX; i++) for(int j=0; j<coadded.sizeY; j++)
	    if(coadded.flag[i][j] == 0) if(coadded.count[i][j] < minCount) coadded.flag[i][j] = 0;
    }

    public static void option(String optionString) {
	StringTokenizer tokens = new StringTokenizer(optionString, "=,");

	String key = tokens.nextToken();

	//options.add(optionString);
	
	if(key.equalsIgnoreCase("-minexp")) minExposure = Double.parseDouble(tokens.nextToken());
	
	if(key.equalsIgnoreCase("-rmsclip")) maxRMS = Double.parseDouble(tokens.nextToken());

	else if(key.equalsIgnoreCase("-out")) fileName = tokens.nextToken();

	else if(key.equalsIgnoreCase("-name")) fileName = Crush.workPath + tokens.nextToken();
	
	else if(key.equalsIgnoreCase("-convolve")) beam = Double.parseDouble(tokens.nextToken()) * Unit.ArcSECOND;

	else if(key.equalsIgnoreCase("-scale")) scalingFactor = Double.parseDouble(tokens.nextToken());

	else if(key.equalsIgnoreCase("-help")) usage();

	else System.out.println("WARNING! Unrecognized option: " + key);
    
    }

    public static boolean isOption(String argument) {
	return argument.charAt(0) == '-';
    }

    public static void versionInfo() {
	System.out.println("jiggle -- Coaddition and Jiggling Utility for SHARC2 maps");
	System.out.println("          Author: Attila Kovacs <attila@submm.caltech.edu");
	System.out.println("          Version: " + version);
	System.out.println();
    }

    public static void usage() {	
	System.out.println("usage: jiggle [options] scanNo [[options] scanNo]...");
	System.out.println();
	System.out.println("Options:");
	System.out.println();
	System.out.println("   -help      Produces this help screen.");
	System.out.println("   -out=      Specify the output file name. The default value is");
	System.out.println("              [source name].coadded.fits.");
	System.out.println("   -name=     Specify the output file name relative to output path.");
	System.out.println("   -minexp=   Relative Exposure under which to clip coadded map.");
	System.out.println("   -rmsclip=  Clips all points from the final map that have RMS values");
	System.out.println("              above the specified value.");
	System.out.println("   -scale=    Scale the successive maps by the specified value (1.0).");

	System.out.println();
	
	System.exit(1);
    }

    static int activeMap = 0;
}
    
	
