/*****************************************************************************
******************************************************************************
**<AUTO>
**
** FILE:	completetile.c
**
**<HTML>
**	This file contains C functions to fill up the fibers on a plate
	after tiling.
**</HTML>
**</AUTO>
**
**
** ENVIRONMENT:
**	ANSI C.
**
******************************************************************************
******************************************************************************
*/
#include <math.h>
#include <dervish.h>
#include "arTargetType.h"
#include "taCompletetile.h"

#define UNRESERVEDFIBERSPERPLATE 615
#define PLATE_RADIUS 1.49
#define PLATE_CENTER_RADIUS 0.02
#define MIN_FIBER_SPACING 0.01528
#define BLANK_SKY_RESERVED 20
#define STNDSTARS_RESERVED 5

#ifdef NOTDEF

/*****************************************************************************
******************************************************************************
**<AUTO EXTRACT>
**
** ROUTINE:	taCompleteTile
**
**<HTML>
**	
**	Given a chain of targets (these objects could be tile objs
**      assigned to the current plate or another one, or untiled
**      targets) for a plug plate, return a chain of PLUGMAPOBJs
**      that should be plugged.
**	
**</HTML>
**</AUTO>
******************************************************************************
******************************************************************************
*/
RET_CODE
taCompleteTile(CHAIN *targetobjs, /* IN: chain of objects
                                     available for this tile*/
	       int tile,	/* id of the current tile being completed */
 	       CHAIN *plugobjs /* OUT: chain of PLUGMAPOBJs to be plugged */) {
 
  CURSOR_T curse;
  PLUGMAPOBJ *plugobj;
  TARGETOBJ *targetobj;
  int nplugged = 0;
  OBJTYPE objtype = 0;
  CHAIN *typechains[4];      /* 0 - QA possibility,
				1 - star,
				2 - standard star,
				3 - sky
				4 - guide star */
  AR_PRIM_TARGET_TYPE quasar = AR_TARGET_QSO_HIZ|AR_TARGET_QSO_CAP|AR_TARGET_QSO_SKIRT|AR_TARGET_QSO_FIRST_CAP|AR_TARGET_QSO_FIRST_SKIRT;
  AR_PRIM_TARGET_TYPE galaxy = AR_TARGET_GALAXY_RED|AR_TARGET_GALAXY|AR_TARGET_GALAXY_EXTRA;
  AR_PRIM_TARGET_TYPE tilegalaxy = AR_TARGET_GALAXY_RED|AR_TARGET_GALAXY;
  AR_PRIM_TARGET_TYPE primstar = AR_TARGET_STAR_BHB|AR_TARGET_STAR_CARBON|AR_TARGET_STAR_BROWN_DWARF|AR_TARGET_STAR_SUB_DWARF|AR_TARGET_STAR_CATY_VAR|AR_TARGET_STAR_RED_DWARF|AR_TARGET_STAR_WHITE_DWARF|AR_TARGET_STAR_PN;
  AR_SEC_TARGET_TYPE secstar = TAR_TARGET_REDDEN_STD;
  AR_SEC_TARGET_TYPE stndstar = TAR_TARGET_SPECTROPHOTO_STD|TAR_TARGET_TELLURIC_STD;
  AR_SEC_TARGET_TYPE na = TAR_TARGET_LIGHT_TRAP|TAR_TARGET_GUIDE_STAR|TAR_TARGET_QUALITY_HOLE|TAR_TARGET_BUNDLE_HOLE;


  curse = shChainCursorNew(targetobjs);
  while ((targetobj = shChainWalk(targetobjs, curse, NEXT)) != NULL) {
    if (targetobj->tileId = tile) {
      /* Create the plug object */
      plugobj = shMalloc(sizeof(PLUGMAPOBJ));
      plugobj->objId[0] = targetobj->run;
      plugobj->objId[1] = targetobj->rerun;
      plugobj->objId[2] = targetobj->col;
      plugobj->objId[3] = targetobj->field;
      plugobj->objId[4] = targetobj->id;
      plugobj->holeType = OBJECT;
      plugobj->ra = targetobj->ra;
      plugobj->dec = targetobj->dec;
      plugobj->mag = -999.;	/* I think this is five fiber magnitudes */
      plugobj->starL = -999.; 	/* not currently set in input */
      plugobj->expL = -999.;	/* not currently set in input */
      plugobj->deVaucL = -999.;	/* not currently set in input */
      if (targetobj->primTarget&na) { objtype = NA; } {
      else {
        if (targetobj->secTarget&stndstar) { objtype = STANDARDSTAR; } {
           if (targetobj->primTarget&quasar) {objtype = QSO;} {
             if (targetobj->primTarget&galaxy) {objtype = GALAXY;} {
               if ((targetobj->primTarget&primstar)||(targetobj->secTarget$secstar)) {
                 objtype = STAR;
               } else { objtype = UNKNOW; }
             }
           }
        }
      }
      plugobj->objType = objtype;
      plugobj->xFocal = -999.;
      plugobj->yFocal = -999.;
      plugobj->spectrographId = -999;
      plugobj->fiberId = -999;
      plugobj->throughput = -999;
      /* Add it to the plug chain */
      if (shChainElementAddByPos(plugobjs, plugobj, PLUGMAPOBJ, TAIL, AFTER)!=SH_SUCCESS) { return SH_GENERIC_ERROR; }
      /* Remove it from the tile chain */
      targetobj = shChainElementRemoveByCursor(targetobjs, curse);
      shFree(tileObj);
      nplugged++;
    }
  }
  shChainCursorDel(targetobjs, curse);

  /* Make an array of chains of each target category, sorted by priority */
  curse = shChainCursorNew(targetobjs);
  while ((targetobj = shChainWalk(targetobjs, curse, NEXT)) != NULL) {
    if ((targetobj->primTarget&quasar)||(targetobj->primTarget&tilegalaxy)) {
       /* Add to QA chain */
      if (shChainElementAddByPos(typechains[0], targetobj, "TARGETOBJ", "TAIL", "AFTER")!=SH_SUCCESS) { return SH_GENERIC_ERROR; }
      if (shChainSort(typechains[0], "priority", 0)!=SH_SUCCESS) { return SH_GENERIC_ERROR; }
    }
    if (targetobj->primTarget&primstar) {
       /* Add to star chain */
      if (shChainElementAddByPos(typechains[1], targetobj, "TARGETOBJ", "TAIL", "AFTER")!=SH_SUCCESS) { return SH_GENERIC_ERROR; }
      if (shChainSort(typechains[1], "priority", 0)!=SH_SUCCESS) { return SH_GENERIC_ERROR; }
    }
    if (targetobj->secTarget&stndstar) {
       /* Add to standard star chain */
      if (shChainElementAddByPos(typechains[2], targetobj, "TARGETOBJ", "TAIL", "AFTER")!=SH_SUCCESS) { return SH_GENERIC_ERROR; }
      if (shChainSort(typechains[2], "priority", 0)!=SH_SUCCESS) { return SH_GENERIC_ERROR; }
    }
    if (targetobj->secTarget&TAR_TARGET_SKY) {
       /* Add to sky chain */
      if (shChainElementAddByPos(typechains[3], targetobj, "TARGETOBJ", "TAIL", "AFTER")!=SH_SUCCESS) { return SH_GENERIC_ERROR; }
      if (shChainSort(typechains[3], "priority", 0)!=SH_SUCCESS) { return SH_GENERIC_ERROR; }
    }
    if (targetobj->secTarget&TAR_TARGET_GUIDE_STAR) {
       /* Add to guide star chain */
      if (shChainElementAddByPos(typechains[4], targetobj, "TARGETOBJ", "TAIL", "AFTER")!=SH_SUCCESS) { return SH_GENERIC_ERROR; }
      if (shChainSort(typechains[4], "priority", 0)!=SH_SUCCESS) { return SH_GENERIC_ERROR; }
    }
  }
  shChainCursorDel(targetobjs, curse);
  
  /** Assign ``as available" fibers **/
  for (fiber = nplugged; fiber < ; fiber++) {

    /* Assign ROSAT source - not implemented */
    /* Assign stars, serendipity, QA objects with required probabilities */
    /* This is currently implemented in the incorrect way that we alternate
	between stars and QA objects (serendipity is not implemented).  I also
	pay no attention to sub-categories. */
    if ((fmod(fiber, 2))&&(typechain[1]!=NULL)) {
      /* Select a star */
      do { targetobj = shChainElementRemByPos(typehchain[1],HEAD);}
      while { ((!withinfiberspacing(targetobj, plugobjs))&&(targetobj!=NULL)) }
    } else {
      /* Select a QA target */
      do { targetobj = shChainElementRemByPos(typehchain[0],HEAD);}
      while { ((!withinfiberspacing(targetobj, plugobjs))&&(targetobj!=NULL)) }
    }
    /* Add to plug chain */

  }

  /** Assign reserved targets **/

  /** Assign guide stars - all are passed along **/

  /** Assign coherent sky bundle - all remaining skies are passed along **/

  return SH_SUCCESS;

}
#endif


/*****************************************************************************
******************************************************************************
**<AUTO EXTRACT>
**
** ROUTINE:     taOnPlate
**
**<HTML>
**
**      Determine whether a given object is within the area covered by a
**      plate.
**
**      Returns 1 if the object is within plateradius of the plate center.
**      Returns 0 otherwise.
**
**</HTML>
**</AUTO>
******************************************************************************
******************************************************************************
*/

int taOnPlate(
  TARGETOBJ *targetobj,		/* pointer to a target in need of a fiber */
  double raCen,			/* RA of the plate center */
  double decCen,		/* DEC of the plate center */
  double plateradius)		/* plate radius in degrees */
{

  double atRadFromDeg(double degrees);	/* radius from degrees */
  double deltaRa;			/* RA difference in degrees */
  double decObj;			/* DEC of object in degrees */

  double decAve;			/* average dec in degrees */
  double decAveRad;			/* average dec in radians */
  double cosDec;			/* cosine of average dec */
  double deltaDec;			/* DEC difference in degrees */
  double delta;				/* coord difference in degrees */

  /* Object must obviously be within plate area to be assigned a fiber */

  /* Since we're always dealing with small angles, use faster approximations */
  deltaRa = fabs(raCen - targetobj->ra);
  if (deltaRa >= 180) {
    /* (void)printf("RA > 180: %f\n",deltaRa); */
    deltaRa = 360.0 - deltaRa;
  }
  decObj = targetobj->dec;
  decAve = (decObj + decCen)/2.0;
  decAveRad = atRadFromDeg(decAve);
  cosDec = cos(decAveRad);
  deltaDec = fabs(decCen - decObj);
  delta = sqrt(cosDec * cosDec * deltaRa * deltaRa + deltaDec * deltaDec);
  if (delta < plateradius) {return 1;}

  /* getting here means object is not in plate area, so return a 0 */
  return 0;

}


/*****************************************************************************
******************************************************************************
**<AUTO EXTRACT>
**
** ROUTINE:	taWithinFiberSpacing
**
**<HTML>
**	
**	Determine whether a given object can be assigned a fiber on this
**	plate.
**
**	Returns 1 if the object is within fiberspacing of any object in
**	chain of PLUGOBJs or too near the center hole.
**      Returns 0 otherwise.
**	
**</HTML>
**</AUTO>
******************************************************************************
******************************************************************************
*/

int taWithinFiberSpacing(
  TARGETOBJ *targetobj,		/* pointer to a target in need of a fiber */
  CHAIN *plugobjs,		/* pointer to a chain of plugged objects */
  double raCen,			/* RA of the plate center in degrees */
  double decCen,		/* DEC of the plate center in degrees */
  double fiberspacing,		/* minimum fiber separation in degrees */
  double centerholeradius)	/* minimum distance from center in degrees */
{

  double atRadFromDeg(double degrees); /* radians from degrees */

  /* The following are used when checking fiber-center spacing */
  double raObj;			/* RA of object in degrees, reused */
  double decObj;                /* DEC of object in degrees, reused */
  double decObjRad;		/* DEC of object in radians, reused */
  double deltaRa;		/* RA difference in degrees, reused */
  double deltaRaRad;		/* RA difference in radians, reused */
  double decCenRad;		/* DEC of plate center in radians */
  double a;			/* first part of distance eqtn */
  double b;			/* second part of distance eqtn */

  double deltaDec;		/* DEC difference in degrees */
  double deltaDecRad;		/* DEC difference in radians, reused */
  double decAve;		/* average DEC in degrees */
  double decAveRad;		/* average DEC in radians */
  double cosDec;		/* cosine of Average DEC */
  double deltaRad;              /* angular separation in radians, reused */
  double delta;			/* angular separation in degrees */

  /* The following are used when checking fiber-fiber spacing */
  CURSOR_T crsr;		/* chain cursor */
  PLUGMAPOBJ *plug;		/* pointer to a PLUGMAPOBJ struct */
  double decPlug;		/* DEC of plugged objects in degrees */
  double decPlugRad;		/* DEC of plugged objects in radians */

  /** these are used often in the routine **/
  raObj = targetobj->ra;
  decObj = targetobj->dec;
  decObjRad = atRadFromDeg(decObj);

  /** First, check the center of the plate **/

  /* Fibers may not be spaced closer than centerholeradius from the center
     of the plate */

/* this uses small angle approx */
  deltaDec = fabs(decCen - decObj);
  decAve = (decCen + decObj)/2.0;
  decAveRad = atRadFromDeg(decAve);
  cosDec = cos(decAveRad);
  deltaRa = fabs(raCen - raObj);
  if (deltaRa > 180.0) {
    deltaRa = 360.0 - deltaRa;
  }
  delta = sqrt(cosDec * cosDec * deltaRa * deltaRa + deltaDec * deltaDec);
  if (delta < centerholeradius) {return 1;}

  /* use exact spherical trig eqns, good even for small angles,
     seems to be a problem with this -- allows angles too close */
/*  deltaRa = fabs(raCen - raObj);
  if (deltaRa > 180.0) {
    deltaRa = 360.0 - deltaRa;
  }
  deltaRaRad = atRadFromDeg(deltaRa) / 2.0;
  decCenRad = atRadFromDeg(decCen);
  deltaDecRad = (decObjRad - decCenRad) / 2.0;
  a = sin(deltaDecRad)*sin(deltaDecRad);
  b = cos(decCenRad)*cos(decObjRad)*sin(deltaRaRad)*sin(deltaRaRad);
  deltaRad = 2*asin(sqrt(a + b));
  delta = atRadToDeg(deltaRad);
  if (delta < centerholeradius) {return 1;}
*/

/*** print some numbers ***/
/*  (void)printf("raCen = %f\n",raCen);
  (void)printf("decCen = %f\n",decCen);
  (void)printf("centerholeradius =  %f\n",centerholeradius);
  (void)printf("cosCenterHoleRadius = %f16\n",cosCenterHoleRadius);
  (void)printf("deltaRa = %f\n",deltaRa);
  (void)printf("deltaRaRad = %f\n",deltaRaRad);
  (void)printf("decCenRad = %f\n",decCenRad);
  (void)printf("decObj = %f\n",decObj);
  (void)printf("decObjRad = %f\n",decObjRad);
  (void)printf("cosDist = %f\n",cosDist);
*/

  /** Next check those objects which might be within the minimum
      fiber spacing **/

  /* Fibers may not be placed closer than fiberspacing from eachother */
  crsr = shChainCursorNew(plugobjs);
  while ((plug = shChainWalk(plugobjs, crsr, NEXT)) != NULL) {

  /* small angle approx */
    deltaRa = fabs(plug->ra - raObj);
    if (deltaRa > 180.0) {
      deltaRa = 360.0 - deltaRa;
    }
    decPlug = plug->dec;
    deltaDec = fabs(decPlug - decObj);
    decAve = (decPlug + decObj)/2.0;
    decAveRad = atRadFromDeg(decAve);
    cosDec = cos(decAveRad);
    delta = sqrt(cosDec * cosDec * deltaRa * deltaRa + deltaDec * deltaDec);
    if (delta < fiberspacing) {
      shChainCursorDel(plugobjs, crsr);
      return 1;
    }


    /* use exact spherical trig eqns, good even for small angles,
     seems to be a problem with this -- allows angles too close */
/*
    deltaRa = fabs(plug->ra - raObj);
    if (deltaRa > 180.0) {
      deltaRa = 360.0 - deltaRa;
    }
    deltaRaRad = atRadFromDeg(deltaRa) / 2.0;
    decPlug = plug->dec;
    decPlugRad = atRadFromDeg(decPlug);
    deltaDecRad = fabs(decPlugRad - decObjRad)/2.0;
    a = sin(deltaDecRad)*sin(deltaDecRad);
    b = cos(decObjRad)*cos(decPlugRad)*sin(deltaRaRad)*sin(deltaRaRad);
    deltaRad = 2*asin(sqrt(a + b));
    delta = atRadToDeg(deltaRad);
    if (delta < fiberspacing) {
      shChainCursorDel(plugobjs, crsr);
      return 1;
    }
*/
  }

  /* free up the memory held by the chain cursor */
  shChainCursorDel(plugobjs, crsr);

 /** getting here means that the object is not too close to an existing hole **/
  return 0;

}

/*****************************************************************************
******************************************************************************
**<AUTO EXTRACT>
**
** ROUTINE:     taNewPlugmapObj
**
**<HTML>
**
**      Returns one new plugmapobj from a targetobj
**
**</HTML>
**</AUTO>
******************************************************************************
******************************************************************************
*/
#ifdef NOTDEF
RET_CODE
taNewPlugmapObj(
  TARGETOBJ *targetobj,	/* IN: TARGETOBJ used to fill new PLUGMAPOBJ */
  HOLETYPE holetype,	/* IN: holetype of the new PLUGMAPOBJ */
  OBJTYPE objtype,	/* IN: objtype of the new PLUGMAPOBJ */
  PLUGMAPOBJ *plugobj)	/* OUT: PLUGMAPOBJ to be created */
{

  /* Create the plug object */
  plugobj = shMalloc(sizeof(PLUGMAPOBJ));
  plugobj->objId[0] = targetobj->run;
  plugobj->objId[1] = targetobj->rerun;
  plugobj->objId[2] = targetobj->col;
  plugobj->objId[3] = targetobj->field;
  plugobj->objId[4] = targetobj->id;
  plugobj->holeType = holetype;
  plugobj->ra = targetobj->ra;
  plugobj->dec = targetobj->dec;
  plugobj->mag[0] = targetobj->mag[0];
  plugobj->mag[1] = targetobj->mag[1];
  plugobj->mag[2] = targetobj->mag[2];
  plugobj->mag[3] = targetobj->mag[3];
  plugobj->mag[4] = targetobj->mag[4];
  plugobj->starL = targetobj->starL;
  plugobj->expL = targetobj->expL;
  plugobj->deVaucL = targetobj->deVaucL;
  plubobj->objType = objtype;
  plugobj->xFocal = -999.;
  plugobj->yFocal = -999.;
  plugobj->spectrographId = -999;
  plugobj->fiberId = -999;
  plugobj->throughput = -999;

  return SH_SUCCESS;

/* to use this: if (taNewPlugmapObj(targetobj, holetype, objtype, plugobj))
                  {return SH_GENERIC_ERROR;}
*/
}
#endif
