/*****************************************************************************
******************************************************************************
**<AUTO>
**
** FILE:	tclCatalogs.c
**
**<HTML>
**	This file contains TCL functions read an delete FIRST and ROSAT
**	catalogs.
**</HTML>
**</AUTO>
**
**
** ENVIRONMENT:
**	ANSI C.
**
******************************************************************************
******************************************************************************
*/

#include "taCatalogs.h"
#include "taCalibObj.h"

static int versionStringLength = 80;   /* Maximum length of version strings */

#define N_ROSAT_FIELDS 14
static char *rosatFieldName[N_ROSAT_FIELDS] = {
  "id",
  "lambda",
  "eta",
  "posErr",
  "cps",
  "cpserr",
  "hr1",
  "hr1Err",
  "hr2",
  "hr2Err",
  "extent",
  "extentLike",
  "detectLike",
  "exposure"
};
enum rosatFieldId {
  ROSAT_ID,
  ROSAT_LAMBDA,
  ROSAT_ETA,
  POSERR,
  CPS,
  CPSERR,
  HR1,
  HR1ERR,
  HR2,
  HR2ERR,
  EXT,
  EXTLIKE,
  DETECTLIKE,
  EXPOSURE
};

#define N_FIRST_FIELDS 10
static char *firstFieldName[N_FIRST_FIELDS] = {
  "id",
  "lambda",
  "eta",
  "wflag",
  "fpeak",
  "fint",
  "rms",
  "maj",
  "min",
  "pa"
};
enum firstFieldId {
  FIRST_ID,
  FIRST_LAMBDA,
  FIRST_ETA,
  WFLAG,
  FPEAK,
  FINT,
  RMS,
  MAJ,
  MIN,
  PA
};


/********************************************************************/
/* qsort helper function. Final list is ordered from smallest to largest */
static int rosatCmpLambda (const void *d1, const void *d2)
{
  TA_ROSAT *f1, *f2;
  f1 = (TA_ROSAT *)d1;
  f2 = (TA_ROSAT *)d2;
  if (f1->lambda < f2->lambda) return -1;
  if (f1->lambda > f2->lambda) return 1;
  return 0;
}

/********************************************************************/
/* qsort helper function. Final list is ordered from smallest to largest */
static int firstCmpLambda (const void *d1, const void *d2)
{
  TA_FIRST *f1, *f2;
  f1 = (TA_FIRST *)d1;
  f2 = (TA_FIRST *)d2;
  if (f1->lambda < f2->lambda) return -1;
  if (f1->lambda > f2->lambda) return 1;
  return 0;
}


/********************************************************************/
/* qsort helper function. Final list is ordered from smallest to largest */
static int usnoCmpLambda (const void *d1, const void *d2)
{
  TA_USNO *f1, *f2;
  f1 = (TA_USNO *)d1;
  f2 = (TA_USNO *)d2;
  if (f1->lambda < f2->lambda) return -1;
  if (f1->lambda > f2->lambda) return 1;
  return 0;
}


/*============================================================================
**<AUTO EXTRACT>
**
** TCL VERB: rosatRead
**
**<HTML>
**	Read a ROSAT catalog into a TA_ROSAT_CATALOG structure.  Returns a
**	handle to the new catalog structure.
**	The catalog will be sorted by increasing lambda.
**</HTML>
**</AUTO>
**============================================================================
*/
static char rosatReadCmd[] = "rosatRead";
static int rosatReadFlg = FTCL_ARGV_NO_LEFTOVERS;
static ftclArgvInfo rosatReadTbl[] = {
   {NULL, FTCL_ARGV_HELP, NULL, NULL,
    "Read a ROSAT catalog\n"},
   {"[file]", FTCL_ARGV_STRING, "/data/dp1.16/data/rosat/rosat.fit",
    NULL, "Rosat catalog file"},
   {NULL, FTCL_ARGV_END, NULL, NULL, NULL}
};

static int
tclRosatRead (ClientData clientData,
	       Tcl_Interp *interp,
	       int argc,
	       char **argv)
{
  char *fileName = NULL;
  int status;

  HANDLE handle;
  TBLCOL *tblCol;
  int fldIdx;
  ARRAY* array[N_ROSAT_FIELDS];
  TA_ROSAT_CATALOG *catalog;
  int k;
  char handleName[HANDLE_NAMELEN];
  HDR *hdr;

  /* Parse the arguments */
  rosatReadTbl[1].dst = &fileName;
  if ((status = shTclParseArgv(interp, &argc, argv, rosatReadTbl,
			       rosatReadFlg, rosatReadCmd))
      != FTCL_ARGV_SUCCESS)
    return status;

  /* Create the TA_ROSAT_CATALOG */
  catalog = shMalloc(sizeof(TA_ROSAT_CATALOG));
  catalog->newSearch = 1;

  /* Get the catalog version from the PDU */
  hdr = shHdrNew();
  if (shHdrReadAsFits(hdr, (char *)fileName, DEF_NONE, NULL, 0, 0) != SH_SUCCESS)
    {
      shHdrDel(hdr);
      shFree(catalog);
      Tcl_AppendResult(interp, rosatReadCmd,
		       ": couldn't read the PDU", NULL);
      return TCL_ERROR;
    }
  catalog->version = shMalloc(versionStringLength * sizeof(char));
  if (shHdrGetAscii(hdr, "VERSION", catalog->version) != SH_SUCCESS)
    {
      shHdrDel(hdr);
      shFree(catalog->version);
      shFree(catalog);
      Tcl_AppendResult(interp, rosatReadCmd,
		       ": couldn't get VERSION keyword from the PDU", NULL);
      return TCL_ERROR;
    }
  shHdrDel(hdr);

  /* Read the file into a TBLCOL */
  tblCol = shTblcolNew();
  handle.ptr = (void *) tblCol;
  handle.type = shTypeGetFromName("TBLCOL");
  if (shFitsRead(&handle, (char *)fileName, DEF_NONE, f_hduBINTABLE, 1,
		 NULL, NULL, 0, 0) != SH_SUCCESS)
    {
      shTblcolDel(tblCol);
      shFree(catalog->version);
      shFree(catalog);
      Tcl_AppendResult(interp, rosatReadCmd,
		       ": couldn't read FITS file", NULL);
      return TCL_ERROR;
    }

  /* Locate each field in the TBLCOL, saving its array pointer. */
  for (fldIdx = 0; fldIdx < N_ROSAT_FIELDS; fldIdx++)
    {
      /* Locate the field */
      if (shTblFldLoc(tblCol, -1, rosatFieldName[fldIdx], 0,
		      SH_CASE_INSENSITIVE, &(array[fldIdx]), NULL, NULL)
	  != SH_SUCCESS)
	{
	  shTblcolDel(tblCol);
	  shFree(catalog->version);
	  shFree(catalog);
	  Tcl_AppendResult(interp, rosatReadCmd,
			   ": couldn't locate field ", rosatFieldName[fldIdx],
			   " in FITS file", NULL);
	  return TCL_ERROR;
	}
    }

  /* For each object ... */
  catalog->nObj = tblCol->rowCnt;
  catalog->obj = shMalloc(tblCol->rowCnt * sizeof(TA_ROSAT));
  for (k = 0; k < tblCol->rowCnt; k++)
    {
      /* Copy its contents from the TBLCOL to the array */
      catalog->obj[k].id      = (((int*)(array[ROSAT_ID]->arrayPtr))[k]);
      catalog->obj[k].lambda  =(((double*)(array[ROSAT_LAMBDA]->arrayPtr))[k]);
      catalog->obj[k].eta     = (((double*)(array[ROSAT_ETA]->arrayPtr))[k]);
      catalog->obj[k].posErr  = (((float*)(array[POSERR]->arrayPtr))[k]);
      catalog->obj[k].cps.val = (((float*)(array[CPS]->arrayPtr))[k]);
      catalog->obj[k].cps.err = (((float*)(array[CPSERR]->arrayPtr))[k]);
      catalog->obj[k].hr1.val = (((float*)(array[HR1]->arrayPtr))[k]);
      catalog->obj[k].hr1.err = (((float*)(array[HR1ERR]->arrayPtr))[k]);
      catalog->obj[k].hr2.val = (((float*)(array[HR2]->arrayPtr))[k]);
      catalog->obj[k].hr2.err = (((float*)(array[HR2ERR]->arrayPtr))[k]);
      catalog->obj[k].ext     = (((float*)(array[EXT]->arrayPtr))[k]);
      catalog->obj[k].extLike = (((float*)(array[EXTLIKE]->arrayPtr))[k]);
      catalog->obj[k].detectLike =(((float*)(array[DETECTLIKE]->arrayPtr))[k]);
      catalog->obj[k].exposure = (((float*)(array[EXPOSURE]->arrayPtr))[k]);
    }
  shTblcolDel(tblCol);

  /* Sort it by increasing lambda */
  qsort((void *)(catalog->obj), catalog->nObj, sizeof(TA_ROSAT),
	rosatCmpLambda);

  /* Return a handle to the TA_ROSAT_CATALOG */
  if (shTclHandleNew(interp, handleName, "TA_ROSAT_CATALOG", (void *)catalog)
      != TCL_OK)
    {
      shFree(catalog->version);
      shFree(catalog->obj);
      shFree(catalog);
      Tcl_AppendResult(interp, rosatReadCmd,
		       ": error allocating TCL handle", NULL);
      return TCL_ERROR;
    }
  Tcl_SetResult(interp, handleName, TCL_VOLATILE);
  return TCL_OK;
}


/*============================================================================
**<AUTO EXTRACT>
**
** TCL VERB: rosatDel
**<HTML>
**	Delete a TA_ROSAT_CATALOG structure.
**</HTML>
**</AUTO>
**============================================================================
*/
static char rosatDelCmd[] = "rosatDel";
static int rosatDelFlg = FTCL_ARGV_NO_LEFTOVERS;
static ftclArgvInfo rosatDelTbl[] = {
   {NULL, FTCL_ARGV_HELP, NULL, NULL, "Delete a TA_ROSAT_CATALOG structure\n"},
   {"<handle>", FTCL_ARGV_STRING, NULL, NULL, "Structure to delete"},
   {NULL, FTCL_ARGV_END, NULL, NULL, NULL}
};

static int
tclRosatDel (ClientData clientData,
		    Tcl_Interp *interp,
		    int argc,
		    char **argv)
{
  char *handle = NULL;
  TA_ROSAT_CATALOG *catalog = NULL;
  int status;

  /* Parse the arguments */
  rosatDelTbl[1].dst = &handle;
  if ((status = shTclParseArgv(interp, &argc, argv, rosatDelTbl,
			       rosatDelFlg, rosatDelCmd))
      != FTCL_ARGV_SUCCESS)
    return status;
   
  /* Get handle to the catalog */
  if (shTclAddrGetFromName (interp, handle, (void **)&catalog,
			    "TA_ROSAT_CATALOG") != TCL_OK)
    {
      Tcl_AppendResult(interp, rosatDelCmd,
		       ": error getting handle to catalog", NULL);
      return TCL_ERROR;
    }

  /* Delete it and its handle */
  shFree(catalog->version);
  shFree(catalog->obj);
  shFree(catalog);
  p_shTclHandleDel(interp, handle);

  /* Return */
  Tcl_SetResult(interp,"",TCL_STATIC);
  return(TCL_OK);
}


/*============================================================================
**<AUTO EXTRACT>
**
** TCL VERB: firstRead
**
**<HTML>
**	Read a FIRST catalog into a TA_FIRST_CATALOG structure.  Returns a
**	handle to the new catalog structure.
**	The catalog will be sorted by increasing lambda.
** 
**      Fixed some bugs here.  (GTR, 10/05/00)
**
**</HTML>
**</AUTO>
**============================================================================
*/
static char firstReadCmd[] = "firstRead";
static int firstReadFlg = FTCL_ARGV_NO_LEFTOVERS;
static ftclArgvInfo firstReadTbl[] = {
   {NULL, FTCL_ARGV_HELP, NULL, NULL,
    "Read a FIRST catalog\n"},
   {"[file]", FTCL_ARGV_STRING, "/opdb/d1/data/first/first.03apr11.stripe86.fit", NULL, "First catalog file"},
   {"[coverage handle]", FTCL_ARGV_STRING, "", NULL, "Handle to the FIRST catalog coverage map region"},
   {NULL, FTCL_ARGV_END, NULL, NULL, NULL}
};

static int
tclFirstRead (ClientData clientData,
	       Tcl_Interp *interp,
	       int argc,
	       char **argv)
{
  char *fileName = NULL;
  char *coverageHandle = NULL;
  int status;

  HANDLE handle;
  TBLCOL *tblCol;
  int fldIdx;
  ARRAY* array[N_FIRST_FIELDS];
  TA_FIRST_CATALOG *catalog;
  int k;
  char handleName[HANDLE_NAMELEN];
  HDR *hdr;

  /* Parse the arguments */
  firstReadTbl[1].dst = &fileName;
  firstReadTbl[2].dst = &coverageHandle;
  if ((status = shTclParseArgv(interp, &argc, argv, firstReadTbl,
			       firstReadFlg, firstReadCmd))
      != FTCL_ARGV_SUCCESS)
    return status;

  /* Create the TA_FIRST_CATALOG */
  catalog = shMalloc(sizeof(TA_FIRST_CATALOG));
  catalog->newSearch = 1;

        /*printf("ch %s\n",coverageHandle);*/
	if (strlen(coverageHandle) > 0) {
	if(shTclAddrGetFromName (interp,coverageHandle,(void **)&(catalog->covermap),"REGION") != TCL_OK) {
      		shFree(catalog);
		Tcl_AppendResult(interp,firstReadCmd, "couldn't get the coverage region",NULL);
		return TCL_ERROR;
	}


  if(shHdrGetDbl(&(catalog->covermap->hdr), "CRVAL1", &(catalog->crval1)) != SH_SUCCESS ||
     shHdrGetDbl(&(catalog->covermap->hdr), "CRVAL2", &(catalog->crval2)) != SH_SUCCESS || 
     shHdrGetDbl(&(catalog->covermap->hdr), "CRPIX1", &(catalog->crpix1)) != SH_SUCCESS ||
     shHdrGetDbl(&(catalog->covermap->hdr), "CRPIX2", &(catalog->crpix2)) != SH_SUCCESS ||
     shHdrGetDbl(&(catalog->covermap->hdr), "CDELT1", &(catalog->cdelt1)) != SH_SUCCESS ||
     shHdrGetDbl(&(catalog->covermap->hdr), "CDELT2", &(catalog->cdelt2)) != SH_SUCCESS) {
      shFree(catalog);
      Tcl_AppendResult(interp, firstReadCmd,
		       ": couldn't get CRxxxx keyword keyword from the coverage PDU", NULL);
      return TCL_ERROR;
	}

	catalog->nrow = catalog->covermap->nrow;
	catalog->ncol = catalog->covermap->ncol;
	} else {

		catalog->nrow = 0;
		catalog->ncol = 0;

	}

  /* Get the catalog version from the PDU */
  hdr = shHdrNew();
  if (shHdrReadAsFits(hdr, (char *)fileName, DEF_NONE, NULL, 0, 0) != SH_SUCCESS)
    {
      shHdrDel(hdr);
      shFree(catalog);
      Tcl_AppendResult(interp, firstReadCmd,
		       ": couldn't read the PDU", NULL);
      return TCL_ERROR;
    }
  catalog->version = shMalloc(versionStringLength * sizeof(char));
  if (shHdrGetAscii(hdr, "VERSION", catalog->version) != SH_SUCCESS)
    {
      shHdrDel(hdr);
      shFree(catalog->version);
      shFree(catalog);
      Tcl_AppendResult(interp, firstReadCmd,
		       ": couldn't get VERSION keyword from the PDU", NULL);
      return TCL_ERROR;
    }
  shHdrDel(hdr);

  /* Read the file into a TBLCOL */
  tblCol = shTblcolNew();
  handle.ptr = (void *) tblCol;
  handle.type = shTypeGetFromName("TBLCOL");
  if (shFitsRead(&handle, (char *)fileName, DEF_NONE, f_hduBINTABLE, 1,
		 NULL, NULL, 0, 0) != SH_SUCCESS)
    {
      shTblcolDel(tblCol);
      shFree(catalog->version);
      shFree(catalog); 
      Tcl_AppendResult(interp, firstReadCmd,
		       ": couldn't read FITS file", NULL);
      return TCL_ERROR;
    }

  /* Locate each field in the TBLCOL, saving its array pointer. */
  for (fldIdx = 0; fldIdx < N_FIRST_FIELDS; fldIdx++)
    {
      /* Locate the field */
      if (shTblFldLoc(tblCol, -1, firstFieldName[fldIdx], 0,
		      SH_CASE_INSENSITIVE, &(array[fldIdx]), NULL, NULL)
	  != SH_SUCCESS)
	{
	  shTblcolDel(tblCol);
	  shFree(catalog->version);
	  shFree(catalog);
	  Tcl_AppendResult(interp, firstReadCmd,
			 ": couldn't locate field in FITS file", NULL);
	  return TCL_ERROR;
	}
    }

  /* For each object ... */
  catalog->nObj = tblCol->rowCnt;
  catalog->obj = shMalloc(tblCol->rowCnt * sizeof(TA_FIRST));
  for (k = 0; k < tblCol->rowCnt; k++)
    {
      /* Copy its contents from the TBLCOL to the array */
      catalog->obj[k].id    = (((int*)(array[FIRST_ID]->arrayPtr))[k]);
      catalog->obj[k].lambda= (((double*)(array[FIRST_LAMBDA]->arrayPtr))[k]);
      catalog->obj[k].eta   = (((double*)(array[FIRST_ETA]->arrayPtr))[k]);
      catalog->obj[k].wflag = (((int*)(array[WFLAG]->arrayPtr))[k]);
      catalog->obj[k].fpeak = (((float*)(array[FPEAK]->arrayPtr))[k]);
      catalog->obj[k].fint  = (((float*)(array[FINT]->arrayPtr))[k]);
      catalog->obj[k].rms   = (((float*)(array[RMS]->arrayPtr))[k]);
      catalog->obj[k].maj   = (((float*)(array[MAJ]->arrayPtr))[k]);
      catalog->obj[k].min   = (((float*)(array[MIN]->arrayPtr))[k]);
      catalog->obj[k].pa    = (((float*)(array[PA]->arrayPtr))[k]);
    }
  shTblcolDel(tblCol);

  /* Sort it by increasing lambda */
  qsort((void *)(catalog->obj), catalog->nObj, sizeof(TA_FIRST),
	firstCmpLambda);

  /* Return a handle to the TA_FIRST_CATALOG */
  if (shTclHandleNew(interp, handleName, "TA_FIRST_CATALOG", (void *)catalog)
      != TCL_OK)
    {
      shFree(catalog->version);
      shFree(catalog->obj);
      shFree(catalog);
      Tcl_AppendResult(interp, firstReadCmd,
		       ": error allocating TCL handle", NULL);
      return TCL_ERROR;
    }
  Tcl_SetResult(interp, handleName, TCL_VOLATILE);
  return TCL_OK;
}


/*============================================================================
**<AUTO EXTRACT>
**
** TCL VERB: firstDel
**<HTML>
**	Delete a TA_FIRST_CATALOG structure.
**</HTML>
**</AUTO>
**============================================================================
*/
static char firstDelCmd[] = "firstDel";
static int firstDelFlg = FTCL_ARGV_NO_LEFTOVERS;
static ftclArgvInfo firstDelTbl[] = {
   {NULL, FTCL_ARGV_HELP, NULL, NULL, "Delete a TA_FIRST_CATALOG structure\n"},
   {"<handle>", FTCL_ARGV_STRING, NULL, NULL, "Structure to delete"},
   {NULL, FTCL_ARGV_END, NULL, NULL, NULL}
};

static int
tclFirstDel (ClientData clientData,
		    Tcl_Interp *interp,
		    int argc,
		    char **argv)
{
  char *handle = NULL;
  TA_FIRST_CATALOG *catalog = NULL;
  int status;

  /* Parse the arguments */
  firstDelTbl[1].dst = &handle;
  if ((status = shTclParseArgv(interp, &argc, argv, firstDelTbl,
			       firstDelFlg, firstDelCmd))
      != FTCL_ARGV_SUCCESS)
    return status;
   
  /* Get handle to the catalog */
  if (shTclAddrGetFromName (interp, handle, (void **)&catalog,
			    "TA_FIRST_CATALOG") != TCL_OK)
    {
      Tcl_AppendResult(interp, firstDelCmd,
		       ": error getting handle to catalog", NULL);
      return TCL_ERROR;
    }

  /* Delete it and its handle */
  shFree(catalog->version);
  shFree(catalog->obj);
  shFree(catalog);
  p_shTclHandleDel(interp, handle);

  /* Return */
  Tcl_SetResult(interp,"",TCL_STATIC);
  return(TCL_OK);
}


/*============================================================================
**<AUTO EXTRACT>
**
** TCL VERB: usnoRead
**
**<HTML>
**	Read a USNO catalog into a TA_USNO_CATALOG structure.
**	Only that portion of the catalog that intersects a scan, as
**	defined	by a strip, 3 degrees wide, along a section of a great circle,
**	will be read in.  Returns a handle to the new catalog structure.
**	The catalog will be sorted by increasing lambda.
**</HTML>
**</AUTO>
**============================================================================
*/
static char usnoReadCmd[] = "usnoRead";
static int usnoReadFlg = FTCL_ARGV_NO_LEFTOVERS;
static ftclArgvInfo usnoReadTbl[] = {
   {NULL, FTCL_ARGV_HELP, NULL, NULL,
    "Read a USNO catalog\n"},
   {"<dir>", FTCL_ARGV_STRING, NULL, NULL, "Directory containing USNO catalog files"},
   {"<node>", FTCL_ARGV_DOUBLE, NULL, NULL, "Ascending node of great circle (degrees)"},
   {"<incl>", FTCL_ARGV_DOUBLE, NULL, NULL, "Inclination of great circle (degrees)"},
   {"<equinox>", FTCL_ARGV_DOUBLE, NULL, NULL, "Equinox of great circle (Julian years)"},
   {"<epoch>", FTCL_ARGV_DOUBLE, NULL, NULL, "Convert coordinates to this epoch (Julian years)"},
   {"<minMu>", FTCL_ARGV_DOUBLE, NULL, NULL, "Minimum great circle longitude in scan (degrees)"},
   {"<maxMu>", FTCL_ARGV_DOUBLE, NULL, NULL, "Maximum great circle longitude in scan (degrees)"},
   {"-minNu", FTCL_ARGV_DOUBLE, NULL, NULL, "Minimum great circle latitude in scan (degrees, defaults to -1.5)"},
   {"-maxNu", FTCL_ARGV_DOUBLE, NULL, NULL, "Maximum great circle latitude in scan (degrees, default to 1.5)"},
   {NULL, FTCL_ARGV_END, NULL, NULL, NULL}
};

static int
tclUsnoRead (ClientData clientData,
	     Tcl_Interp *interp,
	     int argc,
	     char **argv)
{
  char *dirName = NULL;
  double node, incl, equinox, epoch, minMu, maxMu;
  double minNu = -1.5;
  double maxNu = 1.5;
  int status;

  HANDLE handle;
  TA_USNO_CATALOG *catalog = NULL;
  char handleName[HANDLE_NAMELEN];

  /* Parse the arguments */
  usnoReadTbl[1].dst = &dirName;
  usnoReadTbl[2].dst = &node;
  usnoReadTbl[3].dst = &incl;
  usnoReadTbl[4].dst = &equinox;
  usnoReadTbl[5].dst = &epoch;
  usnoReadTbl[6].dst = &minMu;
  usnoReadTbl[7].dst = &maxMu;
  usnoReadTbl[8].dst = &minNu;
  usnoReadTbl[9].dst = &maxNu;
  if ((status = shTclParseArgv(interp, &argc, argv, usnoReadTbl,
			       usnoReadFlg, usnoReadCmd))
      != FTCL_ARGV_SUCCESS)
    return status;

  /* Read the catalog */
  if ((catalog = taUSNORead(node, incl, equinox, minMu, maxMu, minNu, maxNu,
			    dirName, epoch, 0, catalog)) == NULL)
    {
      shTclInterpAppendWithErrStack(interp);
      return TCL_ERROR;
    }
  catalog->newSearch = 1;

  /* Sort it by increasing lambda */
  qsort((void *)(catalog->obj), catalog->nObj, sizeof(TA_USNO),
	usnoCmpLambda);

  /* Return a handle to the TA_USNO_CATALOG */
  if (shTclHandleNew(interp, handleName, "TA_USNO_CATALOG", (void *)catalog)
      != TCL_OK)
    {
      shFree(catalog->version);
      shFree(catalog->obj);
      shFree(catalog);
      Tcl_AppendResult(interp, usnoReadCmd,
		       ": error allocating TCL handle", NULL);
      return TCL_ERROR;
    }
  Tcl_SetResult(interp, handleName, TCL_VOLATILE);
  return TCL_OK;
}


/*============================================================================
**<AUTO EXTRACT>
**
** TCL VERB: usnoToEpoch
**
**<HTML>
**	Convert positions in USNO catalog to a new epoch.
**</HTML>
**</AUTO>
**============================================================================
*/
static char usnoToEpochCmd[] = "usnoToEpoch";
static int usnoToEpochFlg = FTCL_ARGV_NO_LEFTOVERS;
static ftclArgvInfo usnoToEpochTbl[] = {
   {NULL, FTCL_ARGV_HELP, NULL, NULL,
    "Convert USNO catalog to a new epoch\n"},
   {"<catalog>", FTCL_ARGV_STRING, NULL, NULL, "Handle to TA_USNO_CATALOG"},
   {"<epoch>", FTCL_ARGV_DOUBLE, NULL, NULL, "Convert coordinates to this epoch (Julian years)"},
   {NULL, FTCL_ARGV_END, NULL, NULL, NULL}
};

static int
tclUsnoToEpoch (ClientData clientData,
		Tcl_Interp *interp,
		int argc,
		char **argv)
{
  char *handle = NULL;
  TA_USNO_CATALOG *catalog = NULL;
  double epoch;
  int status;

  /* Parse the arguments */
  usnoToEpochTbl[1].dst = &handle;
  usnoToEpochTbl[2].dst = &epoch;
  if ((status = shTclParseArgv(interp, &argc, argv, usnoToEpochTbl,
			       usnoToEpochFlg, usnoToEpochCmd))
      != FTCL_ARGV_SUCCESS)
    return status;

  /* Get handle to the catalog */
  if (shTclAddrGetFromName (interp, handle, (void **)&catalog,
			    "TA_USNO_CATALOG") != TCL_OK)
    {
      Tcl_AppendResult(interp, usnoToEpochCmd,
		       ": error getting handle to catalog", NULL);
      return TCL_ERROR;
    }

  /* Convert it to a new epoch */
  taUSNOToEpoch(catalog, epoch);

  /* Resort by increasing lambda */
  qsort((void *)(catalog->obj), catalog->nObj, sizeof(TA_USNO),
	usnoCmpLambda);
  catalog->newSearch = 1;

  /* Return */
  Tcl_SetResult(interp,"",TCL_STATIC);
  return(TCL_OK);
}


/*============================================================================
**<AUTO EXTRACT>
**
** TCL VERB: usnoDel
**<HTML>
**	Delete a TA_USNO_CATALOG structure.
**</HTML>
**</AUTO>
**============================================================================
*/
static char usnoDelCmd[] = "usnoDel";
static int usnoDelFlg = FTCL_ARGV_NO_LEFTOVERS;
static ftclArgvInfo usnoDelTbl[] = {
   {NULL, FTCL_ARGV_HELP, NULL, NULL, "Delete a TA_USNO_CATALOG structure\n"},
   {"<handle>", FTCL_ARGV_STRING, NULL, NULL, "Structure to delete"},
   {NULL, FTCL_ARGV_END, NULL, NULL, NULL}
};

static int
tclUsnoDel (ClientData clientData,
		    Tcl_Interp *interp,
		    int argc,
		    char **argv)
{
  char *handle = NULL;
  TA_USNO_CATALOG *catalog = NULL;
  int status;

  /* Parse the arguments */
  usnoDelTbl[1].dst = &handle;
  if ((status = shTclParseArgv(interp, &argc, argv, usnoDelTbl,
			       usnoDelFlg, usnoDelCmd))
      != FTCL_ARGV_SUCCESS)
    return status;
   
  /* Get handle to the catalog */
  if (shTclAddrGetFromName (interp, handle, (void **)&catalog,
			    "TA_USNO_CATALOG") != TCL_OK)
    {
      Tcl_AppendResult(interp, usnoDelCmd,
		       ": error getting handle to catalog", NULL);
      return TCL_ERROR;
    }

  /* Delete it and its handle */
  if (catalog->version != NULL) shFree(catalog->version);
  if (catalog->obj != NULL) shFree(catalog->obj);
  shFree(catalog);
  p_shTclHandleDel(interp, handle);

  /* Return */
  Tcl_SetResult(interp,"",TCL_STATIC);
  return(TCL_OK);
}


/*****************************************************************************
 *
 * Declare my new tcl verbs to tcl
 */
void
taTclCatalogsDeclare(Tcl_Interp *interp) 
{
  char *tclHelpFacil = "ta";
  shTclDeclare(interp, firstReadCmd,
	       (Tcl_CmdProc *)tclFirstRead,
	       (ClientData) 0,	(Tcl_CmdDeleteProc *)NULL, tclHelpFacil,
	       shTclGetArgInfo(interp, firstReadTbl, firstReadFlg,
			       firstReadCmd),
	       shTclGetUsage(interp, firstReadTbl, firstReadFlg,
			     firstReadCmd));
  shTclDeclare(interp, firstDelCmd,
	       (Tcl_CmdProc *)tclFirstDel,
	       (ClientData) 0,	(Tcl_CmdDeleteProc *)NULL, tclHelpFacil,
	       shTclGetArgInfo(interp, firstDelTbl, firstDelFlg,
			       firstDelCmd),
	       shTclGetUsage(interp, firstDelTbl, firstDelFlg,
			     firstDelCmd));
  shTclDeclare(interp, rosatReadCmd,
	       (Tcl_CmdProc *)tclRosatRead,
	       (ClientData) 0,	(Tcl_CmdDeleteProc *)NULL, tclHelpFacil,
	       shTclGetArgInfo(interp, rosatReadTbl, rosatReadFlg,
			       rosatReadCmd),
	       shTclGetUsage(interp, rosatReadTbl, rosatReadFlg,
			     rosatReadCmd));
  shTclDeclare(interp, rosatDelCmd,
	       (Tcl_CmdProc *)tclRosatDel,
	       (ClientData) 0,	(Tcl_CmdDeleteProc *)NULL, tclHelpFacil,
	       shTclGetArgInfo(interp, rosatDelTbl, rosatDelFlg,
			       rosatDelCmd),
	       shTclGetUsage(interp, rosatDelTbl, rosatDelFlg,
			     rosatDelCmd));
  shTclDeclare(interp, usnoReadCmd,
	       (Tcl_CmdProc *)tclUsnoRead,
	       (ClientData) 0,	(Tcl_CmdDeleteProc *)NULL, tclHelpFacil,
	       shTclGetArgInfo(interp, usnoReadTbl, usnoReadFlg,
			       usnoReadCmd),
	       shTclGetUsage(interp, usnoReadTbl, usnoReadFlg,
			     usnoReadCmd));
  shTclDeclare(interp, usnoToEpochCmd,
	       (Tcl_CmdProc *)tclUsnoToEpoch,
	       (ClientData) 0,	(Tcl_CmdDeleteProc *)NULL, tclHelpFacil,
	       shTclGetArgInfo(interp, usnoToEpochTbl, usnoToEpochFlg,
			       usnoToEpochCmd),
	       shTclGetUsage(interp, usnoToEpochTbl, usnoToEpochFlg,
			     usnoToEpochCmd));
  shTclDeclare(interp, usnoDelCmd,
	       (Tcl_CmdProc *)tclUsnoDel,
	       (ClientData) 0,	(Tcl_CmdDeleteProc *)NULL, tclHelpFacil,
	       shTclGetArgInfo(interp, usnoDelTbl, usnoDelFlg,
			       usnoDelCmd),
	       shTclGetUsage(interp, usnoDelTbl, usnoDelFlg,
			     usnoDelCmd));
}
