package ij.plugin;
import java.awt.*;
import java.io.*;
import java.util.zip.GZIPInputStream;
import ij.*;
import ij.io.*;
import ij.process.*;
import ij.measure.*;

/** Opens and displays FITS images. The FITS format is 
    described at "ftp://nssdc.gsfc.nasa.gov/pub/fits".
*/
public class FITS_Reader extends ImagePlus implements PlugIn {

    public void run(String arg) {
        OpenDialog od = new OpenDialog("Open FITS...", arg);
        String directory = od.getDirectory();
        String fileName = od.getFileName();
        if (fileName==null)
            return;
        IJ.showStatus("Opening: " + directory + fileName);
        FitsDecoder fd = new FitsDecoder(directory, fileName);
        FileInfo fi = null;
        try {fi = fd.getInfo();}
        catch (IOException e) {}
        if (fi!=null && fi.width>0 && fi.height>0 && fi.offset>0) {
            FileOpener fo = new FileOpener(fi);
            ImagePlus imp = fo.open(false);
            if(fi.nImages==1) {
              ImageProcessor ip = imp.getProcessor();              
              ip.flipVertical(); // origin is at bottom left corner
              setProcessor(fileName, ip);
            } else {
              ImageStack stack = imp.getStack(); // origin is at bottom left corner              
              for(int i=1; i<=stack.getSize(); i++)
                  stack.getProcessor(i).flipVertical();
              setStack(fileName, stack);
            }
            Calibration cal = imp.getCalibration();
            if (fi.fileType==FileInfo.GRAY16_SIGNED && fd.bscale==1.0 && fd.bzero==32768.0)
                cal.setFunction(Calibration.NONE, null, "Gray Value");
            setCalibration(cal);
            setProperty("Info", fd.getHeaderInfo());
			setFileInfo(fi); // needed for File->Revert
            if (arg.equals("")) show();
        } else
            IJ.error("This does not appear to be a FITS file.");
        IJ.showStatus("");
    }

}

class FitsDecoder {
    private String directory, fileName;
    private DataInputStream f;
    private StringBuffer info = new StringBuffer(512);
    double bscale, bzero;

    public FitsDecoder(String directory, String fileName) {
        this.directory = directory;
        this.fileName = fileName;
    }

    FileInfo getInfo() throws IOException {
        FileInfo fi = new FileInfo();
        fi.fileFormat = FileInfo.FITS;
        fi.fileName = fileName;
        fi.directory = directory;
        fi.width = 0;
        fi.height = 0;
        fi.offset = 0;

        InputStream is = new FileInputStream(directory + fileName);
        if (fileName.toLowerCase().endsWith(".gz")) is = new GZIPInputStream(is);
        f = new DataInputStream(is);
        String line = getString(80);
        info.append(line+"\n");
        if (!line.startsWith("SIMPLE"))
            {f.close(); return null;}
        int count = 1;
        while ( true ) {
            count++;
            line = getString(80);
			info.append(line+"\n");
  
            // Cut the key/value pair
			int index = line.indexOf ( "=" );

			// Strip out comments
			int commentIndex = line.indexOf ( "/", index );
			if ( commentIndex < 0 )
				commentIndex = line.length ();
			
			// Split that values
			String key;
			String value;
			if ( index >= 0 ) {
				key = line.substring ( 0, index ).trim ();
				value = line.substring ( index + 1, commentIndex ).trim ();
			} else {
				key = line.trim ();
				value = "";
			}

			// Time to stop ?
			if (key.equals ("END") ) break;

			// Look for interesting information			
            if (key.equals("BITPIX")) {
                int bitsPerPixel = Integer.parseInt ( value );
               if (bitsPerPixel==8)
                    fi.fileType = FileInfo.GRAY8;
                else if (bitsPerPixel==16)
                    fi.fileType = FileInfo.GRAY16_SIGNED;
                else if (bitsPerPixel==32)
                    fi.fileType = FileInfo.GRAY32_INT;
                else if (bitsPerPixel==-32)
                    fi.fileType = FileInfo.GRAY32_FLOAT;
                else if (bitsPerPixel==-64)
                    fi.fileType = FileInfo.GRAY64_FLOAT;
                else {
                    IJ.error("BITPIX must be 8, 16, 32, -32 (float) or -64 (double).");
                    f.close();
                    return null;
                }
            } else if (key.equals("NAXIS1"))
                fi.width = Integer.parseInt ( value );
            else if (key.equals("NAXIS2"))
                fi.height = Integer.parseInt( value );
            else if (key.equals("NAXIS3")) //for multi-frame fits
                fi.nImages = Integer.parseInt ( value );
            else if (key.equals("BSCALE"))
                bscale = parseDouble ( value );
            else if (key.equals("BZERO"))
                bzero = parseDouble ( value );

			if (count>360 && fi.width==0)
				{f.close(); return null;}
        }

        f.close();
        fi.offset = 2880+2880*(((count*80)-1)/2880);
        return fi;
    }

    String getString(int length) throws IOException {
        byte[] b = new byte[length];
        f.readFully(b);
        return new String(b);
    }

    int getInteger(String s) {
        s = s.substring(10, 30);
        s = s.trim();
        return Integer.parseInt(s);
    }

	double parseDouble(String s) throws NumberFormatException {
		Double d = new Double(s);
		return d.doubleValue();
	}

    String getHeaderInfo() {
        return new String(info);
    }

}