/* File iraf2fits.c
 * July 20, 2000
 * By Doug Mink
 */

/* Module:	iraf2fits.c (Translate IRAF header to FITS header)
 * Purpose:	Translate IRAF header to FITS header
 * Copyright:	1998 Smithsonian Astrophysical Observatory
 *		You may do anything you like with this file except remove
 *		this copyright.  The Smithsonian Astrophysical Observatory
 *		makes no representations about the suitability of this
 *		software for any purpose.  It is provided "as is" without
 *		express or implied warranty.
 */

#include <stdio.h>		/* define stderr, FD, and NULL */
#include <unistd.h>		/* define lseek arguments */
#include <stdlib.h>
#include <fcntl.h>
#include <sys/stat.h>
#include "fitshead.h"

#ifndef VMS
#ifdef SYSV
#include <string.h>		/* strlen, strcat, strcpy, strrchr */
#else
#include <strings.h>		/* strlen, strcat, strcpy, rindex */
#define strchr index
#define strrchr rindex
#endif
#else
#include <string.h>		/* strlen, strcat, strcpy, strrchr */
#endif

/* Parameters from iraf/lib/imhdr.h for IRAF version 1 images */
#define SZ_IMPIXFILE	 79		/* name of pixel storage file */
#define SZ_IMHDRFILE	 79   		/* length of header storage file */
#define SZ_IMTITLE	 79		/* image title string */
#define LEN_IMHDR	2052		/* length of std header */

/* Parameters from iraf/lib/imhdr.h for IRAF version 2 images */
#define	SZ_IM2PIXFILE	255		/* name of pixel storage file */
#define	SZ_IM2HDRFILE	255		/* name of header storage file */
#define	SZ_IM2TITLE	383		/* image title string */
#define LEN_IM2HDR	2046		/* length of std header */

/* Offsets into header in bytes for parameters in IRAF version 1 images */
#define IM_PIXTYPE       16	     /* datatype of the pixels */
#define IM_NDIM	  20	     /* number of dimensions */
#define IM_LEN	   24	     /* length (as stored) */
#define IM_PHYSLEN       52	     /* physical length (as stored) */
#define IM_PIXOFF	88	     /* offset of the pixels */
#define IM_PIXFILE      412	     /* name of pixel storage file */
#define IM_HDRFILE      572	     /* name of header storage file */
#define IM_TITLE	732	     /* image name string */

/* Offsets into header in bytes for parameters in IRAF version 2 images */
#define IM2_PIXTYPE      10	     /* datatype of the pixels */
#define IM2_NDIM	 18	     /* number of dimensions */
#define IM2_LEN	  22	     /* length (as stored) */
#define IM2_PHYSLEN      50	     /* physical length (as stored) */
#define IM2_PIXOFF       86	     /* offset of the pixels */
#define IM2_PIXFILE     126	     /* name of pixel storage file */
#define IM2_HDRFILE     382	     /* name of header storage file */
#define IM2_TITLE       638	     /* image name string */

/* Codes from iraf/unix/hlib/iraf.h */
#define	TY_CHAR		2
#define	TY_SHORT	3
#define	TY_INT		4
#define	TY_LONG		5
#define	TY_REAL		6
#define	TY_DOUBLE	7
#define	TY_COMPLEX	8
#define TY_POINTER      9
#define TY_STRUCT       10
#define TY_USHORT       11
#define TY_UBYTE        12

/* Local subroutines used to decode the IRAF header */
int irafgeti4();
char *irafgetc2();
char *irafgetc();
char *iraf2str();
static void same_path();
static int swapiraf=0;	/* =1 if IRAF file has DEC/PC byte order */
static void irafswap();
static void irafswap2();
static void irafswap4();
static void irafswap8();
static int machswap();


/* Convert IRAF image header to FITS image header, returning FITS header */

char *
iraf2fits (hdrname, irafheader, imhver, nbiraf, nbfits)

char	*hdrname;	/* IRAF header file name (may be path) */
char	*irafheader;	/* IRAF image header */
int	imhver;		/* IRAF .imh format version number */
int	nbiraf;		/* Number of bytes in IRAF header */
int	*nbfits;	/* Number of bytes in FITS header (returned) */

{
    int lfhead;		/* Actual length of FITS header (returned) */
    char *objname;	/* object name from FITS file */
    int i, j, k, nax, nbits;
    char *pixname, *bang, *chead;
    char *fitsheader;
    int nblock, nlines;
    char *fhead, *fhead1, *fp, endline[81];
    char fitsline[81];
    int pixtype;
    char irafchar;
    int n, ib, imu, pixoff, impixoff;
    int imndim, imphyslen, impixtype;
    char *calloc_errchk();

    /* Set up last line of FITS header */
    (void)strncpy (endline,"END", 3);
    for (i = 3; i < 80; i++)
	endline[i] = ' ';
    endline[80] = 0;

    if (imhver == 2) {
	nlines = 7 + ((nbiraf - LEN_IM2HDR) / 81);
	imndim = IM2_NDIM;
	imphyslen = IM2_PHYSLEN;
	impixtype = IM2_PIXTYPE;
	impixoff = IM2_PIXOFF;
	}
    else {
	nlines = 7 + (nbiraf - (4 * LEN_IMHDR) / 162);
	imndim = IM_NDIM;
	imphyslen = IM_PHYSLEN;
	impixtype = IM_PIXTYPE;
	impixoff = IM_PIXOFF;
	}

    /*  Initialize FITS header */
    nblock = (nlines * 80) / 2880;
    *nbfits = (nblock + 3) * 2880;
    fitsheader = calloc_errchk(*nbfits, 1, "FITS header");
    fhead = fitsheader;
    lfhead = 0;
    (void)strncpy (fitsheader, endline, 80);
    hputl (fitsheader, "SIMPLE", 1);
    fhead = fhead + 80;

    /*  Set pixel size in FITS header */
    pixtype = irafgeti4 (irafheader, impixtype);
    switch (pixtype) {
    case TY_UBYTE:
	nbits = 8;
	break;
    case TY_CHAR:
	nbits = 8;
	break;
    case TY_USHORT:
	nbits = -16;
	break;
    case TY_SHORT:
	nbits = 16;
	break;
    case TY_INT:
    case TY_LONG:
	nbits = 32;
	break;
    case TY_REAL:
	nbits = -32;
	break;
    case TY_DOUBLE:
	nbits = -64;
	break;
    default:
	(void)fprintf(stderr,"Unsupported data type: %d\n", pixtype);
	return (NULL);
    }
    hputi4 (fitsheader,"BITPIX",nbits);
    fhead = fhead + 80;

    /*  Set image dimensions in FITS header */
    nax = irafgeti4 (irafheader, imndim);
    hputi4 (fitsheader,"NAXIS",nax);
    fhead = fhead + 80;

    n = irafgeti4 (irafheader, imphyslen);
    hputi4 (fitsheader, "NAXIS1", n);
    fhead = fhead + 80;

    if (nax > 1) {
	n = irafgeti4 (irafheader, imphyslen+4);
	hputi4 (fitsheader, "NAXIS2", n);
	}
    else
	hputi4 (fitsheader, "NAXIS2", 1);
    fhead = fhead + 80;

    if (nax > 2) {
	n = irafgeti4 (irafheader, imphyslen+8);
	hputi4 (fitsheader, "NAXIS3", n);
	fhead = fhead + 80;
	}
    if (nax > 3) {
	n = irafgeti4 (irafheader, imphyslen+12);
	hputi4 (fitsheader, "NAXIS4", n);
	fhead = fhead + 80;
	}

    /* Set object name in FITS header */
    if (imhver == 2)
	objname = irafgetc (irafheader, IM2_TITLE, SZ_IM2TITLE);
    else
	objname = irafgetc2 (irafheader, IM_TITLE, SZ_IMTITLE);
    hputs (fitsheader,"OBJECT",objname);
    free (objname);
    fhead = fhead + 80;

    /* Save image header filename in header */
    hputs (fitsheader,"IMHFILE",hdrname);
    fhead = fhead + 80;

    /* Save image pixel file pathname in header */
    if (imhver == 2)
	pixname = irafgetc (irafheader, IM2_PIXFILE, SZ_IM2PIXFILE);
    else {
	pixname = irafgetc2 (irafheader, IM_PIXFILE, SZ_IMPIXFILE);
	}
    if (strncmp(pixname, "HDR", 3) == 0 )
	same_path (pixname, hdrname);
    if ((bang = strchr (pixname, '!')) != NULL )
	hputs (fitsheader,"PIXFILE",bang+1);
    else
	hputs (fitsheader,"PIXFILE",pixname);
    free (pixname);
    fhead = fhead + 80;

    /* Save image offset from star of pixel file */
    pixoff = irafgeti4 (irafheader, impixoff);
    pixoff = (pixoff - 1) * 2;
    hputi4 (fitsheader, "PIXOFF", pixoff);
    fhead = fhead + 80;

    /* Save IRAF file format version in header */
    hputi4 (fitsheader,"IMHVER",imhver);
    fhead = fhead + 80;

    /* Save flag as to whether to swap IRAF data for this file and machine */
    if (machswap() != swapiraf)
	hputl (fitsheader, "SWAPIRAF", 1);
    else
	hputl (fitsheader, "SWAPIRAF", 0);
    fhead = fhead + 80;

    /* Add user portion of IRAF header to FITS header */
    fitsline[80] = 0;
    if (imhver == 2) {
	imu = LEN_IM2HDR;
	chead = (char *)irafheader;
	j = 0;
	for (k = 0; k < 80; k++)
	    fitsline[k] = ' ';
	for (i = imu; i < nbiraf; i++) {
	    irafchar = chead[i];
	    if (irafchar == 0)
		break;
	    else if (irafchar == 10) {
		(void)strncpy (fhead, fitsline, 80);
		/* printf ("%80s\n",fitsline); */
		j = 0;
		fhead = fhead + 80;
		for (k = 0; k < 80; k++)
		    fitsline[k] = ' ';
		}
	    else {
		if (j > 80) {
		    (void)strncpy (fhead, fitsline, 80);
		    /* printf ("%80s\n",fitsline); */
		    j = 9;
		    fhead = fhead + 80;
		    for (k = 0; k < 80; k++)
			fitsline[k] = ' ';
		    }
		if (irafchar > 32)
		    fitsline[j] = irafchar;
		j++;
		}
	    }
	}
    else {
	imu = LEN_IMHDR;
	chead = (char *) irafheader;
	if (swapiraf == 1)
	    ib = 0;
	else
	    ib = 1;
	for (k = 0; k < 80; k++)
	    fitsline[k] = ' ';
	j = 0;
	for (i = imu; i < nbiraf; i=i+2) {
	    irafchar = chead[i+ib];
	    if (irafchar == 0)
		break;
	    else if (irafchar == 10) {
		(void)strncpy (fhead, fitsline, 80);
		/* printf ("%80s\n",fitsline); */
		j = 0;
		fhead = fhead + 80;
		for (k = 0; k < 80; k++)
		    fitsline[k] = ' ';
		}
	    else {
		if (j > 80) {
		    (void)strncpy (fhead, fitsline, 80);
		    /* printf ("%80s\n",fitsline); */
		    j = 9;
		    fhead = fhead + 80;
		    for (k = 0; k < 80; k++)
			fitsline[k] = ' ';
		    }
		if (irafchar > 32)
		    fitsline[j] = irafchar;
		j++;
		}
	    }
	}

    /* Add END to last line */
    (void)strncpy (fhead, endline, 80);
    lfhead = fhead - fitsheader + 80;

    /* Find end of last 2880-byte block of header */
    nblock = lfhead / 2880;
    if (nblock*2880 < lfhead)
	nblock = nblock + 1;
    *nbfits = (nblock + 3) * 2880;
    fhead = ksearch (fitsheader, "END") + 80;
    fhead1 = fitsheader + *nbfits;

    /* Pad rest of header with spaces */
    strncpy (endline,"   ",3);
    for (fp = fhead1; fp < fhead; fp = fp + 80) {
	(void)strncpy (fp, endline,80);
	}

    return (fitsheader);
}


/* Put filename and header path together */

static void
same_path (pixname, hdrname)

char	*pixname;	/* IRAF pixel file pathname */
char	*hdrname;	/* IRAF image header file pathname */

{
    int len;
    char temp[SZ_IMPIXFILE];

    /* Pixel file is in same directory as header */
    if (strncmp(pixname, "HDR$", 4) == 0 ) {
	(void)strncpy (temp, &pixname[4], SZ_IMPIXFILE);
	(void)strncpy (pixname, hdrname, SZ_IMPIXFILE);

	/* find the end of the pathname */
	len = strlen(pixname);
#ifndef VMS
	while( (len > 0) && (pixname[len-1] != '/') )
#else
	while( (len > 0) && (pixname[len-1] != ']') && (pixname[len-1] != ':') )
#endif
      len--;

	/* add name */
	pixname[len] = '\0';
	(void)strncat(pixname, temp, SZ_IMPIXFILE);
	}

    /* Pixel file has same name as header file, but with .pix extension */
    else if (strncmp (pixname, "HDR", 3) == 0 ) {

	/* load entire header name string into name buffer */
	(void)strncpy (pixname, hdrname, SZ_IMPIXFILE);
	len = strlen (pixname);
	pixname[len-3] = 'p';
	pixname[len-2] = 'i';
	pixname[len-1] = 'x';
	}

    return;
}


int
irafgeti4 (irafheader, offset)

char	*irafheader;	/* IRAF image header */
int	offset;		/* Number of bytes to skip before number */

{
    char *ctemp;
    int  temp;

    ctemp = (char *) &temp;
    if ((int)irafheader[offset] > 0)
	swapiraf = 1;
    else
	swapiraf = 0;

    if (machswap() != swapiraf) {
	ctemp[3] = irafheader[offset];
	ctemp[2] = irafheader[offset+1];
	ctemp[1] = irafheader[offset+2];
	ctemp[0] = irafheader[offset+3];
	}
    else {
	ctemp[0] = irafheader[offset];
	ctemp[1] = irafheader[offset+1];
	ctemp[2] = irafheader[offset+2];
	ctemp[3] = irafheader[offset+3];
	}
    return (temp);
}


/* IRAFGETC2 -- Get character string from arbitrary part of v.1 IRAF header */

char *
irafgetc2 (irafheader, offset, nc)

char	*irafheader;	/* IRAF image header */
int	offset;		/* Number of bytes to skip before string */
int	nc;		/* Maximum number of characters in string */

{
    char *irafstring, *string;

    irafstring = irafgetc (irafheader, offset, 2*nc);
    string = iraf2str (irafstring, nc);

    return (string);
}


/* IRAFGETC -- Get character string from arbitrary part of IRAF header */

char *
irafgetc (irafheader, offset, nc)

char	*irafheader;	/* IRAF image header */
int	offset;		/* Number of bytes to skip before string */
int	nc;		/* Maximum number of characters in string */

{
    char *ctemp;
    int i;

    ctemp = (char *) malloc (nc+1);
    for (i = 0; i < nc; i++) {
	ctemp[i] = irafheader[offset+i];
	if (ctemp[i] > 0 && ctemp[i] < 32)
	    ctemp[i] = ' ';
	}

    return (ctemp);
}


/* Convert IRAF 2-byte/char string to 1-byte/char string */

char *
iraf2str (irafstring, nchar)

char	*irafstring;	/* IRAF 2-byte/character string */
int	nchar;		/* Number of characters in string */
{
    char *string;
    int i, j;

    /* Set swap flag according to position of nulls in 2-byte characters */
    if ((int)irafstring[0] != 0 && (int)irafstring[1] == 0)
	swapiraf = 1;
    else if ((int)irafstring[0] == 0 && (int)irafstring[1] != 0)
	swapiraf = 0;

    string = (char *) malloc (nchar+1);

    /* Swap bytes, if requested */
    if (swapiraf)
	j = 0;
    else
	j = 1;

    /* Convert appropriate byte of input to output character */
    for (i = 0; i < nchar; i++) {
	string[i] = irafstring[j];
	j = j + 2;
	}

    return (string);
}


/* IRAFSWAP -- Reverse bytes of any type of vector in place */

static void
irafswap (bitpix, string, nbytes)

int	bitpix;		/* Number of bits per pixel */
			/*  16 = short, -16 = unsigned short, 32 = int */
			/* -32 = float, -64 = double */
char	*string;	/* Address of starting point of bytes to swap */
int	nbytes;		/* Number of bytes to swap */

{
    switch (bitpix) {

	case 16:
	    if (nbytes < 2) return;
	    irafswap2 (string,nbytes);
	    break;

	case 32:
	    if (nbytes < 4) return;
	    irafswap4 (string,nbytes);
	    break;

	case -16:
	    if (nbytes < 2) return;
	    irafswap2 (string,nbytes);
	    break;

	case -32:
	    if (nbytes < 4) return;
	    irafswap4 (string,nbytes);
	    break;

	case -64:
	    if (nbytes < 8) return;
	    irafswap8 (string,nbytes);
	    break;

	}
    return;
}


/* IRAFSWAP2 -- Swap bytes in string in place */

static void
irafswap2 (string,nbytes)


char *string;	/* Address of starting point of bytes to swap */
int nbytes;	/* Number of bytes to swap */

{
    char *sbyte, temp, *slast;

    slast = string + nbytes;
    sbyte = string;
    while (sbyte < slast) {
	temp = sbyte[0];
	sbyte[0] = sbyte[1];
	sbyte[1] = temp;
	sbyte= sbyte + 2;
	}
    return;
}


/* IRAFSWAP4 -- Reverse bytes of Integer*4 or Real*4 vector in place */

static void
irafswap4 (string,nbytes)

char *string;	/* Address of Integer*4 or Real*4 vector */
int nbytes;	/* Number of bytes to reverse */

{
    char *sbyte, *slast;
    char temp0, temp1, temp2, temp3;

    slast = string + nbytes;
    sbyte = string;
    while (sbyte < slast) {
	temp3 = sbyte[0];
	temp2 = sbyte[1];
	temp1 = sbyte[2];
	temp0 = sbyte[3];
	sbyte[0] = temp0;
	sbyte[1] = temp1;
	sbyte[2] = temp2;
	sbyte[3] = temp3;
	sbyte = sbyte + 4;
	}

    return;
}


/* IRAFSWAP8 -- Reverse bytes of Real*8 vector in place */

static void
irafswap8 (string,nbytes)

char *string;	/* Address of Real*8 vector */
int nbytes;	/* Number of bytes to reverse */

{
    char *sbyte, *slast;
    char temp[8];

    slast = string + nbytes;
    sbyte = string;
    while (sbyte < slast) {
	temp[7] = sbyte[0];
	temp[6] = sbyte[1];
	temp[5] = sbyte[2];
	temp[4] = sbyte[3];
	temp[3] = sbyte[4];
	temp[2] = sbyte[5];
	temp[1] = sbyte[6];
	temp[0] = sbyte[7];
	sbyte[0] = temp[0];
	sbyte[1] = temp[1];
	sbyte[2] = temp[2];
	sbyte[3] = temp[3];
	sbyte[4] = temp[4];
	sbyte[5] = temp[5];
	sbyte[6] = temp[6];
	sbyte[7] = temp[7];
	sbyte = sbyte + 8;
	}
    return;
}

static int
machswap ()

{
    char *ctest;
    int itest;

    itest = 1;
    ctest = (char *)&itest;
    if (*ctest)
        return (1);
    else
        return (0);
}

/*
 * Feb 15 1996	New file
 * Apr 10 1996	Add more documentation
 * Apr 17 1996	Print error message on open failure
 * Jun  5 1996	Add byte swapping (reversal); use streams
 * Jun 10 1996	Make fixes after running lint
 * Jun 12 1996	Use IMSWAP subroutines instead of local ones
 * Jul  3 1996	Go back to using local IRAFSWAP subroutines
 * Jul  3 1996	Write to pixel file from FITS header
 * Jul 10 1996	Allocate all headers
 * Aug 13 1996	Add unistd.h to include list
 * Aug 26 1996	Allow 1-d images; fix comments; fix arguments after lint
 * Aug 26 1996	Add IRAF header lingth argument to IRAFWIMAGE and IRAFWHEAD
 * Aug 28 1996	Clean up code in IRAF2FITS
 * Aug 30 1996	Use write instead of fwrite
 * Sep  4 1996	Fix write mode bug
 * Oct 15 1996	Drop unused variables
 * Oct 17 1996	Minor fix after lint; cast arguments to STR2IRAF
 *
 * May 15 1997	Fix returned header length in IRAF2FITS
 * Dec 15 1997	Add IRAF version 2 .imh files
 *
 * Jan  2 1998	Allow uneven length of user parameter lines in IRAF 2.11 headers
 * Jan 13 1998	Allow uneven length of user parameter lines in IRAF 2.10 headers
 * Jan 14 1998	Fix byte swapping so files can be read on any machine
 * Apr 15 1998	Declare irafswap subroutines static
 * Apr 17 1998	Add data type values for unsigned byte and unsigned short

 * Jul 20 2000	Drop unused variables
 */
