/* This class writes an SQL resultSet to a FITS binary table.
Three methods are setFileName, setRSMD (resultset metadata) and
setRS resultSet) */
package net.mar;

import java.io.*;
import java.sql.*;
import java.text.DateFormat;

import nom.tam.util.*;
import nom.tam.fits.*;
import nom.tam.image.*;
import java.util.*;
import java.util.Date;
import java.util.zip.*;

import uk.ac.roe.wfau.FormatLines;

public class NewFITSWriter
{
    static DateFormat timeFormatter =DateFormat.getDateTimeInstance(DateFormat.SHORT,DateFormat.SHORT,Locale.UK);
    


	double nan=3.4E+38;
    double radians=Math.PI/180.0;
    String SQLQuery=null;
    String database="";
	private String FileName; // FITS filename (existing or new)
    private int noCols=0; // no of coulmns in resultSet
    private Object[] row; // array of objects representing the row
    private StringBuffer sb=new StringBuffer("                              "+
	"                                                               "+
	"                                                               "+
	"                                                               "+
    "                                                               "); // large empty stringbuffer for padding out strings
	String tempString=new String()	;
    int recordLength=0; //length in bytes of row used to pad file size to mutiple of 2880
	int noRows=0;
    int noCharCols=0; // number of columns represented as strings
    int[] charCols; // array column numbers which are strings
    int[] charLength; // array of lengths of strings

    BufferedDataOutputStream bdos; // file output stream
	BufferedFile bf;

	Fits fits = new Fits();
	BinaryTableHDU bhdu;
	BasicHDU basicHDU;
//	BinaryTable btab = new BinaryTable();
//	FitsFactory.setUseAsciiTables(false);
	BasicHDU hdu;


	ResultSetMetaData rsmd;
    boolean newFile = true;
    public  NewFITSWriter () {
        this.SQLQuery=null;
    }
    public  NewFITSWriter (String SQLQuery ) {
        this.SQLQuery=SQLQuery;
    }
    public  NewFITSWriter (String SQLQuery, String database ) {
        this.SQLQuery=SQLQuery;
        this.database=database;
    }
	public void setFileName(String fn){
		// method to set FITS output filename
		FileName=fn;
        // test to see if it already exists

		File FITSFile=new File(fn);
		if (FITSFile.exists()){
			newFile=false;
		}
	}


	public void setRSMD(ResultSetMetaData rsmd) throws FileNotFoundException{
	try {
		this.rsmd=rsmd;
	noCols=rsmd.getColumnCount();
	row = new Object[noCols];
    charCols=new int[noCols];
    charLength=new int[noCols];
		for (int i=0; i<noCols; i += 1) {
			switch(rsmd.getColumnType(i+1)){
				case Types.TINYINT :
					row[i]=new short[]{1};
					recordLength+=2;
				break;
				case Types.SMALLINT :
					row[i]=new short[]{1};
					recordLength+=2;
				break;
				case Types.REAL :
					row[i]=new float[]{1};
					recordLength+=4;
					break;
				case Types.FLOAT :
					row[i]=new double[]{1};
					recordLength+=8;
					break;
				case Types.DOUBLE :
					row[i]=new double[]{1};
					recordLength+=8;
					break;
				case Types.INTEGER :
					row[i]=new int[]{1};
					recordLength+=4;
					break;
				case Types.VARCHAR :
				    charCols[noCharCols]=i;
				    charLength[i]=rsmd.getPrecision(i+1);
				    recordLength+=charLength[i];
				    while (charLength[i] > sb.length()) {
						sb.append("                                                               ");
					}
				    row[i]=new String[]{sb.substring(0,charLength[i])};
				    noCharCols+=1;
					break;
				case Types.CHAR :
				    charCols[noCharCols]=i;
				    charLength[i]=rsmd.getPrecision(i+1);
				    recordLength+=charLength[i];
				    row[i]=new String[]{sb.substring(0,charLength[i])};
				    noCharCols+=1;
					break;
				case Types.BIGINT :
				    charCols[noCharCols]=i;
				    charLength[i]=20;
				    recordLength+=charLength[i];
				    row[i]=new String[]{sb.substring(0,charLength[i])};
				    noCharCols+=1;
				    /*
					row[i]=new long[]{1};
					recordLength+=8;
					*/
					break;
				case Types.DATE :
				    charCols[noCharCols]=i;
				    charLength[i]=24;
				    recordLength+=charLength[i];
				    row[i]=new String[]{sb.substring(0,charLength[i])};
				    noCharCols+=1;
					break;
				case Types.NUMERIC :
					row[i]=new double[]{1};
					recordLength+=8;
					break;
				case Types.DECIMAL :
					row[i]=new double[]{1};
					recordLength+=8;
					break;
				default :
				    charCols[noCharCols]=i;
				    charLength[i]=21;
				    recordLength+=charLength[i];
				    row[i]=new String[]{sb.substring(0,charLength[i])};
				    noCharCols+=1;
					break;
			}
		}

//        System.out.println (sb.length());
		BinaryTable btab = new BinaryTable();
		FitsFactory.setUseAsciiTables(false);
		hdu = Fits.makeHDU(row);
		fits.addHDU(hdu);
		bhdu = (BinaryTableHDU) fits.getHDU(1);



        for (int i=0; i<noCols; i += 1) {
			if (rsmd.getColumnName(i+1).equals(null) || rsmd.getColumnName(i+1).equals("")  )
			{
			bhdu.setColumnName(i,"null", null);
		}
		else
		{
			bhdu.setColumnName(i,rsmd.getColumnName(i+1).toUpperCase(), null);
		}
		}

	    Header h=bhdu.getHeader();
	    bdos = new BufferedDataOutputStream(new FileOutputStream(FileName,true));
	    // if its a new file write dummy image extension at start
 		if (newFile){
 	//	ImageHDU.getDummyHDU().write(bdos);
 		basicHDU=ImageHDU.getDummyHDU();
		Header bh=basicHDU.getHeader();
		// delete these keys to pass fverify
		bh.deleteKey("PCOUNT");
	 	bh.deleteKey("GCOUNT");
	    basicHDU.write(bdos);
	}
		h.addValue("NAXIS2",0,"");
//		HeaderCard hc = new HeaderCard("SYMBOL","{} cross 8","");

        h.deleteKey("THEAP");
        h.insertComment("FITSWriter: database:"+database);
        

        h.insertComment(timeFormatter.format(new Date()));
        
        if (SQLQuery != null) {
            h.insertComment("SQL Query");
            FormatLines fl = new FormatLines(SQLQuery.replaceAll("\r?\n"," "),70);
            int nl=0;
            while (fl.startP >= 0 && nl < 200) {
                h.insertComment(fl.getLine());
                nl++;
            }
            fl=null;
        }

        // add TDISP for string columns to work with xcatview



h.findKey("TFIELD");
h.addValue("SYMBOL","{} cross 5","");

		for (int i=0; i<noCols; i += 1) {
			if (rsmd.getColumnName(i+1).equalsIgnoreCase("RA") || rsmd.getColumnName(i+1).equalsIgnoreCase("DEC")){
		    h.findKey("TDIM"+(i+1));
			String key="TUNIT"+(i+1);
			String keyValue="RADIANS";
			h.addValue(key,keyValue,"");
		}
		}


		for (int i=0; i<noCols; i += 1) {
			if (charLength[i] >0){
		    h.findKey("TDIM"+(i+1));
			String key="TDISP"+(i+1);
			String keyValue="A"+charLength[i];
			h.addValue(key,keyValue,"");
		}
		}


			h.write(bdos);
			bdos.flush();
}
	catch (FileNotFoundException fnf) {
	    throw new FileNotFoundException(fnf.toString());
	}

catch(Exception e){
				System.out.println("A Problem occured: " + e);
			}
}


public void setRS(ResultSet rs){
	try {
        noRows=noRows+1;
		for (int i=0; i<noCols; i += 1) {
					switch(rsmd.getColumnType(i+1)){
						case Types.TINYINT :
						//	row[i]=new short[]{rs.getShort(i+1)};
						bdos.writeShort(rs.getShort(i+1));
						break;
						case Types.SMALLINT :
						//	row[i]=new short[]{rs.getShort(i+1)};
						bdos.writeShort(rs.getShort(i+1));
						break;
						case Types.REAL :
//							((float[])row[i])[0]=Float.valueOf(rs.getString(i+1)).floatValue();
							try{
							//row[i]=new float[]{Float.valueOf(rs.getString(i+1)).floatValue()};
							    bdos.writeFloat(Float.valueOf(rs.getString(i+1)).floatValue());
								}
							catch (Exception e) {
							    bdos.writeFloat(0.0f);
							//row[i]=new float[]{0};
								}
							break;

						case Types.FLOAT :
//							((double[])row[i])[0]=rs.getDouble(i+1);

							if (rsmd.getColumnName(i+1).equalsIgnoreCase("RA") || rsmd.getColumnName(i+1).equalsIgnoreCase("DEC")){
							//	row[i]=new double[]{rs.getDouble(i+1)*radians};
								bdos.writeDouble(rs.getDouble(i+1)*radians);
							}
							else {
							//	row[i]=new double[]{rs.getDouble(i+1)};
								bdos.writeDouble(rs.getDouble(i+1));
								//bdos.writeDouble(rs.getDouble(i+1));
							}

							break;
						case Types.DOUBLE :
//							((double[])row[i])[0]=rs.getDouble(i+1);

							if (rsmd.getColumnName(i+1).equalsIgnoreCase("RA") || rsmd.getColumnName(i+1).equalsIgnoreCase("DEC")){
								//row[i]=new double[]{rs.getDouble(i+1)*radians};
								bdos.writeDouble(rs.getDouble(i+1)*radians);
							}
							else {
								row[i]=new double[]{rs.getDouble(i+1)};
							    bdos.writeDouble(rs.getDouble(i+1));
							}

							break;
						case Types.INTEGER :
							//row[i]=new int[]{rs.getInt(i+1)};
						    bdos.writeInt(rs.getInt(i+1));
							break;
						case Types.VARCHAR :
						    tempString=rs.getString(i+1);
						    if (tempString==null || tempString.equals("null")){
								tempString=" ";
							}
//                            row[i]=new String[]{rs.getString(i+1)+sb.substring(0,rsmd.getPrecision(i+1)-rs.getString(i+1).length())};
							//row[i]=new String[]{tempString+sb.substring(0,charLength[i]-tempString.length())};
							bdos.writeBytes(tempString+sb.substring(0,charLength[i]-tempString.length()));
						    break;
						case Types.CHAR :
						    tempString=rs.getString(i+1);
						    if (tempString==null || tempString.equals("null")){
								tempString=" ";
							}

							//row[i]=new String[]{tempString+sb.substring(0,charLength[i]-tempString.length())};
							bdos.writeBytes(tempString+sb.substring(0,charLength[i]-tempString.length()));
						    break;
						case Types.BIGINT :
						    tempString=rs.getString(i+1);
						    if (tempString==null || tempString.equals("null")){
								tempString=" ";
							}
							tempString=(tempString+sb.substring(0,charLength[i])).substring(0,charLength[i]);
							//row[i]=new String[]{tempString};
							bdos.writeBytes(tempString+sb.substring(0,charLength[i]-tempString.length()));
							/*
							 row[i]=new long[]{rs.getLong(i+1)};
							 */
						    break;
						case Types.DATE :
						    tempString=rs.getString(i+1);
						    if (tempString==null || tempString.equals("null")){
								tempString=" ";
							}
								//row[i]=new String[]{tempString+sb.substring(0,charLength[i]-tempString.length())};
								bdos.writeBytes(tempString+sb.substring(0,charLength[i]-tempString.length()));
						    break;

						case Types.NUMERIC :
							try {
							//row[i]=new double[]{Double.parseDouble(rs.getString(i+1))};
							bdos.writeDouble(Double.parseDouble(rs.getString(i+1)));
							}
							catch(Exception e) {
							//row[i]=new double[]{nan};
							    bdos.writeDouble(nan);
							}
							break;

						case Types.DECIMAL :
							try {
							//row[i]=new double[]{Double.parseDouble(rs.getString(i+1))};
							    bdos.writeDouble(Double.parseDouble(rs.getString(i+1)));
							}
							catch(Exception e) {
							//row[i]=new double[]{nan};
							bdos.writeDouble(nan);
							}
							break;

						default :
						    tempString=rs.getString(i+1);
						    if (tempString==null || tempString.equals("null")){
								tempString=" ";
							}
							tempString=(tempString+sb.substring(0,charLength[i])).substring(0,charLength[i]);
							//row[i]=new String[]{tempString};
							bdos.writeBytes(tempString);
							break;
			}
		}
		
	//	bdos.writeArray(row);
		//bdos.flush();



}
catch(Exception e){
				System.out.println("A Problem occured: " + e);
			}
}


	public void close()
	{
		try{
		int pad = FitsUtil.padding(noRows*recordLength);
		bdos.write(new byte[pad]);
		bdos.flush();
		bdos.close();
		fits = new Fits(FileName);


		int countHDU=1;
		boolean notAtEnd=true;
		while (notAtEnd)
		{
			try {
				countHDU+=1;
				basicHDU=fits.getHDU(countHDU);
				if (basicHDU == null){
					notAtEnd=false;
					}
		}
		catch (Exception e)
		{
			notAtEnd=false;
			//System.out.println("no hdus " + countHDU);
		}
		}

		bhdu = (BinaryTableHDU) fits.getHDU(countHDU-1);

		Header h1=bhdu.getHeader();

		h1.addValue("NAXIS2",noRows,"");

		h1.rewrite();
	}
	catch(Exception e){
					System.out.println("A Problem occured: " + e);
			}
	}

}