/*****************************************************************************
******************************************************************************
**<AUTO>
**
** FILE:	tclTargets.cc
**
**<HTML>
**	This file contains TCL functions to select spectroscopic targets.
**</HTML>
**</AUTO>
**
**
** ENVIRONMENT:
**	C++.
**
** REQUIRED PRODUCTS:
**	photoSch
**
******************************************************************************
******************************************************************************
*/

#include <arProduct.h>
#include <arChunk.h>
#include <arSegment.h>
#include <dervish.h>
#include <taCosmic.h>
#include <taDiag.h>
#include "tsTarget.h"

/*============================================================================
**<AUTO EXTRACT>
**
** TCL VERB: dbTargetSelectFromSegment
**
**<HTML>
**	Select spectroscopic targets from all primary objects in a segment.
**	Optionally, a frames pipeline run handle can be passed in for the
**	segment handle, in which case targets are selected from all OK_RUN
**	objects in the frames pipeline run.  When selecting from a frames
**	pipeline run, the "-noUpdate" option must be specified.
**</HTML>
**
**</AUTO>
**============================================================================
*/
static char dbTargetSelectFromSegmentCmd[] = "dbTargetSelectFromSegment";
static int dbTargetSelectFromSegmentFlg = FTCL_ARGV_NO_LEFTOVERS;
static ftclArgvInfo dbTargetSelectFromSegmentTbl[] = {
   {NULL, FTCL_ARGV_HELP, NULL, NULL,
    "Select spectroscopic targets from all primary objects in a segment/run\n"},
   {"<segment>", FTCL_ARGV_STRING, NULL, NULL,
    "Handle to the segment/frames-pipeline-run"},
   {"<startLambda>", FTCL_ARGV_DOUBLE, NULL, NULL,
    "Beginning stripe lambda (degrees, -999=none)"},
   {"<endLambda>", FTCL_ARGV_DOUBLE, NULL, NULL,
    "Ending stripe lambda (degrees, -999=none)"},
   {"-galaxies", FTCL_ARGV_STRING, NULL, NULL,
    "Handle to Galaxies tunable parameters (TA_GALAXIES_PARAMS"},
   {"-quasars", FTCL_ARGV_STRING, NULL, NULL,
    "Handle to Quasars tunable parameters (TA_QUASARS_PARAMS"},
   {"-serendipity", FTCL_ARGV_STRING, NULL, NULL,
    "Handle to Serendipity tunable parameters (TA_SERENDIPITY_PARAMS"},
   {"-rosat", FTCL_ARGV_STRING, NULL, NULL,
    "Handle to ROSAT tunable parameters (TA_ROSAT_PARAMS"},
   {"-stars", FTCL_ARGV_STRING, NULL, NULL,
    "Handle to Stars tunable parameters (TA_STARS_PARAMS"},
   {"-standards", FTCL_ARGV_STRING, NULL, NULL,
    "Handle to Standards tunable parameters (TA_STANDARDS_PARAMS"},
   {"-first", FTCL_ARGV_STRING, NULL, NULL,
    "Handle to FIRST catalog (TA_FIRST_CATALOG)"},
   {"-crosat", FTCL_ARGV_STRING, NULL, NULL,
    "Handle to ROSAT catalog (TA_ROSAT_CATALOG)"},
   {"-usno", FTCL_ARGV_STRING, NULL, NULL,
    "Handle to USNO catalog (TA_USNO_CATALOG)"},
   {"-export", FTCL_ARGV_INT, NULL, NULL,
    "Export tsObjc files --- 0=field files only, 1=targets only, 2=primaries only, 3=primaries and secondaries, 4=everything"},
   {"-southern", FTCL_ARGV_CONSTANT, (void *) 1, NULL,
    "Use Southern target algs?"},
   {"-parents", FTCL_ARGV_CONSTANT, (void *) 1, NULL,
    "If exporting, export parent objects as well"},
   {"-noSplit", FTCL_ARGV_CONSTANT, (void *) 1, NULL,
    "If exporting, export one file, rather than one file per field (defaults to 1 file per field)"},
   {"-outputDir", FTCL_ARGV_STRING, NULL, NULL,
    "Directory to create exported tsObjc files in (defaults to current; if it contains the string %d, the %d is replaced with the camCol)"},
   {"-force", FTCL_ARGV_CONSTANT, (void *) 1, NULL,
    "Force re-selection if chunk previously through target selection"},
   {"-field0", FTCL_ARGV_INT, NULL, NULL,
    "First field to select on (defaults to first field in the segment/run) --- use for debugging only"},
   {"-nFields", FTCL_ARGV_INT, NULL, NULL,
    "Select on this number of fields only (starting with first field, default=0=all) --- use for debugging only"},
   {"-noUpdate", FTCL_ARGV_CONSTANT, (void *) 1, NULL,
    "Don't update the OPDB --- useful for generating test tsObj files"},
   {"-diag", FTCL_ARGV_STRING, NULL, NULL,
    "Handle to running diagnsotics (TA_DIAG)"},
   {"-resolve", FTCL_ARGV_INT, NULL, NULL,
    "0=use OPDB resolution (default), 1=do an on-the-fly resolve for a frames pipeline run, obeying stripe eta limits, 2=do an on-the-fly resolve for a frames pipeline run, ignoring stripe eta limits"},
   {NULL, FTCL_ARGV_END, NULL, NULL, NULL}
};

static int
tclDbTargetSelectFromSegment (ClientData clientData,
		   Tcl_Interp *interp,
		   int argc,
		   char **argv)
{
  ooHandle(arSegment) *segment = NULL;
  ooHandle(arFramesPipeRun) *fpr = NULL;
  char *segmentHandle = NULL;
  TA_GALAXIES_PARAMS *galaxies = NULL;
  char *galaxiesHandle = NULL;
  TA_QUASARS_PARAMS *quasars = NULL;
  char *quasarsHandle = NULL;
  TA_SERENDIPITY_PARAMS *serendipity = NULL;
  char *serendipityHandle = NULL;
  TA_ROSAT_PARAMS *rosat = NULL;
  char *rosatHandle = NULL;
  TA_STARS_PARAMS *stars = NULL;
  char *starsHandle = NULL;
  TA_STANDARDS_PARAMS *standards = NULL;
  char *standardsHandle = NULL;
  TA_FIRST_CATALOG *firstCat = NULL;
  char *firstCatHandle = NULL;
  TA_ROSAT_CATALOG *rosatCat = NULL;
  char *rosatCatHandle = NULL;
  TA_USNO_CATALOG *usnoCat = NULL;
  char *usnoCatHandle = NULL;
  int export = -1;
  int parents = 0;
  int noSplit = 0;
  char *outputDir = NULL;
  int force = 0;
  int field0 = -1;
  int nFields = 0;
  int noUpdate = 0;
  double startLambda = -999;
  double endLambda = -999;
  char *diagHandle = NULL;
  TA_DIAG *diag = NULL;
  int resolve = 0;
  int southern = 0;
  int status;

  // Parse the arguments
  dbTargetSelectFromSegmentTbl[1].dst = &segmentHandle;
  dbTargetSelectFromSegmentTbl[2].dst = &startLambda;
  dbTargetSelectFromSegmentTbl[3].dst = &endLambda;
  dbTargetSelectFromSegmentTbl[4].dst = &galaxiesHandle;
  dbTargetSelectFromSegmentTbl[5].dst = &quasarsHandle;
  dbTargetSelectFromSegmentTbl[6].dst = &serendipityHandle;
  dbTargetSelectFromSegmentTbl[7].dst = &rosatHandle;
  dbTargetSelectFromSegmentTbl[8].dst = &starsHandle;
  dbTargetSelectFromSegmentTbl[9].dst = &standardsHandle;
  dbTargetSelectFromSegmentTbl[10].dst = &firstCatHandle;
  dbTargetSelectFromSegmentTbl[11].dst = &rosatCatHandle;
  dbTargetSelectFromSegmentTbl[12].dst = &usnoCatHandle;
  dbTargetSelectFromSegmentTbl[13].dst = &export;
  dbTargetSelectFromSegmentTbl[14].dst = &southern;
  dbTargetSelectFromSegmentTbl[15].dst = &parents;
  dbTargetSelectFromSegmentTbl[16].dst = &noSplit;
  dbTargetSelectFromSegmentTbl[17].dst = &outputDir;
  dbTargetSelectFromSegmentTbl[18].dst = &force;
  dbTargetSelectFromSegmentTbl[19].dst = &field0;
  dbTargetSelectFromSegmentTbl[20].dst = &nFields;
  dbTargetSelectFromSegmentTbl[21].dst = &noUpdate;
  dbTargetSelectFromSegmentTbl[22].dst = &diagHandle;
  dbTargetSelectFromSegmentTbl[23].dst = &resolve;
  if ((status = shTclParseArgv(interp, &argc, argv, dbTargetSelectFromSegmentTbl,
			       dbTargetSelectFromSegmentFlg,
			       dbTargetSelectFromSegmentCmd))
      != FTCL_ARGV_SUCCESS)
    return status;
   
  // Get handle to segment/run
  ooHandle(arSegment) dummySegment;
  ooHandle(arFramesPipeRun) dummyFPR;
  if (shTclAddrGetFromName (interp, segmentHandle, (void **)&segment,
			    "ooHandle(arSegment)") != TCL_OK)
    {
      // Not a segment.  Must be a frames pipeline run.
      if (shTclAddrGetFromName (interp, segmentHandle, (void **)&fpr,
				"ooHandle(arFramesPipeRun)") != TCL_OK)
	{
	  Tcl_AppendResult(interp, dbTargetSelectFromSegmentCmd,
			   ": error getting handle to segment/run", NULL);
	  return TCL_ERROR;
	}
      segment = &dummySegment;
    }
  else
    {
      if (resolve != 0)
	{
	  Tcl_AppendResult(interp, dbTargetSelectFromSegmentCmd,
			   ": can't use -resolve option with segments");
	  return TCL_ERROR;
	}
      fpr = &dummyFPR;
    }
   
  /* Get handle for each specified set of tunable parameters */
  if (ftcl_ArgIsPresent(argc, argv,"-galaxies",dbTargetSelectFromSegmentTbl) ==1)
    {
      /* Get handle to galaxies parameters */
      if (shTclAddrGetFromName (interp, galaxiesHandle, (void **)&galaxies,
				"TA_GALAXIES_PARAMS") != TCL_OK)
	{
	  Tcl_AppendResult(interp, dbTargetSelectFromSegmentCmd,
			   ": error getting handle to galaxies params", NULL);
	  return TCL_ERROR;
	}
    }
  if (ftcl_ArgIsPresent(argc, argv,"-quasars",dbTargetSelectFromSegmentTbl) == 1)
    {
      /* Get handle to quasars parameters */
      if (shTclAddrGetFromName (interp, quasarsHandle, (void **)&quasars,
				"TA_QUASARS_PARAMS") != TCL_OK)
	{
	  Tcl_AppendResult(interp, dbTargetSelectFromSegmentCmd,
			   ": error getting handle to quasars params", NULL);
	  return TCL_ERROR;
	}
    }
  if (ftcl_ArgIsPresent(argc,argv,"-serendipity",dbTargetSelectFromSegmentTbl)
      == 1)
    {
      /* Get handle to serendipity parameters */
      if (shTclAddrGetFromName (interp,serendipityHandle,(void **)&serendipity,
				"TA_SERENDIPITY_PARAMS") != TCL_OK)
	{
	  Tcl_AppendResult(interp, dbTargetSelectFromSegmentCmd,
			   ": error getting handle to serendipity params",
			   NULL);
	  return TCL_ERROR;
	}
    }
  if (ftcl_ArgIsPresent(argc,argv,"-rosat",dbTargetSelectFromSegmentTbl)== 1)
    {
      /* Get handle to rosat parameters */
      if (shTclAddrGetFromName (interp,rosatHandle,(void **)&rosat,
				"TA_ROSAT_PARAMS") != TCL_OK)
	{
	  Tcl_AppendResult(interp, dbTargetSelectFromSegmentCmd,
			   ": error getting handle to rosat params",
			   NULL);
	  return TCL_ERROR;
	}
    }
  if (ftcl_ArgIsPresent(argc, argv, "-stars", dbTargetSelectFromSegmentTbl) == 1)
    {
      /* Get handle to stars parameters */
      if (shTclAddrGetFromName (interp, starsHandle, (void **)&stars,
				"TA_STARS_PARAMS") != TCL_OK)
	{
	  Tcl_AppendResult(interp, dbTargetSelectFromSegmentCmd,
			   ": error getting handle to stars params", NULL);
	  return TCL_ERROR;
	}
    }
  if (ftcl_ArgIsPresent(argc,argv,"-standards",dbTargetSelectFromSegmentTbl)== 1)
    {
      /* Get handle to standards parameters */
      if (shTclAddrGetFromName (interp, standardsHandle, (void **)&standards,
				"TA_STANDARDS_PARAMS") != TCL_OK)
	{
	  Tcl_AppendResult(interp, dbTargetSelectFromSegmentCmd,
			   ": error getting handle to standards params", NULL);
	  return TCL_ERROR;
	}
    }
  if (ftcl_ArgIsPresent(argc, argv, "-first", dbTargetSelectFromSegmentTbl) == 1)
    {
      /* Get handle to FIRST catalog */
      if (shTclAddrGetFromName (interp, firstCatHandle, (void **)&firstCat,
				"TA_FIRST_CATALOG") != TCL_OK)
	{
	  Tcl_AppendResult(interp, dbTargetSelectFromSegmentCmd,
			   ": error getting handle to FIRST catalog", NULL);
	  return TCL_ERROR;
	}
    }
  if (ftcl_ArgIsPresent(argc, argv, "-crosat",dbTargetSelectFromSegmentTbl) == 1)
    {
      /* Get handle to ROSAT catalog */
      if (shTclAddrGetFromName (interp, rosatCatHandle, (void **)&rosatCat,
				"TA_ROSAT_CATALOG") != TCL_OK)
	{
	  Tcl_AppendResult(interp, dbTargetSelectFromSegmentCmd,
			   ": error getting handle to ROSAT catalog", NULL);
	  return TCL_ERROR;
	}
    }
  if (ftcl_ArgIsPresent(argc, argv, "-usno",dbTargetSelectFromSegmentTbl) == 1)
    {
      /* Get handle to USNO catalog */
      if (shTclAddrGetFromName (interp, usnoCatHandle, (void **)&usnoCat,
				"TA_USNO_CATALOG") != TCL_OK)
	{
	  Tcl_AppendResult(interp, dbTargetSelectFromSegmentCmd,
			   ": error getting handle to USNO catalog", NULL);
	  return TCL_ERROR;
	}
    }
  if (ftcl_ArgIsPresent(argc, argv, "-diag",dbTargetSelectFromSegmentTbl) == 1)
    {
      /* Get handle to diagnostics */
      if (shTclAddrGetFromName (interp, diagHandle, (void **)&diag,
				"TA_DIAG") != TCL_OK)
	{
	  Tcl_AppendResult(interp, dbTargetSelectFromSegmentCmd,
			   ": error getting handle to diagnostics", NULL);
	  return TCL_ERROR;
	}
    }

  // Fetch the global TCL variable "photo_targets" for container with all
  // spectroscopic targets.  Then fetch the associated handle.  We'll use
  // this as a clustering directive when creating new targets.
  char *photo_targets = Tcl_GetVar(interp, "photo_targets", TCL_GLOBAL_ONLY);
  if (photo_targets == NULL)
    {
      Tcl_AppendResult(interp, dbTargetSelectFromSegmentCmd,
		       ": global variable $photo_targets doesn't exist",
		       NULL);
      return TCL_ERROR;
    }
  ooHandle(ooContObj) *targetCluster;
  if (shTclAddrGetFromName (interp, photo_targets, (void **)&targetCluster,
			    "ooHandle(ooContObj)") != TCL_OK)
    {
      Tcl_AppendResult(interp, dbTargetSelectFromSegmentCmd,
		       ": error getting handle to $photo_targets", NULL);
      return TCL_ERROR;
    }

  // Fetch the global TCL variable "photo_first" for container with all
  // FIRST composite objects.  Then fetch the associated handle.  We'll use
  // this as a clustering directive when creating new FIRST objects.
  char *photo_first = Tcl_GetVar(interp, "photo_first", TCL_GLOBAL_ONLY);
  if (photo_first == NULL)
    {
      Tcl_AppendResult(interp, dbTargetSelectFromSegmentCmd,
		       ": global variable $photo_first doesn't exist",
		       NULL);
      return TCL_ERROR;
    }
  ooHandle(ooContObj) *firstCluster;
  if (shTclAddrGetFromName (interp, photo_first, (void **)&firstCluster,
			    "ooHandle(ooContObj)") != TCL_OK)
    {
      Tcl_AppendResult(interp, dbTargetSelectFromSegmentCmd,
		       ": error getting handle to $photo_first", NULL);
      return TCL_ERROR;
    }

  // Fetch the global TCL variable "photo_rosat" for container with all
  // ROSAT objects.  Then fetch the associated handle.  We'll use
  // this to fetch handles to matched ROSAT objects.
  char *photo_rosat = Tcl_GetVar(interp, "photo_rosat", TCL_GLOBAL_ONLY);
  if (photo_rosat == NULL)
    {
      Tcl_AppendResult(interp, dbTargetSelectFromSegmentCmd,
		       ": global variable $photo_rosat doesn't exist",
		       NULL);
      return TCL_ERROR;
    }
  ooHandle(ooContObj) *rosatCluster;
  if (shTclAddrGetFromName (interp, photo_rosat, (void **)&rosatCluster,
			    "ooHandle(ooContObj)") != TCL_OK)
    {
      Tcl_AppendResult(interp, dbTargetSelectFromSegmentCmd,
		       ": error getting handle to $photo_rosat", NULL);
      return TCL_ERROR;
    }

  // Set up cosmic color for use by atTransApply().
  taTransCosmicSet(cosmicColor, cosmicScatter);

  // Select targets.
  int select = 1;
  if (tsSegmentCalibrate(*segment, *fpr, field0, nFields, export, parents,
			 !noSplit, select, force, noUpdate, resolve,
			 startLambda, endLambda, southern, outputDir,
			 targetCluster, firstCluster, rosatCluster,
			 galaxies, quasars, serendipity,
			 rosat, stars, standards, firstCat, rosatCat,
			 usnoCat, diag)
      != SH_SUCCESS)
    {
      shTclInterpAppendWithErrStack(interp);
      return TCL_ERROR;
    }

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

/*============================================================================
**<AUTO EXTRACT>
**
** TCL VERB: segmentStats
**
**<HTML>
**	Print out some basic stats about a segment.
**</HTML>
**
**</AUTO>
**============================================================================
*/
static char segmentStatsCmd[] = "segmentStats";
static int segmentStatsFlg = FTCL_ARGV_NO_LEFTOVERS;
static ftclArgvInfo segmentStatsTbl[] = {
   {NULL, FTCL_ARGV_HELP, NULL, NULL,
    "Print out stats about a segment\n"},
   {"<segment>", FTCL_ARGV_STRING, NULL, NULL,
    "Handle to the segment"},
   {"-check", FTCL_ARGV_CONSTANT, (void *) 1, NULL,
    "check target links"},
   {NULL, FTCL_ARGV_END, NULL, NULL, NULL}
};

static int
tclSegmentStats (ClientData clientData,
		 Tcl_Interp *interp,
		 int argc,
		 char **argv)
{
  ooHandle(arSegment) *segment = NULL;
  char *segmentHandle = NULL;
  int check = 0;
  int status;

  // Parse the arguments
  segmentStatsTbl[1].dst = &segmentHandle;
  segmentStatsTbl[2].dst = &check;
  if ((status = shTclParseArgv(interp, &argc, argv, segmentStatsTbl,
			       segmentStatsFlg, segmentStatsCmd))
      != FTCL_ARGV_SUCCESS)
    return status;
   
  // Get handle to segment/run
  if (shTclAddrGetFromName (interp, segmentHandle, (void **)&segment,
			    "ooHandle(arSegment)") != TCL_OK)
    {
      Tcl_AppendResult(interp, segmentStatsCmd,
		       ": error getting handle to segment", NULL);
      return TCL_ERROR;
    }

#ifdef SKIP
  // Fetch the global TCL variable "photo_targets" for container with all
  // spectroscopic targets.  Then fetch the associated handle.  We'll use
  // this as a clustering directive when creating new targets.
  char *photo_targets = Tcl_GetVar(interp, "photo_targets", TCL_GLOBAL_ONLY);
  if (photo_targets == NULL)
    {
      Tcl_AppendResult(interp, dbTargetSelectFromSegmentCmd,
		       ": global variable $photo_targets doesn't exist",
		       NULL);
      return TCL_ERROR;
    }
  ooHandle(ooContObj) *targetCluster;
  if (shTclAddrGetFromName (interp, photo_targets, (void **)&targetCluster,
			    "ooHandle(ooContObj)") != TCL_OK)
    {
      Tcl_AppendResult(interp, dbTargetSelectFromSegmentCmd,
		       ": error getting handle to $photo_targets", NULL);
      return TCL_ERROR;
    }
#endif

  // Do it
  if (tsSegmentStats(*segment, check) != SH_SUCCESS)
    {
      shTclInterpAppendWithErrStack(interp);
      return TCL_ERROR;
    }
  
  // Return
  Tcl_SetResult(interp, "", TCL_VOLATILE);
  return TCL_OK;
}

/*============================================================================
**<AUTO EXTRACT>
**
** TCL VERB: segmentDiag
**
**<HTML>
**	Update running target selection diagnostics from a segment.
**</HTML>
**
**</AUTO>
**============================================================================
*/
static char segmentDiagCmd[] = "segmentDiag";
static int segmentDiagFlg = FTCL_ARGV_NO_LEFTOVERS;
static ftclArgvInfo segmentDiagTbl[] = {
   {NULL, FTCL_ARGV_HELP, NULL, NULL,
    "Update targetting diagnostics from a segment\n"},
   {"<segment>", FTCL_ARGV_STRING, NULL, NULL,
    "Handle to the segment"},
   {"<startLambda>", FTCL_ARGV_DOUBLE, NULL, NULL,
    "Beginning stripe lambda (degrees, -999=none)"},
   {"<endLambda>", FTCL_ARGV_DOUBLE, NULL, NULL,
    "Ending stripe lambda (degrees, -999=none)"},
   {"<diag>", FTCL_ARGV_STRING, NULL, NULL,
    "Handle to running diagnsotics (TA_DIAG)"},
   {NULL, FTCL_ARGV_END, NULL, NULL, NULL}
};

static int
tclSegmentDiag (ClientData clientData,
		 Tcl_Interp *interp,
		 int argc,
		 char **argv)
{
  ooHandle(arSegment) *segment = NULL;
  char *segmentHandle = NULL;
  double startLambda = -999;
  double endLambda = -999;
  TA_DIAG *diag = NULL;
  char *diagHandle = NULL;
  int status;

  // Parse the arguments
  segmentDiagTbl[1].dst = &segmentHandle;
  segmentDiagTbl[2].dst = &startLambda;
  segmentDiagTbl[3].dst = &endLambda;
  segmentDiagTbl[4].dst = &diagHandle;
  if ((status = shTclParseArgv(interp, &argc, argv, segmentDiagTbl,
			       segmentDiagFlg, segmentDiagCmd))
      != FTCL_ARGV_SUCCESS)
    return status;
   
  // Get handle to segment
  if (shTclAddrGetFromName (interp, segmentHandle, (void **)&segment,
			    "ooHandle(arSegment)") != TCL_OK)
    {
      Tcl_AppendResult(interp, segmentDiagCmd,
		       ": error getting handle to segment", NULL);
      return TCL_ERROR;
    }
   
  // Get handle to TA_DIAG
  if (shTclAddrGetFromName (interp, diagHandle, (void **)&diag,
			    "TA_DIAG") != TCL_OK)
    {
      Tcl_AppendResult(interp, segmentDiagCmd,
		       ": error getting handle to TA_DIAG", NULL);
      return TCL_ERROR;
    }

  // Do it
  if (tsSegmentDiag(*segment, startLambda, endLambda, diag) != SH_SUCCESS)
    {
      shTclInterpAppendWithErrStack(interp);
      return TCL_ERROR;
    }
  
  // Return
  Tcl_SetResult(interp, "", TCL_VOLATILE);
  return TCL_OK;
}

/*============================================================================
**<AUTO EXTRACT>
**
** TCL VERB: dbTargetUnselectFromChunk
**
**<HTML>
**	Unselect spectroscopic targets previously selected in a chunk.
**</HTML>
**
**</AUTO>
**============================================================================
*/
static char dbTargetUnselectFromChunkCmd[] = "dbTargetUnselectFromChunk";
static int dbTargetUnselectFromChunkFlg = FTCL_ARGV_NO_LEFTOVERS;
static ftclArgvInfo dbTargetUnselectFromChunkTbl[] = {
   {NULL, FTCL_ARGV_HELP, NULL, NULL,
    "Unselect spectroscopic targets previously selected in a chunk\n"},
   {"<chunk>", FTCL_ARGV_STRING, NULL, NULL,
    "Handle to chunk"},
   {"-force", FTCL_ARGV_CONSTANT, (void *) 1, NULL,
    "Force unselection regardless"},
   {NULL, FTCL_ARGV_END, NULL, NULL, NULL}
};

static int
tclDbTargetUnselectFromChunk (ClientData clientData,
			      Tcl_Interp *interp,
			      int argc,
			      char **argv)
{
  ooHandle(arChunk) *chunk = NULL;
  char *chunkHandle = NULL;
  int force = 0;
  int status;

  // Parse the arguments
  dbTargetUnselectFromChunkTbl[1].dst = &chunkHandle;
  dbTargetUnselectFromChunkTbl[2].dst = &force;
  if ((status = shTclParseArgv(interp, &argc, argv,
			       dbTargetUnselectFromChunkTbl,
			       dbTargetUnselectFromChunkFlg,
			       dbTargetUnselectFromChunkCmd))
      != FTCL_ARGV_SUCCESS)
    return status;
   
  // Get handle to chunk
  if (shTclAddrGetFromName (interp, chunkHandle, (void **)&chunk,
			    "ooHandle(arChunk)") != TCL_OK)
    {
      if (shTclAddrGetFromName (interp, chunkHandle, (void **)&chunk,
				"ooItr(arChunk)") != TCL_OK)
	{
	  Tcl_AppendResult(interp, dbTargetUnselectFromChunkCmd,
			   ": error getting handle to chunk", NULL);
	  return TCL_ERROR;
	}
    }
  
  // Verify that this chunk has been through target selection
  if (! force)
    if ((*chunk)->selectTargets().isValid() == oocFalse)
      {
	Tcl_AppendResult(interp, dbTargetUnselectFromChunkCmd,
			 ": chunk has not been through target selection",
			 NULL);
	return TCL_ERROR;
      }
   
  // Delete targeting report.
  ooHandle(arProduct) report = (*chunk)->selectTargets();
  ooDelete(report);

  // Initialize iterator over segments
  ooItr(arSegment) itr;
  if ((*chunk)->segments(itr, oocUpdate) != oocSuccess)
    {
      Tcl_AppendResult(interp, dbTargetUnselectFromChunkCmd,
		       ": couldn't initialize iterator over segments");
      return TCL_ERROR;
    }

  // For each segment ...
  while (itr.next())
    {
      // Targets selected in primary and secondary segments only.
      if (itr->status() != AR_SEGMENT_STATUS_PRIMARY &&
	  itr->status() != AR_SEGMENT_STATUS_SECONDARY)
	continue;
      
      // Unselect targets.
      if (tsSegmentTargetUnselect(itr) != SH_SUCCESS)
	{
	  shTclInterpAppendWithErrStack(interp);
	  return TCL_ERROR;
	}
    }

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

/*============================================================================
**<AUTO EXTRACT>
**
** TCL VERB: chunkCopyToSouthernSurvey
**
**<HTML>
**	Copy a chunk in the current version of the sky to the southern
**	survey, giving it a new skyVersion number.  The skyVersion number
**	must be greater than 1 and unique.
**</HTML>
**
**</AUTO>
**============================================================================
*/
static char chunkCopyToSouthernSurveyCmd[] = "chunkCopyToSouthernSurvey";
static int chunkCopyToSouthernSurveyFlg = FTCL_ARGV_NO_LEFTOVERS;
static ftclArgvInfo chunkCopyToSouthernSurveyTbl[] = {
   {NULL, FTCL_ARGV_HELP, NULL, NULL,
    "Copy a chunk to the southern survey\n"},
   {"<chunk>", FTCL_ARGV_STRING, NULL, NULL,
    "Handle to chunk"},
   {"<skyVersion>", FTCL_ARGV_INT, NULL, NULL,
    "Unique skyVersion number for new chunk"},
   {NULL, FTCL_ARGV_END, NULL, NULL, NULL}
};

static int
tclChunkCopyToSouthernSurvey (ClientData clientData,
			      Tcl_Interp *interp,
			      int argc,
			      char **argv)
{
  ooHandle(arChunk) *chunk = NULL;
  char *chunkHandle = NULL;
  int skyVersion = 0;
  int status;
  char predicate[100];

  // Parse the arguments
  chunkCopyToSouthernSurveyTbl[1].dst = &chunkHandle;
  chunkCopyToSouthernSurveyTbl[2].dst = &skyVersion;
  if ((status = shTclParseArgv(interp, &argc, argv,
			       chunkCopyToSouthernSurveyTbl,
			       chunkCopyToSouthernSurveyFlg,
			       chunkCopyToSouthernSurveyCmd))
      != FTCL_ARGV_SUCCESS)
    return status;
   
  // Fetch the global TCL variable "photo_segments" for container with all
  // segments.  Then fetch the associated handle.
  char *photo_segments = Tcl_GetVar(interp, "photo_segments", TCL_GLOBAL_ONLY);
  if (photo_segments == NULL)
    {
      Tcl_AppendResult(interp, chunkCopyToSouthernSurveyCmd,
		       ": global variable $photo_segments doesn't exist",
		       NULL);
      return TCL_ERROR;
    }
  ooHandle(ooContObj) *photoSegments;
  if (shTclAddrGetFromName (interp, photo_segments, (void **)&photoSegments,
			    "ooHandle(ooContObj)") != TCL_OK)
    {
      Tcl_AppendResult(interp, chunkCopyToSouthernSurveyCmd,
		       ": error getting handle to $photo_segments", NULL);
      return TCL_ERROR;
    }

  // Get handle to chunk
  if (shTclAddrGetFromName (interp, chunkHandle, (void **)&chunk,
			    "ooHandle(arChunk)") != TCL_OK)
    {
      if (shTclAddrGetFromName (interp, chunkHandle, (void **)&chunk,
				"ooItr(arChunk)") != TCL_OK)
	{
	  Tcl_AppendResult(interp, chunkCopyToSouthernSurveyCmd,
			   ": error getting handle to chunk", NULL);
	  return TCL_ERROR;
	}
    }
  
  // Assure skyVersion number is unique.
  if (skyVersion <= 1)
    {
      Tcl_AppendResult(interp, chunkCopyToSouthernSurveyCmd,
		       ": 'skyVersion' must be > 1", NULL);
      return TCL_ERROR;
    }
  sprintf(predicate, "skyVersion_ == %d", skyVersion);
  ooItr(arChunk) chunkItr;
  if (chunkItr.scan(*photoSegments, oocNoOpen, oocAll,predicate) != oocSuccess)
    {
      Tcl_AppendResult(interp, chunkCopyToSouthernSurveyCmd,
		       ": couldn't initialize iterator over chunks", NULL);
      return TCL_ERROR;
    }
  if (chunkItr.next())
    {
      Tcl_AppendResult(interp, chunkCopyToSouthernSurveyCmd,
		       ": chunk with this 'skyVersion' already exists", NULL);
      return TCL_ERROR;
    }

  // Initialize iterator over segments in old chunk
  ooItr(arSegment) itr;
  if ((*chunk)->segments(itr, oocRead) != oocSuccess)
    {
      Tcl_AppendResult(interp, chunkCopyToSouthernSurveyCmd,
		       ": couldn't initialize iterator over segments");
      return TCL_ERROR;
    }

  // For each old segment ...
  ooTVArray(ooHandle(arSegment)) newSegments;
  while (itr.next())
    {
      // Copy as a new segment and append to new segment list
      ooHandle(arSegment) newSeg =
	new(*photoSegments) arSegment(itr->framesPipelineRun(),
				      itr->startMu(),
				      itr->endMu(),
				      itr->field0(),
				      itr->nFields(),
				      itr->status());
      newSegments.extend(newSeg);
    }

  // Create the new chunk.
  ooHandle(arChunk) *newChunk = new ooHandle(arChunk);
  *newChunk = new(*photoSegments) arChunk((*chunk)->stripe(),
					  (*chunk)->startMu(),
					  (*chunk)->endMu(), newSegments,
					  skyVersion);
  if (! (*newChunk).isValid())
    {
      for (uint i = 0; i < newSegments.size(); i++)
	delete (arSegment*) newSegments[i];
      delete newChunk;
      Tcl_SetResult(interp, "chunkCopyToSouthernSurvey: couldn't create chunk",
		    TCL_VOLATILE);
      return TCL_ERROR;
    }

  // Return a handle to it
  char newChunkHandleName[HANDLE_NAMELEN];
  if (shTclHandleNew(interp, newChunkHandleName, "ooHandle(arChunk)",
		     (void *)newChunk) != TCL_OK)
    {
      Tcl_SetResult(interp, "chunkCopyToSouthernSurvey: error allocating new chunk TCL handle --- chunk was created",
		    TCL_VOLATILE);
      return TCL_ERROR;
    }
  Tcl_SetResult(interp, newChunkHandleName, TCL_VOLATILE);
  return TCL_OK;
}

/*============================================================================
**<AUTO EXTRACT>
**
** TCL VERB: segmentExport
**
**<HTML>
**	Export calibrated versions of all primary objects in a segment
**	to FITS files.  Optionally, a handle to a frames pipeline run may
**	be specified rather than a segment, in which case that run is
**	exported (all OK_RUN objects are exported).
**</HTML>
**
**</AUTO>
**============================================================================
*/
static char segmentExportCmd[] = "segmentExport";
static int segmentExportFlg = FTCL_ARGV_NO_LEFTOVERS;
static ftclArgvInfo segmentExportTbl[] = {
   {NULL, FTCL_ARGV_HELP, NULL, NULL,
    "Export calibrated versions of all primary objects in a segment to FITS files\n"},
   {"<segment>", FTCL_ARGV_STRING, NULL, NULL,
    "Handle to segment or frames pipeline run"},
   {"<outputDir>", FTCL_ARGV_STRING, NULL, NULL,
    "Directory to create exported tsObjc files in (defaults to current; if it contains the string %d, the %d is replaced with the camCol)"},
   {"<startLambda>", FTCL_ARGV_DOUBLE, NULL, NULL,
    "Beginning stripe lambda (degrees, -999=none)"},
   {"<endLambda>", FTCL_ARGV_DOUBLE, NULL, NULL,
    "Ending stripe lambda (degrees, -999=none)"},
   {"[export]", FTCL_ARGV_INT, NULL, NULL,
    "Export option --- 0=field files only, 1=targets only, 2=primaries only (default), 3=primaries and secondaries, 4=everything"},
   {"-southern", FTCL_ARGV_CONSTANT, (void *) 1, NULL,
    "Southern target select algs?"},
   {"-parents", FTCL_ARGV_CONSTANT, (void *) 1, NULL,
    "Export parent objects as well"},
   {"-noSplit", FTCL_ARGV_CONSTANT, (void *) 1, NULL,
    "One file, rather than one file per field (defaults to 1 file per field)"},
   {"-field0", FTCL_ARGV_INT, NULL, NULL,
    "First field to export (defaults to first field in the segment/run)"},
   {"-nFields", FTCL_ARGV_INT, NULL, NULL,
    "Export this number of fields only (starting with first field, default=0=all)"},
   {"-resolve", FTCL_ARGV_INT, NULL, NULL,
    "0=use OPDB resolution (default), 1=do an on-the-fly resolve for a frames pipeline run, obeying stripe eta limits, 2=do an on-the-fly resolve for a frames pipeline run, ignoring stripe eta limits"},
   {NULL, FTCL_ARGV_END, NULL, NULL, NULL}
};

static int
tclSegmentExport (ClientData clientData,
		  Tcl_Interp *interp,
		  int argc,
		  char **argv)
{
  ooHandle(arSegment) *segment = NULL;
  ooHandle(arFramesPipeRun) *fpr = NULL;
  char *segmentHandle = NULL;
  char *outputDir = NULL;
  int export = 2;
  int parents = 0;
  int noSplit = 0;
  int field0 = -1;
  int nFields = 0;
  double startLambda = -999;
  double endLambda = -999;
  int resolve = 0;
  int southern = 0;
  int status;

  // Parse the arguments
  segmentExportTbl[1].dst = &segmentHandle;
  segmentExportTbl[2].dst = &outputDir;
  segmentExportTbl[3].dst = &startLambda;
  segmentExportTbl[4].dst = &endLambda;
  segmentExportTbl[5].dst = &export;
  segmentExportTbl[6].dst = &southern;
  segmentExportTbl[7].dst = &parents;
  segmentExportTbl[8].dst = &noSplit;
  segmentExportTbl[9].dst = &field0;
  segmentExportTbl[10].dst = &nFields;
  segmentExportTbl[11].dst = &resolve;
  if ((status = shTclParseArgv(interp, &argc, argv, segmentExportTbl,
			       segmentExportFlg, segmentExportCmd))
      != FTCL_ARGV_SUCCESS)
    return status;
   
  // Get handle to segment/run
  ooHandle(arSegment) dummySegment;
  ooHandle(arFramesPipeRun) dummyFPR;
  if (shTclAddrGetFromName (interp, segmentHandle, (void **)&segment,
			    "ooHandle(arSegment)") != TCL_OK)
    {
      // Not a segment.  Must be a frames pipeline run.
      if (shTclAddrGetFromName (interp, segmentHandle, (void **)&fpr,
				"ooHandle(arFramesPipeRun)") != TCL_OK)
	{
	  Tcl_AppendResult(interp, segmentExportCmd,
			   ": error getting handle to segment/run", NULL);
	  return TCL_ERROR;
	}
      segment = &dummySegment;
    }
  else
    {
      if (resolve != 0)
	{
	  Tcl_AppendResult(interp, segmentExportCmd,
			   ": can't use -resolve option with segments");
	  return TCL_ERROR;
	}
      fpr = &dummyFPR;
    }

  // Set up cosmic color for use by atTransApply().
  taTransCosmicSet(cosmicColor, cosmicScatter);

  // Export calibrated versions of the primary objects
  int select = 0;
  int force = 0;
  int noUpdate = 1;
  if (tsSegmentCalibrate(*segment, *fpr, field0, nFields, export, parents,
			 !noSplit, select, force, noUpdate, resolve,
			 startLambda, endLambda, southern, outputDir)
      != SH_SUCCESS)
    {
      shTclInterpAppendWithErrStack(interp);
      return TCL_ERROR;
    }

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

/*============================================================================
**<AUTO EXTRACT>
**
** TCL VERB: objectCalibrate
**
**<HTML>
**	Return a calibrated version of the specified object as a TA_CALIB_OBJ.
**</HTML>
**
**</AUTO>
**============================================================================
*/
static char objectCalibrateCmd[] = "objectCalibrate";
static int objectCalibrateFlg = FTCL_ARGV_NO_LEFTOVERS;
static ftclArgvInfo objectCalibrateTbl[] = {
   {NULL, FTCL_ARGV_HELP, NULL, NULL,
    "Return a calibrated version of an object\n"},
   {"<obj>", FTCL_ARGV_STRING, NULL, NULL,
    "Handle to the input database object (ooHandle(arObject))"},
   {"<skyVersion>", FTCL_ARGV_INT, NULL, NULL,
    "0=drilling, 1=current"},
   {"-target", FTCL_ARGV_CONSTANT, (void *) 1, NULL,
    "Use target selection calibrations, rather than current best calibrations"},
   {"-raw", FTCL_ARGV_CONSTANT, (void *) 1, NULL,
    "Don't calibrate lengths, magnitudes, surface brightnesses, and angles"},
   {NULL, FTCL_ARGV_END, NULL, NULL, NULL}
};

static int
tclObjectCalibrate (ClientData clientData,
		    Tcl_Interp *interp,
		    int argc,
		    char **argv)
{
  ooHandle(arObject) *in = NULL;
  ooHandle(arObject) *itrIn = NULL;
  char *objectHandle = NULL;
  int skyVersion = 0;
  int target = 0;
  int raw = 0;
  int status;

  // Parse the arguments
  objectCalibrateTbl[1].dst = &objectHandle;
  objectCalibrateTbl[2].dst = &skyVersion;
  objectCalibrateTbl[3].dst = &target;
  objectCalibrateTbl[4].dst = &raw;
  if ((status = shTclParseArgv(interp, &argc, argv, objectCalibrateTbl,
			       objectCalibrateFlg, objectCalibrateCmd))
      != FTCL_ARGV_SUCCESS)
    return status;
  shAssert(skyVersion == 0 || skyVersion == 1);
   
  // Get handle to object
  if (shTclAddrGetFromName (interp, objectHandle, (void **)&in,
			    "ooHandle(arObject)") != TCL_OK)
    {
      if (shTclAddrGetFromName (interp, objectHandle, (void **)&itrIn,
				"ooItr(arObject)") != TCL_OK)
	{
	  Tcl_AppendResult(interp, objectCalibrateCmd,
			   ": error getting handle to object", NULL);
	  return TCL_ERROR;
	}
      *in = *itrIn;
    }

  // Set up cosmic color for use by atTransApply().
  taTransCosmicSet(cosmicColor, cosmicScatter);

  // Calibrate it
  TA_CALIB_OBJ *out = (TA_CALIB_OBJ*) shMalloc(sizeof(TA_CALIB_OBJ));
  if (tsObjCalibrate(*in, out, (target ? 0 : 1), raw, skyVersion)
      != SH_SUCCESS)
    {
      shFree(out);
      shTclInterpAppendWithErrStack(interp);
      return TCL_ERROR;
    }

  // Return a handle to the calibrated object
  char vName[HANDLE_NAMELEN];
  if (shTclHandleNew(interp, vName, "TA_CALIB_OBJ", (void *)out) != TCL_OK)
    {
      shFree(out);
      Tcl_AppendResult(interp, objectCalibrateCmd,
		       ": error allocating TCL handle", NULL);
      return TCL_ERROR;
    }
  Tcl_SetResult(interp, vName, TCL_VOLATILE);
  return TCL_OK;
}

//****************************************************************************/
//
// Declare my new tcl verbs to tcl
//
void
tsTclTargetsDeclare(Tcl_Interp *interp) 
{
  char *tclHelpFacil = "ts";
  shTclDeclare(interp, dbTargetSelectFromSegmentCmd,
	       (Tcl_CmdProc *)tclDbTargetSelectFromSegment,
	       (ClientData) 0,	(Tcl_CmdDeleteProc *)NULL, tclHelpFacil,
	       shTclGetArgInfo(interp, dbTargetSelectFromSegmentTbl,
			       dbTargetSelectFromSegmentFlg,
			       dbTargetSelectFromSegmentCmd),
	       shTclGetUsage(interp, dbTargetSelectFromSegmentTbl,
			     dbTargetSelectFromSegmentFlg,
			     dbTargetSelectFromSegmentCmd));
  shTclDeclare(interp, dbTargetUnselectFromChunkCmd,
	       (Tcl_CmdProc *)tclDbTargetUnselectFromChunk,
	       (ClientData) 0,	(Tcl_CmdDeleteProc *)NULL, tclHelpFacil,
	       shTclGetArgInfo(interp, dbTargetUnselectFromChunkTbl,
			       dbTargetUnselectFromChunkFlg,
			       dbTargetUnselectFromChunkCmd),
	       shTclGetUsage(interp, dbTargetUnselectFromChunkTbl,
			     dbTargetUnselectFromChunkFlg,
			     dbTargetUnselectFromChunkCmd));
  shTclDeclare(interp, chunkCopyToSouthernSurveyCmd,
	       (Tcl_CmdProc *)tclChunkCopyToSouthernSurvey,
	       (ClientData) 0,	(Tcl_CmdDeleteProc *)NULL, tclHelpFacil,
	       shTclGetArgInfo(interp, chunkCopyToSouthernSurveyTbl,
			       chunkCopyToSouthernSurveyFlg,
			       chunkCopyToSouthernSurveyCmd),
	       shTclGetUsage(interp, chunkCopyToSouthernSurveyTbl,
			     chunkCopyToSouthernSurveyFlg,
			     chunkCopyToSouthernSurveyCmd));
  shTclDeclare(interp, segmentStatsCmd,
	       (Tcl_CmdProc *)tclSegmentStats,
	       (ClientData) 0,	(Tcl_CmdDeleteProc *)NULL, tclHelpFacil,
	       shTclGetArgInfo(interp, segmentStatsTbl, segmentStatsFlg,
			       segmentStatsCmd),
	       shTclGetUsage(interp, segmentStatsTbl, segmentStatsFlg,
			     segmentStatsCmd));
  shTclDeclare(interp, segmentDiagCmd,
	       (Tcl_CmdProc *)tclSegmentDiag,
	       (ClientData) 0,	(Tcl_CmdDeleteProc *)NULL, tclHelpFacil,
	       shTclGetArgInfo(interp, segmentDiagTbl, segmentDiagFlg,
			       segmentDiagCmd),
	       shTclGetUsage(interp, segmentDiagTbl, segmentDiagFlg,
			     segmentDiagCmd));
  shTclDeclare(interp, segmentExportCmd,
	       (Tcl_CmdProc *)tclSegmentExport,
	       (ClientData) 0,	(Tcl_CmdDeleteProc *)NULL, tclHelpFacil,
	       shTclGetArgInfo(interp, segmentExportTbl,
			       segmentExportFlg,
			       segmentExportCmd),
	       shTclGetUsage(interp, segmentExportTbl,
			     segmentExportFlg,
			     segmentExportCmd));
  shTclDeclare(interp, objectCalibrateCmd,
	       (Tcl_CmdProc *)tclObjectCalibrate,
	       (ClientData) 0,	(Tcl_CmdDeleteProc *)NULL, tclHelpFacil,
	       shTclGetArgInfo(interp, objectCalibrateTbl,
			       objectCalibrateFlg,
			       objectCalibrateCmd),
	       shTclGetUsage(interp, objectCalibrateTbl,
			     objectCalibrateFlg,
			     objectCalibrateCmd));
}
