/*****************************************************************************
******************************************************************************
**<AUTO>
**
** FILE:	tclCalibrate.c
**
**<HTML>
**	This file contains TCL functions to calibrate object lists.
**</HTML>
**</AUTO>
**
**
** ENVIRONMENT:
**	ANSI C.
**
******************************************************************************
******************************************************************************
*/

#include <dervish.h>
#include <astrotools.h>
#include "taCalibObj.h"
#include "arObjectStatus.h"
#include "arObjectType.h"
#include "arDetectionFlag.h"
#include "taCosmic.h"


/*============================================================================
**<AUTO EXTRACT>
**
** TCL VERB: objsCalibrate
**
**<HTML>
**	Calibrate a CHAIN of uncalibrated objects.  The calibration is done
**	in place.  Thus, the TA_CALIB_OBJ chain should contain instrumental
**	quantities on input, and will contain calibrated quantities on output.
**	For DCR corrections in astrometric transformations, PSF magnitudes are
**	used for stars and model magnitudes for galaxies.
**	All ojbects status flags are set
**	appropriately as if this were a primary segment (with no mu bounds).
**	<p>
**	NOTE: The "offsetRa" and "offsetDec" fields are not calculated
**	correctly.  They are calculated using the final astrometric TRANS
**	structures, not the TRANS structures used by photo, and do not
**	incorporate the "rowOffset" and "colOffset" values determined by photo.
**</HTML>
**
**</AUTO>
**============================================================================
*/
static char objsCalibrateCmd[] = "objsCalibrate";
static int objsCalibrateFlg = FTCL_ARGV_NO_LEFTOVERS;
static ftclArgvInfo objsCalibrateTbl[] = {
  {NULL, FTCL_ARGV_HELP, NULL, NULL,
   "Calibrate an object list\n"},
  {"<chain>", FTCL_ARGV_STRING, NULL, NULL, "CHAIN of TA_CALIB_OBJ"},
  {"<ptrans>", FTCL_ARGV_STRING, NULL, NULL,
   "TA_PTRANS structure for photometric calibrations"},
  {"<u>", FTCL_ARGV_STRING, NULL, NULL,
   "u TRANS structure for astrometric calibrations"},
  {"<g>", FTCL_ARGV_STRING, NULL, NULL,
   "g TRANS structure for astrometric calibrations"},
  {"<r>", FTCL_ARGV_STRING, NULL, NULL,
   "r TRANS structure for astrometric calibrations"},
  {"<i>", FTCL_ARGV_STRING, NULL, NULL,
   "i TRANS structure for astrometric calibrations"},
  {"<z>", FTCL_ARGV_STRING, NULL, NULL,
   "z TRANS structure for astrometric calibrations"},
  {"<node>", FTCL_ARGV_DOUBLE, NULL, NULL,
   "Scanned (i.e., TRANS) great circle ascending node (degrees, J2000)"},
  {"<incl>", FTCL_ARGV_DOUBLE, NULL, NULL,
   "Scanned (i.e., TRANS) great circle inclination (degrees, J2000)"},
  {"<refFilter>", FTCL_ARGV_STRING, NULL, NULL,
   "Reference filter for primary positions of objects"},
  {"<expTime>", FTCL_ARGV_DOUBLE, NULL, NULL,
   "Exposure time (seconds)"},
  {"<stripeNode>", FTCL_ARGV_DOUBLE, NULL, NULL,
   "Stripe's great circle ascending node (degrees, J2000)"},
  {"<stripeIncl>", FTCL_ARGV_DOUBLE, NULL, NULL,
   "Stripe's great circle inclination (degrees, J2000)"},
  {"<nuMin>", FTCL_ARGV_DOUBLE, NULL, NULL,
   "Minimum stripe great circle latitude for primary region of scanline (degrees)"},
  {"<nuMax>", FTCL_ARGV_DOUBLE, NULL, NULL,
   "Maximum stripe great circle latitude for primary region of scanline (degrees)"},
  {"-etaMin", FTCL_ARGV_DOUBLE, NULL, NULL,
   "Minimum survey latitude for primary region of scanline (degrees)"},
  {"-etaMax", FTCL_ARGV_DOUBLE, NULL, NULL,
   "Maximum survey latitude for primary region of scanline (degrees)"},
  {"-lambdaMin", FTCL_ARGV_DOUBLE, NULL, NULL,
   "Minimum survey longitude for primary region of scanline (degrees)"},
  {"-lambdaMax", FTCL_ARGV_DOUBLE, NULL, NULL,
   "Maximum survey longitude for primary region of scanline (degrees)"},
  {"-firstField", FTCL_ARGV_CONSTANT, (void *) 1, NULL,
   "Select if objects belong to first field in segment"},
  {NULL, FTCL_ARGV_END, NULL, NULL, NULL}
};

static int
tclObjsCalibrate (ClientData clientData,
		  Tcl_Interp *interp,
		  int argc,
		  char **argv)
{
  CHAIN *chain = NULL;
  TA_PTRANS *ptrans = NULL;
  TRANS *trans[TA_NFILTERS] = {NULL, NULL, NULL, NULL, NULL};
  char *chainHandle = NULL;
  char *ptransHandle = NULL;
  char *transHandle[TA_NFILTERS] = {NULL, NULL, NULL, NULL, NULL};
  double node, incl, expTime;
  double stripeNode, stripeIncl;
  float cosmicMag[5], cosmicMagScatter[5];
  double nuMin, nuMax, etaMin = -100000., etaMax = 100000.;
  double lambdaMin = -100000., lambdaMax = 100000.;
  char *refFilter;
  int firstField = 0, status;

  TA_CALIB_OBJ *obj;
  CURSOR_T curse;
  int i, inSurvey;
  double mu, nu;

  /* HARDWIRE: frame sizes */
  int rowsPerField = 1361;
  int overlapRows = 128;
  double rowMin = overlapRows / 2.;
  double rowMax = rowsPerField + overlapRows / 2.;

  /* Parse the arguments */
  objsCalibrateTbl[1].dst = &chainHandle;
  objsCalibrateTbl[2].dst = &ptransHandle;
  objsCalibrateTbl[3].dst = &transHandle[0];
  objsCalibrateTbl[4].dst = &transHandle[1];
  objsCalibrateTbl[5].dst = &transHandle[2];
  objsCalibrateTbl[6].dst = &transHandle[3];
  objsCalibrateTbl[7].dst = &transHandle[4];
  objsCalibrateTbl[8].dst = &node;
  objsCalibrateTbl[9].dst = &incl;
  objsCalibrateTbl[10].dst = &refFilter;
  objsCalibrateTbl[11].dst = &expTime;
  objsCalibrateTbl[12].dst = &stripeNode;
  objsCalibrateTbl[13].dst = &stripeIncl;
  objsCalibrateTbl[14].dst = &nuMin;
  objsCalibrateTbl[15].dst = &nuMax;
  objsCalibrateTbl[16].dst = &etaMin;
  objsCalibrateTbl[17].dst = &etaMax;
  objsCalibrateTbl[18].dst = &lambdaMin;
  objsCalibrateTbl[19].dst = &lambdaMax;
  objsCalibrateTbl[20].dst = &firstField;
  if ((status = shTclParseArgv(interp, &argc, argv, objsCalibrateTbl,
			       objsCalibrateFlg, objsCalibrateCmd))
      != FTCL_ARGV_SUCCESS)
    return status;
   
  /* Get handle to chain */
  if (shTclAddrGetFromName (interp, chainHandle, (void **)&chain, "CHAIN")
      != TCL_OK)
    {
      Tcl_AppendResult(interp, objsCalibrateCmd,
		       ": error getting handle to chain", NULL);
      return TCL_ERROR;
    }
  
  /* Verify that chain is for TA_CALIB_OBJ */
  if (shChainTypeGet(chain) != shTypeGetFromName("TA_CALIB_OBJ"))
    {
      Tcl_AppendResult(interp, objsCalibrateCmd,
		       ": input CHAIN not of type TA_CALIB_OBJ", NULL);
      return TCL_ERROR;
    }

  /* Get handle to PTRANS */
  if (shTclAddrGetFromName (interp, ptransHandle, (void **)&ptrans, "TA_PTRANS")
      != TCL_OK)
    {
      Tcl_AppendResult(interp, objsCalibrateCmd,
		       ": error getting handle to ptrans", NULL);
      return TCL_ERROR;
    }
  
  /* Get handles to TRANS structures */
  for (i = 0; i < TA_NFILTERS; i++)
    if (shTclAddrGetFromName (interp, transHandle[i], (void **)&trans[i],
    "TRANS") != TCL_OK)
      {
	Tcl_AppendResult(interp, objsCalibrateCmd,
			 ": error getting handle to trans", NULL);
	return TCL_ERROR;
      }
  
  /* Set up cosmic color for use by atTransApply(). */
  taTransCosmicSet(cosmicColor, cosmicScatter);

  /* For each object ... */
  curse = shChainCursorNew(chain);
  while ((obj = (TA_CALIB_OBJ*) shChainWalk(chain, curse, NEXT)) != NULL)
    {
      /* Calibrate it */
      if (taObjCalibrate(obj, ptrans, (const TRANS**)trans, 
			 (const TRANS**)trans, node, incl,
			 refFilter[0], expTime, 0) != SH_SUCCESS)
	{
	  shTclInterpAppendWithErrStack(interp);
	  return TCL_ERROR;
	}

      /* Set status flags as if for a primary segment. */
      obj->status = AR_OBJECT_STATUS_SET;
      if (taGood(obj->objc_flags))
	{
	  obj->status |= AR_OBJECT_STATUS_GOOD;
	  if (obj->objc_rowc.val >= rowMin && obj->objc_rowc.val < rowMax)
	    {
	      obj->status |= (AR_OBJECT_STATUS_OK_RUN | 
			      AR_OBJECT_STATUS_RESOLVED |
			      AR_OBJECT_STATUS_PSEGMENT);
	      if (firstField) obj->status |= AR_OBJECT_STATUS_FIRST_FIELD;

	      /* Only objects of class STAR, GALAXY, UNK, and SKY will be
	       * classed as either SECONDARY or PRIMARY. */
	      inSurvey = (obj->objc_type == AR_OBJECT_TYPE_STAR ||
			  obj->objc_type == AR_OBJECT_TYPE_GALAXY ||
			  obj->objc_type == AR_OBJECT_TYPE_UNK ||
			  obj->objc_type == AR_OBJECT_TYPE_SKY);


	      /* Apply geometry cuts */
	      atEqToGC(obj->ra, obj->dec, &mu, &nu, stripeNode, stripeIncl);
	      if (nu >= nuMin && nu < nuMax)
		{
		  obj->status |= AR_OBJECT_STATUS_OK_SCANLINE;
		  if (obj->eta >= etaMin && obj->eta < etaMax)
		    {
		      obj->status |= AR_OBJECT_STATUS_OK_STRIPE;
		      if (inSurvey)
			{
			  if (obj->lambda>=lambdaMin && obj->lambda<lambdaMax)
			    obj->status |= AR_OBJECT_STATUS_PRIMARY;
			  else
			    obj->status |= AR_OBJECT_STATUS_SECONDARY;
			}
		    }
		  else
		    {
		      if (inSurvey) obj->status |= AR_OBJECT_STATUS_SECONDARY;
		    }
		}
	      else
		{
		  if (inSurvey) obj->status |= AR_OBJECT_STATUS_SECONDARY;
		}
	    }
	}
    }
  shChainCursorDel(chain, curse);
  
  /* Return */
  Tcl_SetResult(interp, "", TCL_VOLATILE);
  return TCL_OK;
}

/*****************************************************************************
 *
 * Declare my new tcl verbs to tcl
 */
void
taTclCalibrateDeclare(Tcl_Interp *interp) 
{
  char *tclHelpFacil = "ta";
  shTclDeclare(interp, objsCalibrateCmd,
	       (Tcl_CmdProc *)tclObjsCalibrate,
	       (ClientData) 0,	(Tcl_CmdDeleteProc *)NULL, tclHelpFacil,
	       shTclGetArgInfo(interp, objsCalibrateTbl, objsCalibrateFlg,
			       objsCalibrateCmd),
	       shTclGetUsage(interp, objsCalibrateTbl, objsCalibrateFlg,
			     objsCalibrateCmd));
}
