import java.util.*;
import java.text.*;

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

public final class Util {
    
    static String HMS(double time) {
	DecimalFormat d2 = new DecimalFormat("00");

	time /= Unit.HOUR;

	int h = (int)time; time -= h;
	time *= 60.0; int m = (int)time; time -= m;
	time *= 60.0; int s = (int)time; time -= s;
	time *= 10.0; int S = (int)time;

	return d2.format(h) + ":" + d2.format(m) + ":" + d2.format(s) + "." + S;
    }

    static String DMS(double angle) {
	DecimalFormat d2 = new DecimalFormat("00");

	int sign = angle >= 0 ? 1 : -1;
	angle *= sign;

	angle *= 180.0/Math.PI; int d = (int)angle; angle -= d;
	angle *= 60.0; int m = (int)angle; angle -= m;
	angle *= 60.0; int s = (int)angle; angle -= s;
	angle *= 10.0; int S = (int)angle; 

	String signString = sign > 0 ? "" : "-";

	return signString + d2.format(d) + ":" + d2.format(m) + ":" + d2.format(s) + "." + S;
    }
	
      
    static double parseTime(String HMS) {
	StringTokenizer tokens = new StringTokenizer(HMS, " \t\n\r:hmsHMS");
	
	double time = Integer.parseInt(tokens.nextToken()) * Unit.HOUR;
	time += Integer.parseInt(tokens.nextToken()) * Unit.MINUTE;
	time += Double.parseDouble(tokens.nextToken()) * Unit.SECOND;
	
	return time;
    }

    static double parseAngle(String DMS) {
	StringTokenizer tokens = new StringTokenizer(DMS, " \t\n\r:dmsDMS");
	
	double angle = Integer.parseInt(tokens.nextToken()) * Unit.DEGREE;
	angle += Integer.parseInt(tokens.nextToken()) * Unit.ArcMINUTE;
	angle += Double.parseDouble(tokens.nextToken()) * Unit.ArcSECOND;
	
	return angle;
    }

     public static boolean parseBoolean(String value)throws NumberFormatException {
	// Yes/No
	if(value.equalsIgnoreCase("y")) return true;
	if(value.equalsIgnoreCase("yes")) return true;
	if(value.equalsIgnoreCase("n")) return false;
	if(value.equalsIgnoreCase("no")) return false;
	// True/False
	if(value.equalsIgnoreCase("t")) return true;
	if(value.equalsIgnoreCase("true")) return true;
	if(value.equalsIgnoreCase("f")) return false;
	if(value.equalsIgnoreCase("false")) return false;
	// On/Off
	if(value.equalsIgnoreCase("on")) return true;
	if(value.equalsIgnoreCase("off")) return false;
	// 1/0
	if(value.equalsIgnoreCase("1")) return true;
	if(value.equalsIgnoreCase("0")) return false;

	throw new NumberFormatException(" ERROR! Illegal Boolean value: " + value);
    }

    static double[] lineCoeff = new double[2];
    public static double[] lineFit(double[] y, double[] w) {
	double s=0.0, sx=0.0, sy=0.0, sxx=0.0, sxy=0.0;
	
	for(int i=0; i<y.length; i++) {
	    s += w[i];
	    sx += w[i]*i;
	    sy += w[i]*y[i];
	    sxx += w[i]*i*i;
	    sxy += w[i]*i*y[i];
	}
	double D = s*sxx - sx*sx;

	lineCoeff[0] = (sxx*sy - sx*sxy) / D;
	lineCoeff[1] = (s*sxy - sx*sy) / D;

	return lineCoeff;
    }

    public static double fitsDouble(double value) {
	if(value < 0.0 && value > -1.0) {
	    DecimalFormat df = new DecimalFormat("0.0000000000000E0");
	    return Double.parseDouble(df.format(value));
	}
	else if(value > 0.0 && value < 1.0) {
	    DecimalFormat df = new DecimalFormat("0.00000000000000E0");
	    return Double.parseDouble(df.format(value));
	}
	else return value;
    }

    public static void addLongFitsKey(Header header, String key, String value, String comment) 
	throws FitsException, HeaderCardException {

	Cursor cursor = header.iterator();
	while(cursor.hasNext()) cursor.next();
	addLongFitsKey(cursor, key, value, comment);
    }

    public static String getLongFitsKey(Header header, String key) {
	String value = header.getStringValue(key);
	
	if(value == null) {
	    value = new String();
	    char ext = 'A';
	    String part;
	    do {
		part = header.getStringValue(key + ext);
		if(part != null) value += part;
		ext++;
	    } 
	    while(part != null);
	}
	
	return value;
    }
	    
    
    public static void addLongFitsKey(Cursor cursor, String key, String value, String comment) 
	throws FitsException, HeaderCardException {

	final int size = 65 - comment.length();

	if(value.length() <= size) {
	    cursor.add(new HeaderCard(key, value, comment));
	    return;
	}
	
	int start = 0;	
	char ext = 'A';

	while(start < value.length()) {
	    int end = start + size;
	    if(end > value.length()) end = value.length();

	    cursor.add(new HeaderCard(key + ext, value.substring(start, end), comment));

	    ext++;
	    start = end;
	}
    }
	

    public static void notchFilter(double[] signal, double samplingInterval, double frequency, double width, boolean alias) {
	if(true) return;

	final double nyqvist = 1.0 / (2.0 * samplingInterval);
	final double resolution = nyqvist / signal.length;
	
	// If the requested filter frequency is higher than the Nyqvist frequency
	// Either return signal as is, or construct an appropriate aliased filter.
	if(frequency > nyqvist) {
	    if(!alias) return;
	    frequency = Math.IEEEremainder(frequency, nyqvist);
	    if(frequency < 0.0) frequency += nyqvist;
	}

	// Set the frequency to the nearest frequency channel
	int ch = (int) Math.round(frequency/resolution);

	final int N = (int) Math.round(nyqvist/width);
	if(N <= 0) return;

	double[][] filter = new double[N][2];	

	final double omega = Math.PI * ch / N;

	for(int t=0; t<N; t++) {
	    final double angle = omega * t;

	    filter[t][0] = Math.cos(angle);
	    filter[t][1] = Math.sin(angle);
	}
       
	final int nT = (int) Math.floor((float) signal.length / N);
	
	double A=0, B=0;
	for(int T=0; T < nT; T++) {
	    double normA = 0.0, normB = 0.0;
	    A = 0.0; B = 0.0;	    

	    for(int blockt=0,t=T*N; blockt < N; blockt++,t++) {
		final double value = signal[t];
		final double angle = -omega * blockt;
		    
		A += value * filter[blockt][0];
		B += value * filter[blockt][1];
		    
		normA += filter[blockt][0] * filter[blockt][0];
		normB += filter[blockt][1] * filter[blockt][1];		
	    }
	    
	    if(normA > 0.0) A /= normA;
	    if(normB > 0.0) B /= normB;
	    
	    for(int blockt=0,t=T*N; blockt < N; blockt++,t++) if(t < signal.length)
		signal[t] -= A * filter[blockt][0] + B * filter[blockt][1];
	}

	// Assume that signal amplitude stays the same for the last incomplete bit
	// may cause a bit of a chirp if that is not the case.
	for(int t=nT*N, blockt=0; t<signal.length; t++, blockt++)
	    signal[t] -= A * filter[blockt][0] + B * filter[blockt][1];
	
    }   
}

    
