/*****************************************************************************
******************************************************************************
**<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 <arObjectStatus.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;

  // Bit masks used to determine how the mask field is set
  int orMask = AR_TMASK_TILED | AR_TMASK_DECOLLIDED | AR_TMASK_COVERED;
  int andMask = AR_TMASK_REMOVED | AR_TMASK_CULLED;

  // Loop over all untiled spectroscopic targets
  int tid = -1;
  char predicate[80];
  sprintf(predicate, "status_ == %d", AR_TSTATUS_UNTILED);
  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 not a tilable target.
      ooHandle(arObject) object;
      itr->object(object, oocRead);
      if (! (object->primaryType(0) & priMask) &&
	  ! (object->secondaryType(0) & secMask))
	  continue;

      // Verify object is both a TARGET and a PRIMARY
      if (! (object->status(0) & AR_OBJECT_STATUS_PRIMARY) ||
	  ! (object->status(0) & AR_OBJECT_STATUS_TARGET))
	{
	  //taGenericChainDestroy(ch);
	  //Tcl_AppendResult(interp, untiledToChainCmd,
	  //		   ": target not a PRIMARY or TARGET", NULL);
	  //return TCL_ERROR;
	  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(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 = object->priority(0);
      obj->tileId = -1;
      obj->collisionGroup = -1;
      obj->mask = 0;
      ooVArray(arTileInfo) info = itr->tileInfo();
      for (int i = 0; i < info.size(); i++)
	obj->mask = ((obj->mask & orMask) | (info[i].mask() & orMask)) |
	  ((obj->mask & andMask) & (info[i].mask() & andMask));
      // TODO: remove following lines once culling is no longer needed
      obj->primTarget = object->primaryType(0);
      obj->secTarget = object->secondaryType(0);
      obj->objc_rowc = object->objc_rowc().value();
      obj->objc_colc = object->objc_colc().value();
      for (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 unculled spectroscopic
**	targets.
**</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 unculled spectroscopic targets\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 unculled spectroscopic targets
  int tid = -1;
  char predicate[80];
  sprintf(predicate, "status_ != %d", AR_TSTATUS_CULLED);
  ooItr(arTarget) itr;
  if (itr.scan(*targetCluster, oocRead, oocAll, predicate) != oocSuccess)
    {
      taGenericChainDestroy(ch);
      Tcl_AppendResult(interp, targetsToChainCmd,
		       ": error initializing iterator over targets", NULL);
      return TCL_ERROR;
    }
  while (itr.next())
    {
      // Verify object is both a TARGET and a PRIMARY
      ooHandle(arObject) object;
      itr->object(object, oocRead);
      if (! (object->status(0) & AR_OBJECT_STATUS_PRIMARY) ||
	  ! (object->status(0) & AR_OBJECT_STATUS_TARGET))
	{
	  //taGenericChainDestroy(ch);
	  //Tcl_AppendResult(interp, untiledToChainCmd,
	  //		   ": target not a PRIMARY or TARGET", NULL);
	  //return TCL_ERROR;
	  continue;
	}

      // 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(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 = object->primaryType(0);
      obj->secTarget = object->secondaryType(0);
      obj->priority = object->priority(0);
      // 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;
}

/*============================================================================
**<AUTO EXTRACT>
**
** TCL VERB: targetsStats
**
**<HTML>
**	Utility routine to examine targets.
**</HTML>
**
**</AUTO>
**============================================================================
*/
static char targetsStatsCmd[] = "targetsStats";
static int targetsStatsFlg = FTCL_ARGV_NO_LEFTOVERS;
static ftclArgvInfo targetsStatsTbl[] = {
   {NULL, FTCL_ARGV_HELP, NULL, NULL,
    "Utility routine to examine targets\n"},
   {"[predicate]", FTCL_ARGV_STRING, NULL, NULL,
    "Limit targets to this objectivity predicate"},
   {NULL, FTCL_ARGV_END, NULL, NULL, NULL}
};

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

  // Parse the arguments
  targetsStatsTbl[1].dst = &predicate;
  if ((status = shTclParseArgv(interp, &argc, argv, targetsStatsTbl,
			       targetsStatsFlg, targetsStatsCmd))
      != 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, targetsStatsCmd,
		       ": 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, targetsStatsCmd,
		       ": error getting handle to $photo_targets", NULL);
      return TCL_ERROR;
    }

  // Mode to open objects in
  ooMode mode = oocRead;

  // Loop over all spectroscopic targets
  ooItr(arTarget) itr;
  if (itr.scan(*targetCluster, mode, oocAll, predicate) != oocSuccess)
    {
      Tcl_AppendResult(interp, targetsStatsCmd,
		       ": error initializing iterator over targets", NULL);
      return TCL_ERROR;
    }
  int nObjects[2] = {0,0};
  int nPrimaries[2] = {0,0};
  int nSecondaries[2] = {0,0};
  int nNone[2] = {0,0};
  int nPrimDup[2] = {0,0};
  int nSecDup[2] = {0,0};
  while (itr.next())
    {
      nObjects[0]++;
      if (nObjects[0] % 10000 == 0)
	{
	  printf("%8d\n", nObjects[0]);
	  fflush(stdout);
	}

      ooHandle(arObject) object;
      itr->object(object, oocRead);
      if (! object.isValid())
	{
	  Tcl_AppendResult(interp, targetsStatsCmd,
			   ": invalid object", NULL);
	  return TCL_ERROR;
	}
      if (object->status(0) & AR_OBJECT_STATUS_PRIMARY)
	{
	  nPrimaries[0]++;
	  if (object->primaryType(0) != 0) nPrimaries[1]++;
	}
      else if (object->status(0) & AR_OBJECT_STATUS_SECONDARY)
	{
	  if (itr->run() != 752 && itr->run() != 756 && itr->run() != 745)
	    {
	      nNone[0]++;
	      printf("hmm: %d %d %d %d %d %x\n", itr->run(), itr->rerun(),
		     itr->camCol(), itr->field(), itr->id(),
		     object->status(0));
	    }
	  else
	    {
	      nSecondaries[0]++;
	      if (object->primaryType(0) != 0) nSecondaries[1]++;
	    }
	}
      else
	{
	  //printf("UNRESOLVED: %d %d %d %d %d %x\n", itr->run(), itr->rerun(),
	  //	 itr->camCol(), itr->field(), itr->id(),
	  //	 object->status(0));
	  nNone[0]++;
	  if (object->primaryType(0) != 0) nNone[1]++;
	}
      continue;
      //if (itr->run() == 94 || itr->run() == 125 ||
      //  itr->run() == 1056 || itr->run() == 1033)
      //continue;
      ooItr(arObject) matches;
      if (! object->matches(matches))
	{
	  Tcl_AppendResult(interp, targetsStatsCmd,
			   ": couldn't initialize iterator over matches",
			   NULL);
	  return TCL_ERROR;
	}
      while (matches.next())
	{
	  ooHandle(arTarget) t = matches->spectroscopicTarget();
	  if (! t.isValid()) continue;
	  char *priString = "PRI";
	  char *secString = "SEC";
	  char *unString = "UNR";
	  char *firstString, *secondString;
	  if (object->status(0) & AR_OBJECT_STATUS_PRIMARY)
	    {
	      nPrimDup[0]++;
	      if (object->primaryType(0) != 0) nPrimDup[1]++;
	      firstString = priString;
	    }
	  else if (object->status(0) & AR_OBJECT_STATUS_SECONDARY)
	    {
	      nSecDup[0]++;
	      if (object->primaryType(0) != 0) nSecDup[1]++;
	      firstString = secString;
	    }
	  else
	    {
	      firstString = unString;
	    }
	  if (matches->status(0) & AR_OBJECT_STATUS_PRIMARY)
	    secondString = priString;
	  else if (matches->status(0) & AR_OBJECT_STATUS_SECONDARY)
	    secondString = secString;
	  else
	    secondString = unString;
	  printf("%s1 %d-%d-%d-%d-%d:%c -> %s2 %d-%d-%d-%d-%d:%c\n",
		 firstString, itr->run(), itr->rerun(), itr->camCol(),
		 itr->field(), itr->id(),
		 (itr->status() == AR_TSTATUS_TILED ? 'y' : 'n'),
		 secondString,
		 matches->run(), matches->rerun(), matches->camCol(),
		 matches->field(), matches->id(),
		 (t->status() == AR_TSTATUS_TILED ? 'y' : 'n'));
	}

      if (object->run() != itr->run() || object->rerun() != itr->rerun() || object->camCol() != itr->camCol() || object->id() != itr->id())
	printf("mismatch %d %d %d %d %d %d %d %d\n", nObjects, nPrimaries, nSecondaries, itr->run(), itr->rerun(), itr->camCol(), itr->field(), itr->id());
    }
  printf("\n");

  char results[100];
  sprintf(results, "%d %d(%d) %d(%d) %d(%d) %d(%d) %d(%d)",
	  nObjects[0], nPrimaries[0], nPrimaries[1], nSecondaries[0],
	  nSecondaries[1], nNone[0], nNone[1], nPrimDup[0], nPrimDup[1],
	  nSecDup[0], nSecDup[1]);
  Tcl_SetResult(interp, results, 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));
  shTclDeclare(interp, targetsStatsCmd,
	       (Tcl_CmdProc *)tcltargetsStats,
	       (ClientData) 0,	(Tcl_CmdDeleteProc *)NULL, tclHelpFacil,
	       shTclGetArgInfo(interp, targetsStatsTbl, targetsStatsFlg,
			       targetsStatsCmd),
	       shTclGetUsage(interp, targetsStatsTbl, targetsStatsFlg,
			     targetsStatsCmd));
}
