/* File getfits.c
 * September 17, 2013
 * By Jessica Mink, Harvard-Smithsonian Center for Astrophysics
 * Send bug reports to jmink@cfa.harvard.edu

   Copyright (C) 2002-2013
   Smithsonian Astrophysical Observatory, Cambridge, MA USA

   This program is free software; you can redistribute it and/or
   modify it under the terms of the GNU General Public License
   as published by the Free Software Foundation; either version 2
   of the License, or (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software Foundation,
   Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>
#include <math.h>
#include "libwcs/fitswcs.h"
#include "libwcs/wcscat.h"

#define OFFSCL_BUGFIX
#define MAXRANGE 20
#define MAXFILES 2000
#define SUPPORT_PHYSICAL 1 /* Support the WCSNAMEP = "PHYSICAL" keyword extraction format */
#define CRPIX_OFFSET 1     /* WCSNAMEP = PHYSICAL offset when rewriting the crpix<n> keywords */
#define CRVAL_OFFSET 0     /* WCSNAMEP = PHYSICAL offset when writing the crval<n>p keywords */
#define TANPV  1           /* Bugfix for the SCAMP polynomial fit */
static int maxnfile = MAXFILES;

#define MAXKWD 500
static int maxnkwd = MAXKWD;

static void usage();
static void nextname();	/* Find next available name (namea, nameb, ...) */
static int ExtractFITS();

static int verbose = 0;		/* verbose/debugging flag */
#ifdef SUPPORT_PHYSICAL
static int physicalFlag = 0; /* keyword protocol.  0 =  WCSNAMEP = 'PLATE' 1 =  WCSNAMEP = 'PHYSICAL' */
#endif /* SUPPORT_PHYSICAL */
static char *RevMsg = "GETFITS WCSTools 3.9.2, 15 May 2015, Jessica Mink (jmink@cfa.harvard.edu)";
static int version = 0;		/* If 1, print only program name and version */
static char outname[128];	/* Name for output image */
static char outdir[256];	/* Output directory pathname */
static int first = 1;
static int nlog = 100;
static int xcpix = 0;
static int ycpix = 0;
static int xdpix = 0;
static int ydpix = 0;
static char *rrange;	/* Row range string */
static char *crange;	/* Column range string */
static double ra0 = -99.0;	/* Initial center RA in degrees */
static double dec0 = -99.0;	/* Initial center Dec in degrees */
static int syscoor = 0;		/* Input search coordinate system */
static double eqcoor = 0.0;	/* Input search coordinate system */

int
main (ac, av)
int ac;
char **av;
{			
    char *str;
    char *listfile = NULL;
    char **fn;
    char filename[256];
    char temp[80];
    int ifile, nfile, nbytes;
    FILE *flist;
    char *nextarg;
    char rastr[32], decstr[32];
    int nkwd = 0;		/* Number of keywords to delete */
    int nkwd1 = 0;		/* Number of keywords in delete file */
    char **kwd = NULL;		/* List of keywords to be deleted */
    char **kwdnew;
    int ikwd;
    FILE *fdk;
    char *klistfile;

    crange = NULL;
    rrange = NULL;
    outname[0] = 0;
    outdir[0] = 0;
    nfile = 0;
    fn = (char **)calloc (maxnfile, sizeof(char *));
    klistfile = NULL;

    /* Check for help or version command first */
    str = *(av+1);
    if (!str || !strcmp (str, "help") || !strcmp (str, "-help"))
	usage("");
    if (!strcmp (str, "version") || !strcmp (str, "-version")) {
	version = 1;
	usage("");
	}

    /* crack arguments */
    for (av++; --ac > 0; av++) {

	/* Set center RA, Dec, and equinox if colon in argument */
	if (strsrch (*av,":") != NULL) {
	    if (ac < 2)
		usage ("Right ascension given but no declination");
	    else {
		strcpy (rastr, *av);
    ac--;
		strcpy (decstr, *++av);
		ra0 = str2ra (rastr);
		dec0 = str2dec (decstr);
		ac--;
		if (ac < 1) {
		    syscoor = WCS_J2000;
		    eqcoor = 2000.0;
		    }
		else if ((syscoor = wcscsys (*(av+1))) >= 0) {
		    eqcoor = wcsceq (*++av);
		    ac--;
		    }
		else {
		    syscoor = WCS_J2000;
		    eqcoor = 2000.0;
		    }
		xcpix = -1;
		ycpix = -1;
		}
	    }

	/* Read command */
	else if (*(str = *av) == '-') {
	    char c;
	    while ((c = *++str)) {
		switch (c) {

		case 'v':	/* more verbosity */
		    verbose++;
		    break;

#ifdef SUPPORT_PHYSICAL
		case 'p':	/* WCSNAMEP = "PHYSICAL" keyword format */
		    physicalFlag++;
		    break;
#endif /* SUPPORT_PHYSICAL */

		case 'c':	/* center coordinates for extraction */
		    if (ac < 3)
			usage ("-c needs two arguments");
		    xcpix = atoi (*++av);
		    ac--;
		    ycpix = atoi (*++av);
		    ac--;
		    break;

		case 'd':	/* output directory */
		    if (ac < 2)
			usage ("-d needs a directory");
		    strcpy (outdir, *++av);
		    ac--;
		    break;

		case 'k':	/* Read keywords to delete from this file */
		    if (ac < 2)
			usage ("-k needs a file of keywords");
		    klistfile = *av++;
		    nkwd1 = getfilelines (klistfile);
		    if (nkwd1 + nkwd > maxnkwd) {
			maxnkwd = maxnkwd + nkwd1;
			kwdnew = (char **)calloc (maxnkwd, sizeof(char *));
			if (nkwd > 0) {
			    for (ikwd = 0; ikwd < nkwd; ikwd++)
				kwdnew[ikwd] = kwd[ikwd];
			    free (kwd);
			    }
			kwd = kwdnew;
			}
		    if ((fdk = fopen (klistfile, "r")) == NULL) {
			fprintf (stderr,"GETFITS: File %s cannot be read\n",
				 klistfile);
			}
		    else {
			for (ikwd = nkwd; ikwd < nkwd+nkwd1; ikwd++) {
			    kwd[ikwd] = (char *) calloc (32, 1);
			    first_token (fdk, 31, kwd[ikwd]);
			    }
			fclose (fdk);
			}
		    ac--;
		    break;

		case 'i':	/* logging interval */
		    if (ac < 2)
			usage ("-i needs an argument");
		    nlog = atoi (*++av);
		    ac--;
		    break;

		case 'o':	/* output file name */
		    if (ac < 2)
			usage ("-o needs an argument");
		    strcpy (outname, *++av);
		    ac--;
		    break;

		case 's':	/* output to stdout */
		    strcpy (outname, "stdout");
		    break;

		case 'x':	/* width and height for extraction */
		    if (ac < 2)
			usage ("-x needs at least one argument");
		    xdpix = atoi (*++av);
		    ac--;
		    if (ac > 1) {
			nextarg = *(av+1);
			if (isnum (nextarg)) {
			    ydpix = atoi (nextarg);
			    av++;
			    ac--;
			    }
			else
			    ydpix = xdpix;
			}
		    else
			ydpix = xdpix;
		    break;

	        default:
		    sprintf (temp, "Illegal argument '%c'", c);
		    usage(temp);
		    break;
		}
		}
    	    }

        /* Center or center and size of section to extract */
        else if (isnum (*av)) {
	    if (ac > 2 && isnum (*(av+1)) && (syscoor = wcscsys (*(av+2))) > 0) {
		ra0 = str2ra (*av++);
		ac--;
		dec0 = str2dec (*av++);
		ac--;
		eqcoor = wcsceq (*av);
		xcpix = -1;
		ycpix = -1;
		}
	    else {
		if (!xcpix)
		    xcpix = atoi (str);
		else if (!ycpix)
		    ycpix = atoi (str);
		else if (!xdpix)
		    xdpix = atoi (str);
		else if (!ydpix)
		    ydpix = atoi (str);
		}
	    }

        /* Range of pixels to extract */
        else if (isrange (*av)) {
	    if (crange == NULL)
		crange = str;
	    else
		rrange = str;
	    }

	/* File with list of image files */
 	else if (*av[0] == '@') {
	    listfile = *av + 1;
	    }

        /* Image file */
        else if (isfits (*av) || isiraf (*av)) {
            if (nfile >= maxnfile) {
                maxnfile = maxnfile * 2;
                nbytes = maxnfile * sizeof (char *);
                fn = (char **) realloc ((void *)fn, nbytes);
                }
            fn[nfile] = *av;
            nfile++;
            }
	}

    /* If center is set, but not size, extract 500x500 image */
    if (!crange) {
	if (xcpix && ycpix && xdpix == 0)
	    xdpix = 500;
	if (xcpix && ycpix && ydpix == 0)
	    ydpix = xdpix;
	}

    /* If one side is set, set the other */
    if (xdpix && !ydpix)
	ydpix = xdpix;

    /* Process files from listfile one at a time */
    if (listfile && isimlist (listfile)) {
	nfile = getfilelines (listfile);
	if ((flist = fopen (listfile, "r")) == NULL) {
	    fprintf (stderr,"GETFITS: Image list file %s cannot be read\n",
		     listfile);
	    usage ();
	    }
	for (ifile = 0; ifile < nfile; ifile++) {
	    first_token (flist, 254, filename);
	    (void) ExtractFITS (filename,kwd,nkwd);
	    }
	fclose (flist);
	}

    /* Process files from command line */
    else if (fn) {
	for (ifile = 0; ifile < nfile; ifile++) {
	    (void) ExtractFITS (fn[ifile],kwd,nkwd);
	    }
	}

    return (0);
}

static void
usage (errmsg)

char *errmsg;	/* Error message */
{
    fprintf (stderr,"%s\n",RevMsg);
    if (version)
	exit (-1);
    if (*errmsg)
	fprintf (stderr, "*** %s ***\n", errmsg);
    fprintf (stderr,"Extract FITS files from FITS image files\n");
    fprintf(stderr,"Usage: getfits -sv [-i num] [-o name] [-d dir] file.fits xrange yrange...\n");
    fprintf(stderr,"  or : getfits -sv [-i num] [-o name] [-d dir] file.fits [x y dx [dy] ...\n");
    fprintf(stderr,"  or : getfits [-sv1][-i num][-o name] [-d path] @fitslist xrange yrange\n");
    fprintf(stderr,"  or : getfits [-sv1][-i num][-o name] [-d path] @fitslist x y dx [dy]\n");
    fprintf(stderr,"  xrange: Columns to extract in format x1-x2\n");
    fprintf(stderr,"  yrange: Rows to extract in format y1-y2\n");
    fprintf(stderr,"  x y: Center pixel of region to extract\n");
    fprintf(stderr,"       column row or\n");
    fprintf(stderr,"       hh:mm:ss dd:mm:ss system (sky coordinates)\n");
    fprintf(stderr,"  dx dy: Width and height in pixels of region to extract\n");
    fprintf(stderr,"         (Height is same as width if omitted)\n");
    fprintf(stderr,"  -d dir: write FITS file(s) to this directory\n");
    fprintf(stderr,"  -i num: log rows as they are copied at this interval\n");
    fprintf(stderr,"  -k file: file of keyword names to delete from output header\n");
    fprintf(stderr,"  -o name: output name for one file\n");
    fprintf(stderr,"  -s: write output to standard output\n");
    fprintf(stderr,"  -x dx dy: dimensions of image section to be extracted\n");
    fprintf(stderr,"  -v: verbose\n");
#ifdef SUPPORT_PHYSICAL
    fprintf(stderr,"  -p: support  WCSNAMEP = 'PHYSICAL' instead of WCSNAMEP = 'PLATE' format \n");
#endif /* SUPPORT_PHYSICAL */
    exit (1);
}

static int
ExtractFITS (name, kwd, nkwd)

     char	*name;
     char	**kwd;
     int	nkwd;
{
  char *header;	/* FITS header */
  int lhead;		/* Maximum number of bytes in FITS header */
  int nbhead;		/* Actual number of bytes in FITS header */
  char history[128];	/* for HISTORY line */
  char fitsname[256];	/* Name of FITS file */
  char fitspath[256];	/* Pathname of FITS file  */
  char *fitsfile;
  double crpix1, crpix2;
  double cxpix, cypix;
  double ra, dec;
  double bzero, bscale, dmin, dmax, drange;
  int offscl;
  int wp;             /* Image width in pixels (returned) */
  int hp;             /* Image height in pixels (returned) */
  double eqout=0.0;   /* Equinox to return (0=image, returned) */
  int ifrow1, ifrow2;	/* First and last rows to extract */
  int ifcol1, ifcol2;	/* First and last columns to extracT */
  int npimage;        /* Number of pixels in image */
  int nbimage;        /* Number of bytes in image */
  int ikwd;
  char *endchar;
  char *ltime;
  char *newimage;
  char *kw, *kwl;
  int bitpix;
  int bytepix;
  int nblock, nbleft;
  int nrows, ncols;
  char rastr[32], decstr[32], cstr[32];
  char temp[80];
  int xdim = 1;
  int ydim = 1;
  int historyHeader = 0;
  int istnx = 0;
  struct Range *xrange;    /* Column range structure */
  struct Range *yrange;    /* Row range structure */
#ifdef WCSDEP_BUGFIX
  struct WorldCoor *wcs = NULL;
  struct WorldCoor  *GetWCSFITS();
#else /* WCSDEP_BUGFIX */
  struct WorldCoor *wcs, *GetWCSFITS();
#endif /* WCSDEP_BUGFIX */

  /* Check to see if this is a FITS file */
  if (!isfits (name)) {
    fprintf (stderr, "File %s is not a FITS file\n", name);
    return (-1);
	}

  /* Read FITS header */
  if ((header = fitsrhead (name, &lhead, &nbhead)) == NULL) {
    fprintf (stderr, "Cannot read FITS file %s\n", name);
    return (-1);
	}

  /* If requested, delete keywords one at a time */
  for (ikwd = 0; ikwd < nkwd; ikwd++) {

    /* Make keyword all upper case */
    kwl = kwd[ikwd] + strlen (kwd[ikwd]);
    for (kw = kwd[ikwd]; kw < kwl; kw++) {
	    if (*kw > 96 && *kw < 123)
        *kw = *kw - 32;
    }

    /* Delete keyword */
    if (hdel (header, kwd[ikwd]) && verbose)
	    printf ("%s: %s deleted\n", name, kwd[ikwd]);
	}


  if (verbose && first) {
    fprintf (stderr,"%s\n",RevMsg);
    fprintf (stderr, "Extract from FITS image file %s\n", name);
	}

#ifdef WCSDEP_BUGFIX
  wcs = GetWCSFITS (name, header, verbose);
#endif /* WCSDEP_BUGFIX */
  if (ra0 > -99.0 && dec0 > -99.0) {
#ifndef WCSDEP_BUGFIX
    wcs = GetWCSFITS (name, verbose);
#endif /* WCSDEP_BUGFIX */
    if (wcs->lngcor != NULL) {
	    istnx = 1;
    }
    if (iswcs (wcs)) {
	    ra = ra0;
	    dec = dec0;
	    wcscon (syscoor, wcs->syswcs, eqcoor, eqout, &ra, &dec, wcs->epoch);
	    wcs2pix (wcs, ra, dec, &cxpix, &cypix, &offscl);
#ifdef OFFSCL_BUGFIX
      if (offscl != 0) {
        fprintf (stderr, "Image is offscale in FITS image file %s\n", name);
        wcsfree (wcs);
        return (-1);
      }        
#endif /* OFFSCL_BUGFIX */
	    xcpix = (int) (cxpix + 0.5);
	    ycpix = (int) (cypix + 0.5);
#ifndef WCSDEP_BUGFIX 
	    wcsfree (wcs);
#endif /* WCSDEP_BUGFIX */
    }
    else {
	    fprintf (stderr, "No WCS in FITS image file %s\n", name);
	    wcsfree (wcs);
	    return (-1);
    }
	}

  ncols = 0;
  (void) hgeti4 (header, "NAXIS1", &ncols);
  nrows = 1;
  (void) hgeti4 (header, "NAXIS2", &nrows);
  bitpix = 16;
  (void) hgeti4 (header, "BITPIX", &bitpix);
  if (bitpix < 0)
    bytepix = -bitpix / 8;
  else
    bytepix = bitpix / 8;

  /* Set up limiting rows to read */
  if (rrange) {
    yrange = RangeInit (rrange, ydim);
    ifrow1 = yrange->ranges[0];
    ifrow2 = yrange->ranges[1];
	}
  else if (xcpix && ycpix) {
    int ydpix2 = ydpix / 2;
    ifrow1 = ycpix - ydpix2 + 1;
    ifrow2 = ifrow1 + ydpix - 1;
	}
  else{
    ifrow1 = 1;
    ifrow2 = nrows;
	}

  /* Return with error if both first and last row off image */
  if ((ifrow1 < 1 && ifrow2 < 1) ||
      (ifrow1 > nrows && ifrow2 > nrows)) {
    fprintf (stderr,"GETFITS: Rows %d-%d off image %s\n",
             ifrow1, ifrow2, name);
    return (1);
	}

  if (ifrow1 < 1)
    ifrow1 = 1;
  if (ifrow2 < 1)
    ifrow2 = 1;
  if (ifrow1 > nrows)
    ifrow1 = nrows;
  if (ifrow2 > nrows)
    ifrow2 = nrows;
  hp = ifrow2 - ifrow1 + 1;

  /* Set up first and number of columns to write */
  if (crange) {
    xrange = RangeInit (crange, xdim);
    ifcol1 = xrange->ranges[0];
    ifcol2 = xrange->ranges[1];
	}
  else if (xdpix) {
    int xdpix2 = xdpix / 2;
    ifcol1 = xcpix - xdpix2 + 1;
    ifcol2 = ifcol1 + xdpix - 1;
	}
  else {
    ifcol1 = 1;
    ifcol2 = ncols;
	}

  /* Return with error if both first and last column off image */
  if ((ifcol1 < 1 && ifcol2 < 1) ||
      (ifcol1 > ncols && ifcol2 > ncols)) {
    fprintf (stderr,"GETFITS: Columns %d-%d off image %s\n",
             ifcol1, ifcol2, name);
    return (1);
	}

  if (ifcol1 < 1)
    ifcol1 = 1;
  if (ifcol2 < 1)
    ifcol2 = 1;
  if (ifcol1 > ncols)
    ifcol1 = ncols;
  if (ifcol2 > ncols)
    ifcol2 = ncols;
  wp = ifcol2 - ifcol1 + 1;

  /* Extract image */
  newimage = fitsrsect (name, header, nbhead, ifcol1, ifrow1, wp, hp, nlog);

  hputi4 (header, "NAXIS", 2);
  hputi4 (header, "NAXIS1", wp);
  hputi4 (header, "NAXIS2", hp);
  hdel (header, "NAXIS3");
  hdel (header, "NAXIS4");
  hdel (header, "NAXIS5");
  npimage = wp * hp;
  nbimage = npimage * bytepix;
  nblock = nbimage / 2880;
  nbleft = (nblock + 1) * 2880 - nbimage;
  if (nbleft > 2880)
    nbleft = 0;

  /* Set data limits for scaling when converting image formats */
  bscale = 1.0;
  hgetr8 (header, "BSCALE", &bscale);
  bzero = 0.0;
  hgetr8 (header, "BZERO", &bzero);
  dmin = minvec (newimage, bitpix, bzero, bscale, 0, npimage);
  dmax = maxvec (newimage, bitpix, bzero, bscale, 0, npimage);
  drange = dmax - dmin;
  dmin = dmin - (0.2 * drange);
  dmax = dmax + (0.2 * drange);
  hputnr8 (header, "DATAMIN", 0, dmin);
  hputnr8 (header, "DATAMAX", 0, dmax);

  /* Reset image WCS if present */

  if (ifcol1 > 1 || ifrow1 > 1) {
    /* If IRAF TNX image, keep all WCS keywords, but add dependency on PLATE WCS */
#ifdef TANPV
    if ((istnx) || 
        (iswcs(wcs) && ((wcs->projppv[1+wcs->wcsl.lng*MAXPV] != 0) ||
                        (wcs->projppv[1+wcs->wcsl.lat*MAXPV] != 0))))
#else /* TANPV */
      if (istnx)
#endif /* TANPV */
        {
#ifdef SUPPORT_PHYSICAL
          if (physicalFlag != 0) {
            hputs (header, "WCSNAMEP", "PHYSICAL");
            hputi4 (header, "WCSAXESP",2);
          } else {
            hputs (header, "WCSDEP", "PLATE");
          }
#else /* SUPPORT_PHYSICAL */
          hputs (header, "WCSDEP", "PLATE");
#endif /* SUPPORT_PHYSICAL */
        }

    /* Otherwise, reset reference pixel coordinate */
      else {
        if (hgetr8 (header, "CRPIX1", &crpix1)) {
          crpix1 = crpix1 - (double) (ifcol1 - 1);
          hputr8 (header, "CRPIX1", crpix1);
        }
        else {
          hputs (header, "CTYPE1", "PIXEL");
          hputi4 (header, "CRPIX1", 1);
          hputi4 (header, "CRVAL1", ifcol1);
          hputi4 (header, "CDELT1", 1);
        }
        if (hgetr8 (header, "CRPIX2", &crpix2)) {
          crpix2 = crpix2 - (double) (ifrow1 - 1);
          hputr8 (header, "CRPIX2", crpix2);
        }
        else {
          hputs (header, "CTYPE2", "PIXEL");
          hputi4 (header, "CRPIX2", 1);
          hputi4 (header, "CRVAL2", ifrow1);
          hputi4 (header, "CDELT2", 1);
        }
      }

    /* Set up output pixel to original pixel transformation */
    if (hgetr8 (header, "CRPIX1P", &crpix1)) {
	    crpix1 = crpix1 - (double) (ifcol1 - 1);
	    hputr8 (header, "CRPIX1P", crpix1);
    }
    else {
#ifdef SUPPORT_PHYSICAL
      if (physicalFlag == 1) { 
        if (hgetr8 (header, "CRPIX1", &crpix1)) {
          crpix1 = crpix1 - (double) (ifcol1 - CRPIX_OFFSET);
          hputr8 (header, "CRPIX1", crpix1);
        }
        hputs (header, "CTYPE1P", "RAWX");
        hputs (header, "CUNIT1P", "PIXEL");
        hputi4 (header, "CRPIX1P", 1);
        hputi4 (header, "CRVAL1P", ifcol1 - CRVAL_OFFSET);
        hputr8 (header, "CDELT1P", 1.0);
      } else 
#endif /* SUPPORT_PHYSICAL */
        {
          hputs (header, "WCSNAMEP", "PLATE");
          hputs (header, "CTYPE1P", "PIXEL");
          hputi4 (header, "CRPIX1P", 1);
          hputi4 (header, "CRVAL1P", ifcol1);
          hputi4 (header, "CDELT1P", 1);  
        }
    }
    if (hgetr8 (header, "CRPIX2P", &crpix2)) {
	    crpix2 = crpix2 - (double) (ifrow1  -1);
	    hputr8 (header, "CRPIX2P", crpix2);
    }
    else {
#ifdef SUPPORT_PHYSICAL     
      if (physicalFlag == 1) {
        if (hgetr8 (header, "CRPIX2", &crpix2)) {
          crpix2 = crpix2 - (double) (ifrow1 - CRPIX_OFFSET);
          hputr8 (header, "CRPIX2", crpix2);
        }
        hputs (header, "CTYPE2P", "RAWX");
        hputs (header, "CUNIT2P", "PIXEL");
        hputi4 (header, "CRPIX2P", 1);
        hputi4 (header, "CRVAL2P", ifrow1 - CRVAL_OFFSET);
        hputr8 (header, "CDELT2P", 1.0);
      } else 
#endif /* SUPPORT_PHYSICAL */        
        {
          hputs (header, "CTYPE2P", "PIXEL");
          hputi4 (header, "CRPIX2P", 1);
          hputi4 (header, "CRVAL2P", ifrow1);
          hputi4 (header, "CDELT2P", 1);
        }
    }
	}

  /* Add HISTORY notice of this conversion */
  if (ra0 > -99.0 && dec0 > -99.0) {
    strcpy (history, RevMsg);
    endchar = strchr (history, ',');
    *endchar = (char) 0;
    strcat (history, " ");
    ltime = lt2fd ();
    strcat (history, ltime);
    endchar = strrchr (history,':');
    *endchar = (char) 0;
    hputc (header, "HISTORY", history);
    historyHeader = 1;
    ra2str (rastr, 31, ra0, 3);
    dec2str (decstr, 31, dec0, 2);
    wcscstr (cstr, syscoor, eqcoor, 0.0);
    sprintf (history, " %d x %d centered on %s %s %s",
             xdpix, ydpix, rastr, decstr, cstr);
    hputc (header, "HISTORY", history);
	}
  if (xcpix && ycpix) {
    if (historyHeader == 0) {
      strcpy (history, RevMsg);
      endchar = strchr (history, ',');
      *endchar = (char) 0;
      strcat (history, " ");
      ltime = lt2fd ();
      strcat (history, ltime);
      endchar = strrchr (history,':');
      *endchar = (char) 0;
      hputc (header, "HISTORY", history);
      historyHeader = 1;
    }
#ifdef SUPPORT_PHYSICAL
    if (physicalFlag == 0) {
      sprintf (history, " %d x %d centered on [%d,%d] (PLATE)",
               xdpix, ydpix, xcpix, ycpix);
    } else
#endif /* SUPPORT_PHYSICAL */
      {
        sprintf (history, " %d x %d centered on [%d,%d] (PHYSICAL)",
                 xdpix, ydpix, xcpix, ycpix);
      }
    hputc (header, "HISTORY", history);
	}
  else if (crange && rrange) {
    if (historyHeader == 0) {
      strcpy (history, RevMsg);
      endchar = strchr (history, ',');
      *endchar = (char) 0;
      strcat (history, " ");
      ltime = lt2fd ();
      strcat (history, ltime);
      endchar = strrchr (history,':');
      *endchar = (char) 0;
      hputc (header, "HISTORY", history);
      historyHeader = 1;
    }


    sprintf (history, " Rows %d - %d, Columns %d - %d",
             ifrow1, ifrow2, ifcol1, ifcol2);
    hputc (header, "HISTORY", history);
	}

  /* If assigned output name, use it */
  if (outname[0] != (char) 0)
    strcpy (fitsname, outname);

  /* Otherwise, create a new name */
  else
    nextname (name, fitsname);

  /* Write FITS file to a specified directory */
  if (outdir[0] > 0) {
    strcpy (fitspath, outdir);
    strcat (fitspath, "/");
    fitsfile = strrchr (fitsname,'/');
    if (fitsfile == NULL)
	    strcat (fitspath, fitsname);
    else
	    strcat (fitspath, fitsfile+1);
	}
  else
    strcpy (fitspath, fitsname);

  if (fitswimage (fitspath, header, newimage) > 0) {
    if (verbose)
	    fprintf (stderr, "%s: written successfully.\n", fitspath);
    else
	    printf ("%s\n", fitspath);
	}
  else if (verbose)
    fprintf (stderr, "NEWFITS: File %s not written.\n", fitspath);

  if (verbose)
    fprintf (stderr, "\n");
  free (newimage);
  free (header);
  return (0);
}

static void
nextname (name, newname)

char *name;
char *newname;
{
    char *ext, *sufchar;
    int lname;

    ext = strrchr (name, '.');
    if (ext)
	lname = ext - name;
    else
	lname = strlen (name);
    strncpy (newname, name, lname);
    sufchar = newname + lname;
    *sufchar = 'a';
    *(sufchar+1) = (char) 0;
    strcat (newname, ext);
    while (!access (newname, F_OK)) {
	if (*sufchar < 'z')
	    (*sufchar)++;
	else {
	    *sufchar = 'a';
	    sufchar++;
	    *sufchar = 'a';
	    *(sufchar+1) = (char) 0;
	    strcat (newname, ext);
	    }
	}
    return;
}

/* Oct 22 2002	New program based on t2f
 * Dec  6 2002	Initialize bytepix, which wasn't
 * Dec 16 2002	Add -k option to delete FITS keywords when copying
 *
 * Jan 30 2003	Fix typo in variable name 
 * May  2 2003	Fix bug if no keywords are deleted
 *
 * Apr 16 2004	Delete NAXISn for n > 2 in output image
 * Sep 15 2004	Fix bug dealing with center specified as sky coordinates
 * Sep 17 2004	Add option to set extraction center as decimal degrees
 * Dec  6 2004	Don't print gratuitous newline at end of process
 *
 * Sep 30 2005	Convert input center coordinates to image system
 *
 * Jun 21 2006	Clean up code
 *
 * Jan 10 2007	Use range subroutines in library
 * Jun 11 2007	Compute minimum and maximum data values in output image
 * Sep  2 2007  Avoid HISTORY strings larger than 80 characters
 * Apr  4 2008	Make extracted TNX WCS dependent on original PLATE WCS
 *
 * Apr 10 2010	Fix bug so WCS is not accessed after it is freed
 *
 * Jan 22 2012	Print error, not blank file, if requested region is off image
 * Sep 17 2013	Include fitswcs.h
 * Feb 25 2020  Add '-p' qualifier for "PHYSICAL" keyword support. New code qualified by SUPPORT_PHYSICAL
 */
