/* 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;
import uk.ac.roe.wfau.TextToLines;
import uk.ac.roe.wfau.VDFSSchema;

public class FITSWriter
{
    static DateFormat timeFormatter =DateFormat.getDateTimeInstance(DateFormat.SHORT,DateFormat.SHORT,Locale.UK);
    
    public static int padding (int sizeSoFar) {
    	return padding((long)sizeSoFar);
    }
    public static int padding (long sizeSoFar) {
    	int mod=(int) (sizeSoFar%2880);
    	if (mod >0) {
    		mod=2880-mod;
    	}
    	return mod;
    } 
    String [] comments=null;
	double nan=3.4E+38;
    double radians=Math.PI/180.0;   
    String SQLQuery=null;
    int archiveID=VDFSSchema.WSAARCHIVEID;
    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  FITSWriter () {
        this.SQLQuery=null;
    }
    public  FITSWriter (String SQLQuery ) {
        this.SQLQuery=SQLQuery;
    }
    public  FITSWriter (String SQLQuery, String database ) {
        //System.out.println("constructing");
        this.SQLQuery=SQLQuery;
        this.database=database;
    }
    public  FITSWriter (String SQLQuery, String database, int archiveID ) {
        //System.out.println("constructing");
        this.SQLQuery=SQLQuery;
        this.database=database;
        this.archiveID=archiveID;
    }
    public void setComments(String [] comm){
        comments=comm;
    }
	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));
//	    bdos = new BufferedDataOutputStream(new GZIPOutputStream(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.findKey("TFIELD");
		h.addValue("SYMBOL","{} cross 5","");
        h.insertComment("FITSWriter: database:"+database);
        

        h.insertComment(timeFormatter.format(new Date()));
        
        if (SQLQuery != null) {
            h.insertComment("SQL Query");           
            FormatLines fl = new FormatLines(SQLQuery.replaceAll("[^\\p{ASCII}]"," ").replaceAll("\r?\n"," "),70);
            int nl=0;
            while (fl.startP >= 0 && nl < 200) {
                h.insertComment(fl.getLine());
                nl++;
            }
            fl=null;
        }
        if (comments != null) {
            for (int j=0; j<comments.length;j++) {
                if (comments[j].length() < 70) {
                h.insertComment(comments[j]);
                }
                else {
                    String [] stA=TextToLines.getLineArray(comments[j],70);
                    if (stA != null) {
                        for (int k=0; k<stA.length; k++) {
                            h.insertComment(stA[k]);
                        }
                    }
                }
            }
        }
        
        // add TDISP for string columns to work with xcatview



        String ucd;
        String desc;

		for (int i=0; i<noCols; i += 1) {
			h.findKey("TDIM"+(i+1));
			if (rsmd.getColumnName(i+1).equalsIgnoreCase("RA") || rsmd.getColumnName(i+1).equalsIgnoreCase("DEC")){
			String key="TUNIT"+(i+1);
			String keyValue="RADIANS";
			h.addValue(key,keyValue,"");
		}
			ucd=VDFSSchema.getUCDOfCol(archiveID,rsmd.getColumnName(i+1));
			desc=VDFSSchema.getDescOfCol(archiveID,rsmd.getColumnName(i+1));
			
			//System.out.println(VDFSSchema.getUCDOfCol(archiveID,rsmd.getColumnName(i+1)));
			h.addValue("TUCD"+(i+1),ucd.substring(0, Math.min(ucd.length(), 68)),"" );
			h.addValue("TCOMM"+(i+1),desc.substring(0, Math.min(desc.length(), 68)),"" );
			
			switch(rsmd.getColumnType(i+1)){
			case Types.SMALLINT :
				h.addValue("TNULL"+(i+1),-9999,"" );
			break;
			case Types.INTEGER :
				h.addValue("TNULL"+(i+1),-99999999,"");
				break;
			case Types.BIGINT :
				h.addValue("TNULL"+(i+1),-99999999,"");
				break;
			default :
				break;
		}	
			
			
		
		}
		

		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: ref2" + 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)};
						break;
						case Types.SMALLINT :
							row[i]=new short[]{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()};
								}
							catch (Exception e) {
							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};
							}
							else {
								row[i]=new double[]{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};
							}
							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())};
						    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())};
						    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};
							*/
							
							 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())};
						    break;

						case Types.NUMERIC :
							try {
							row[i]=new double[]{Double.parseDouble(rs.getString(i+1))};
							}
							catch(Exception e) {
							row[i]=new double[]{nan};
							}
							break;

						case Types.DECIMAL :
							try {
							row[i]=new double[]{Double.parseDouble(rs.getString(i+1))};
							}
							catch(Exception e) {
							row[i]=new double[]{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};
							break;
			}
		}
		
		bdos.writeArray(row);
		//bdos.flush();



}
catch(Exception e){
				System.out.println("A Problem occured ref1: " + e);
			}
}


	public void close()
	{
		try{
		int pad=padding((long)noRows*(long)recordLength);
	//	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,"");
		//System.out.println("her2");
		h1.rewrite();
		
	}
	catch(Exception e){
					System.out.println("A Problem occured refa: " + e);
			}
	}

}