/*****************************************************************************
******************************************************************************
**<AUTO>
**
** FILE:	tclTile.cc
**
**<HTML>
**	This file contains TCL functions to support tiling in the OPDB.
**</HTML>
**</AUTO>
**
**
** ENVIRONMENT:
**	C++.
**
** REQUIRED PRODUCTS:
**	photoSch
**
******************************************************************************
******************************************************************************
*/

#include <arSphericalCoord.h>
#include <arTarget.h>
#include <arFramesPipeRun.h>
#include <arAstromCalib.h>
#include <arFinalPhotoCalib.h>
#include <arTile.h>
#include <arObject.h>
#include <arObjectData.h>
#include <arObjectList.h>
#include <arFieldPTrans.h>
#include <arTiledTarget.h>
#include <arDetection.h>
#include <dervish.h>
#include <astrotools.h>
#include <taCosmic.h>
#include <taCalibObj.h>
#include "tsTile.h"

/*============================================================================
**<AUTO EXTRACT>
**
** TCL VERB: untiledToChain
**
**<HTML>
**	Return a chain of TS_TILABLE_OBJECT containing all untiled
**	tilable spectroscopic targets.
**</HTML>
**
**</AUTO>
**============================================================================
*/
static char untiledToChainCmd[] = "untiledToChain";
static int untiledToChainFlg = FTCL_ARGV_NO_LEFTOVERS;
static ftclArgvInfo untiledToChainTbl[] = {
   {NULL, FTCL_ARGV_HELP, NULL, NULL,
    "Return a chain of all untiled spectroscopic targets\n"},
   {NULL, FTCL_ARGV_END, NULL, NULL, NULL}
};

static int
tclUntiledToChain (ClientData clientData,
		   Tcl_Interp *interp,
		   int argc,
		   char **argv)
{
  int status;
  // Parse the arguments
  if ((status = shTclParseArgv(interp, &argc, argv, untiledToChainTbl,
			       untiledToChainFlg, untiledToChainCmd))
      != FTCL_ARGV_SUCCESS)
    return status;

  // Fetch the global TCL variable "photo_targets" for container with all
  // spectroscopic targets.  Then fetch the associated handle.
  char *photo_targets = Tcl_GetVar(interp, "photo_targets", TCL_GLOBAL_ONLY);
  if (photo_targets == NULL)
    {
      Tcl_AppendResult(interp, untiledToChainCmd,
		       ": 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, untiledToChainCmd,
		       ": error getting handle to $photo_targets", NULL);
      return TCL_ERROR;
    }

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

  // Initialize chain of tilable objects
  CHAIN *ch = shChainNew("TS_TILABLE_OBJ");
  if (ch == NULL)
    {
      Tcl_AppendResult(interp, untiledToChainCmd, ": couldn't allocate chain",
		       NULL);
      return TCL_ERROR;
    }

  // Only QSOs, galaxies, hot standards, and brown dwarfs are tiled.
  int priMask =
    AR_TARGET_QSO_HIZ |
    AR_TARGET_QSO_CAP |
    AR_TARGET_QSO_SKIRT |
    AR_TARGET_QSO_FIRST_CAP |
    AR_TARGET_QSO_FIRST_SKIRT |
    AR_TARGET_GALAXY_RED |
    AR_TARGET_GALAXY |
    AR_TARGET_GALAXY_BIG |
    AR_TARGET_GALAXY_BRIGHT_CORE |
    AR_TARGET_STAR_BROWN_DWARF;
  int secMask = TAR_TARGET_HOT_STD;

  // Loop over all untiled spectroscopic targets
  int tid = -1;
  char predicate[80];
  sprintf(predicate, "status_ != %d", AR_TSTATUS_TILED);
  ooItr(arTarget) itr;
  if (itr.scan(*targetCluster, oocRead, oocAll, predicate)
      != oocSuccess)
    {
      taGenericChainDestroy(ch);
      Tcl_AppendResult(interp, untiledToChainCmd,
		       ": error initializing iterator over targets", NULL);
      return TCL_ERROR;
    }
  while (itr.next())
    {
      // Skip if 0 priority, and therefore not in the spectroscopic samples.
      if (itr->priority() == 0) continue;

      // Skip if not a tilable target.
      if (! (itr->primaryType() & priMask) &&
	  ! (itr->secondaryType() & secMask))
	  continue;

      // Add to chain of tilable objects.
      TS_TILABLE_OBJ *obj = (TS_TILABLE_OBJ *)shMalloc(sizeof(TS_TILABLE_OBJ));
      if (obj == NULL)
	{
	  taGenericChainDestroy(ch);
	  Tcl_AppendResult(interp, untiledToChainCmd,
			   ": couldn't allocate object", NULL);
	  return TCL_ERROR;
	}
      if (shChainElementAddByPos(ch, (void *)obj, "TS_TILABLE_OBJ", TAIL,
				 AFTER) != SH_SUCCESS)
	{
	  taGenericChainDestroy(ch);
	  Tcl_AppendResult(interp, untiledToChainCmd,": couldn't append chain",
			   NULL);
	  return TCL_ERROR;
	}
      ooHandle(arObject) object;
      itr->object(object, oocRead);
      ooHandle(arObjectList) list;
      object->list(list);
      ooHandle(arFramesPipeRun) fpr;
      list->framesPipelineRun(fpr, oocRead);
      obj->run = fpr->run();
      obj->rerun = fpr->rerun();
      obj->col = fpr->camCol();
      obj->field = list->field();
      obj->id = object->id();
      obj->tid = ++tid;
      // All Frames Pipeline Runs are required when stuffed to have used r' as
      // their reference filter.  Thus, we always want the TRANS structure for
      // the r' CCD.
      int camRow = 1;
      int ccd = 10 * camRow + fpr->camCol();
      ooHandle(arAstromCalib) astrom;
      fpr->currentAstrometricCalibration(astrom, oocRead);
      TRANS trans = astrom->astrotoolsTrans(ccd, list->field());
      ooHandle(arFinalPhotoCalib) photom;
      fpr->currentPhotometricCalibration(photom, oocRead);
      arFieldPTrans ptrans = photom->ptrans(list->field());
      arSphericalCoord coord = object->gcCoord(trans, &ptrans);
      atGCToEq(coord.longitude(), coord.latitude(), &obj->ra, &obj->dec,
	       astrom->node(), astrom->incl());
      obj->priority = itr->priority();
      obj->tileId = -1;
      obj->tiFlag = 3;
      // TODO: remove following lines once culling is no longer needed
      obj->primTarget = itr->primaryType();
      obj->secTarget = itr->secondaryType();
      obj->objc_rowc = object->objc_rowc().value();
      obj->objc_colc = object->objc_colc().value();
      for (int i = 0; i < 5; i++)
	obj->psfMag[i] = object->psfMag((arFilter)i).value();
    }

  // Return the chain
  char vName[HANDLE_NAMELEN];
  if (shTclHandleNew(interp, vName, "CHAIN", (void *)ch) != TCL_OK)
    {
      // Error.  Delete the chain and return an error.
      taGenericChainDestroy(ch);
      Tcl_AppendResult(interp, untiledToChainCmd,
		       ": error allocating chain TCL handle", NULL);
      return TCL_ERROR;
    }
  Tcl_SetResult(interp, vName, TCL_VOLATILE);
  return TCL_OK;
}

/*============================================================================
**<AUTO EXTRACT>
**
** TCL VERB: targetsToChain
**
**<HTML>
**	Return a chain of TS_TARGET containing all spectroscopic targets
**	with positive priority.
**</HTML>
**
**</AUTO>
**============================================================================
*/
static char targetsToChainCmd[] = "targetsToChain";
static int targetsToChainFlg = FTCL_ARGV_NO_LEFTOVERS;
static ftclArgvInfo targetsToChainTbl[] = {
   {NULL, FTCL_ARGV_HELP, NULL, NULL,
    "Return a chain of all spectroscopic targets with positive priority\n"},
   {NULL, FTCL_ARGV_END, NULL, NULL, NULL}
};

static int
tcltargetsToChain (ClientData clientData,
		   Tcl_Interp *interp,
		   int argc,
		   char **argv)
{
  int status;
  // Parse the arguments
  if ((status = shTclParseArgv(interp, &argc, argv, targetsToChainTbl,
			       targetsToChainFlg, targetsToChainCmd))
      != FTCL_ARGV_SUCCESS)
    return status;

  // Fetch the global TCL variable "photo_targets" for container with all
  // spectroscopic targets.  Then fetch the associated handle.
  char *photo_targets = Tcl_GetVar(interp, "photo_targets", TCL_GLOBAL_ONLY);
  if (photo_targets == NULL)
    {
      Tcl_AppendResult(interp, targetsToChainCmd,
		       ": 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, targetsToChainCmd,
		       ": error getting handle to $photo_targets", NULL);
      return TCL_ERROR;
    }

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

  // Initialize chain of targets.
  CHAIN *ch = shChainNew("TS_TARGET");
  if (ch == NULL)
    {
      Tcl_AppendResult(interp, targetsToChainCmd, ": couldn't allocate chain",
		       NULL);
      return TCL_ERROR;
    }

  // Loop over all spectroscopic targets with positive priority
  int tid = -1;
  ooItr(arTarget) itr;
  if (itr.scan(*targetCluster, oocRead, oocAll, "priority_ > 0") != oocSuccess)
    {
      taGenericChainDestroy(ch);
      Tcl_AppendResult(interp, targetsToChainCmd,
		       ": error initializing iterator over targets", NULL);
      return TCL_ERROR;
    }
  while (itr.next())
    {
      // Add to chain of targets.
      TS_TARGET *obj = (TS_TARGET *)shMalloc(sizeof(TS_TARGET));
      if (obj == NULL)
	{
	  taGenericChainDestroy(ch);
	  Tcl_AppendResult(interp, targetsToChainCmd,
			   ": couldn't allocate object", NULL);
	  return TCL_ERROR;
	}
      if (shChainElementAddByPos(ch, (void *)obj, "TS_TARGET", TAIL,
				 AFTER) != SH_SUCCESS)
	{
	  taGenericChainDestroy(ch);
	  Tcl_AppendResult(interp, targetsToChainCmd,": couldn't append chain",
			   NULL);
	  return TCL_ERROR;
	}
      ooHandle(arObject) object;
      itr->object(object, oocRead);
      ooHandle(arObjectList) list;
      object->list(list);
      ooHandle(arFramesPipeRun) fpr;
      list->framesPipelineRun(fpr, oocRead);
      obj->run = fpr->run();
      obj->rerun = fpr->rerun();
      obj->col = fpr->camCol();
      obj->field = list->field();
      obj->id = object->id();
      obj->tid = ++tid;
      // All Frames Pipeline Runs are required when stuffed to have used r' as
      // their reference filter.  Thus, we always want the TRANS structure for
      // the r' CCD.
      int camRow = 1;
      int ccd = 10 * camRow + fpr->camCol();
      ooHandle(arAstromCalib) astrom;
      fpr->currentAstrometricCalibration(astrom, oocRead);
      TRANS trans = astrom->astrotoolsTrans(ccd, list->field());
      ooHandle(arFinalPhotoCalib) photom;
      fpr->currentPhotometricCalibration(photom, oocRead);
      arFieldPTrans ptrans = photom->ptrans(list->field());
      arSphericalCoord coord = object->gcCoord(trans, &ptrans);
      atGCToEq(coord.longitude(), coord.latitude(), &obj->ra, &obj->dec,
	       astrom->node(), astrom->incl());
      obj->primTarget = itr->primaryType();
      obj->secTarget = itr->secondaryType();
      obj->priority = itr->priority();
      // Return id of first tile to which it was assigned as a non-QA object
      ooItr(arTiledTarget) tiledTargets;
      char predicate[80];
      sprintf(predicate, "holeType_ != %d", AR_HOLE_QA);
      if (itr->tiledTargets(tiledTargets, oocRead, oocAll, predicate)
	  != oocSuccess)
	{
	  taGenericChainDestroy(ch);
	  Tcl_AppendResult(interp, targetsToChainCmd,
			   ": error initializing iterator over targets", NULL);
	  return TCL_ERROR;
	}
      if (tiledTargets.next())
	{
	  ooHandle(arTile) tile;
	  tile = tiledTargets->tile(tile, oocRead);
	  obj->tileId = tile->id();
	}
      else
	{
	  obj->tileId = -1;
	}
      obj->tiFlag = itr->status();
      for (int i = 0; i < 5; i++)
	obj->mag[i] = object->fiberMag((arFilter)i).value();
      ooHandle(arObjectData) data;
      data = object->data(data, oocRead);
      arDetection detection = data->detection(AR_R);
      obj->starL = detection.star_L();
      obj->expL = detection.exp_L();
      obj->deVaucL = detection.deV_L();
    }

  // Return the chain
  char vName[HANDLE_NAMELEN];
  if (shTclHandleNew(interp, vName, "CHAIN", (void *)ch) != TCL_OK)
    {
      // Error.  Delete the chain and return an error.
      taGenericChainDestroy(ch);
      Tcl_AppendResult(interp, targetsToChainCmd,
		       ": error allocating chain TCL handle", NULL);
      return TCL_ERROR;
    }
  Tcl_SetResult(interp, vName, TCL_VOLATILE);
  return TCL_OK;
}

//****************************************************************************/
//
// Declare my new tcl verbs to tcl
//
void
tsTclTileDeclare(Tcl_Interp *interp) 
{
  char *tclHelpFacil = "ts";
  shTclDeclare(interp, untiledToChainCmd,
	       (Tcl_CmdProc *)tclUntiledToChain,
	       (ClientData) 0,	(Tcl_CmdDeleteProc *)NULL, tclHelpFacil,
	       shTclGetArgInfo(interp, untiledToChainTbl, untiledToChainFlg,
			       untiledToChainCmd),
	       shTclGetUsage(interp, untiledToChainTbl, untiledToChainFlg,
			     untiledToChainCmd));
  shTclDeclare(interp, targetsToChainCmd,
	       (Tcl_CmdProc *)tcltargetsToChain,
	       (ClientData) 0,	(Tcl_CmdDeleteProc *)NULL, tclHelpFacil,
	       shTclGetArgInfo(interp, targetsToChainTbl, targetsToChainFlg,
			       targetsToChainCmd),
	       shTclGetUsage(interp, targetsToChainTbl, targetsToChainFlg,
			     targetsToChainCmd));
}
