# include "dvo.h"
# include "libdvo_astro.h"

/* time concepts */
static time_t TimeReference;
static int TimeFormat;

static CoordTransform *celestial_to_galactic = NULL;
static CoordTransform *celestial_to_ecliptic = NULL;

static int haveGalactic = FALSE;
static double GLON = 0.0;
static double GLAT = 0.0;

static int haveEcliptic = FALSE;
static double ELON = 0.0;
static double ELAT = 0.0;

// define a locally-static transform
int dbExtractAveragesInitTransform (CoordTransformSystem target) {

  // galactic transform is kept forever
  if (target == COORD_GALACTIC) {
    if (celestial_to_galactic != NULL) return (TRUE);
    celestial_to_galactic = InitTransform (COORD_CELESTIAL, target);
    return (TRUE);
  }

  // ecliptic transform must be updated (is weakly time-dependent)
  if (target == COORD_ECLIPTIC) {
    if (celestial_to_ecliptic != NULL) {
      free (celestial_to_ecliptic);
    }
    celestial_to_ecliptic = InitTransform (COORD_CELESTIAL, target);
    return (TRUE);
  }
  return (FALSE);
}

int dbExtractAveragesInit () {
  GetTimeFormat (&TimeReference, &TimeFormat);
  return (TRUE);
}

int dbExtractAveragesInitAve () {
  haveGalactic = FALSE;
  haveEcliptic = FALSE;
  return (TRUE);
}

/* return average.field based on the selection */
dbValue dbExtractAverages (Average *average, SecFilt *secfilt, Measure *measure, Lensobj *lensobj, StarPar *starpar, dbField *field) {

  // off_t i;
  dbValue value;

  value.Flt = NAN;
  value.Int =   0;

  /* assign vector values */
  switch (field->ID) {
    case AVE_RA:
      value.Flt = average[0].R;
      break;
    case AVE_DEC:
      value.Flt = average[0].D;
      break;
    case AVE_GLON:
      if (!haveGalactic) {
	ApplyTransform (&GLON, &GLAT, average[0].R, average[0].D, celestial_to_galactic);
	haveGalactic = TRUE;
      }
      value.Flt = GLON;
      break;
    case AVE_GLAT:
      if (!haveGalactic) {
	ApplyTransform (&GLON, &GLAT, average[0].R, average[0].D, celestial_to_galactic);
	haveGalactic = TRUE;
      }
      value.Flt = GLAT;
      break;
    case AVE_ELON:
      if (!haveEcliptic) {
	ApplyTransform (&ELON, &ELAT, average[0].R, average[0].D, celestial_to_ecliptic);
	haveEcliptic = TRUE;
      }
      value.Flt = ELON;
      break;
    case AVE_ELAT:
      if (!haveEcliptic) {
	ApplyTransform (&ELON, &ELAT, average[0].R, average[0].D, celestial_to_ecliptic);
	haveEcliptic = TRUE;
      }
      value.Flt = ELAT;
      break;
    case AVE_RA_ERR:
      value.Flt = average[0].dR;
      break;
    case AVE_DEC_ERR:
      value.Flt = average[0].dD;
      break;

    case AVE_U_RA:
      value.Flt = average[0].uR;
      break;
    case AVE_U_DEC:
      value.Flt = average[0].uD;
      break;
    case AVE_U_RA_ERR:
      value.Flt = average[0].duR;
      break;
    case AVE_U_DEC_ERR:
      value.Flt = average[0].duD;
      break;

    case AVE_PAR:
      value.Flt = average[0].P;
      break;
    case AVE_PAR_ERR:
      value.Flt = average[0].dP;
      break;

    case AVE_NMEAS:
      value.Int = average[0].Nmeasure;
      break;
    case AVE_NMISS:
      value.Int = average[0].Nmissing;
      break;
    case AVE_NPOS:
      value.Int = average[0].Npos;
      break;

    case AVE_CHISQ_POS:
      value.Flt = average[0].ChiSqAve;
      break;
    case AVE_CHISQ_PM:
      value.Flt = average[0].ChiSqPM;
      break;
    case AVE_CHISQ_PAR:
      value.Flt = average[0].ChiSqPar;
      break;

    // XXX case AVE_PM_GROUPS:
    // XXX   value.Int = GetProperMotionGroups (average, measure);
    // XXX   break;

    case AVE_TMEAN:
      value.Flt = TimeValue (average[0].Tmean, TimeReference, TimeFormat);
      break;
    case AVE_TRANGE:
      value.Flt = TimeValue (average[0].Trange, 0, TimeFormat);
      break;

    case AVE_PSF_QF:
      value.Flt = average[0].psfQF;
      break;
    case AVE_PSF_QF_PERF:
      value.Flt = average[0].psfQFperf;
      break;
    case AVE_STARGAL:
      value.Flt = average[0].stargal;
      break;
    case AVE_REF_COLOR_BLUE:
      value.Flt = average[0].refColorBlue;
      break;
    case AVE_REF_COLOR_RED:
      value.Flt = average[0].refColorRed;
      break;

    case AVE_OBJ_FLAGS:
      value.Int = average[0].flags;
      break;
    case AVE_OBJID:
      value.Int = average[0].objID;
      break;
    case AVE_CATID:
      value.Int = average[0].catID;
      break;
    case AVE_EXTID_HI:
      value.Int = (0x00000000FFFFFFFF & (average[0].extID >> 32));
      break;
    case AVE_EXTID_LO:
      value.Int = (0x00000000FFFFFFFF & average[0].extID);
      break;

    case AVE_PHOT:
      // if we request mag:ave, use equiv for photcode (ie a given measure, say GPC1.g.XY01, will return g for mag:ave)
      if  (field->photcode->type == PHOT_MAG) {
	// this is an error
	break;
      }

      // if we ask for 2MASS_K, etc (REF values), return NAN unless measure->code matches
      if (field->photcode->type == PHOT_REF) {
	// need to ensure measure exists.
	break;
      }

      // if we ask for GPC1.g.XY03:rel, etc (DEP values), return NAN unless measure->code matches
      if (field->photcode->type == PHOT_DEP) {
	// again.
	break;
      }

      /* in some cases below we need a measurement which matches the requested photcode.
	 here is some sample code to find this.

	  for (i = 0; i < average[0].Nmeasure; i++) {
	    if (field->photcode->code != measure[i].photcode) continue;
	    value.Flt = measure[i].M;
	    break;
	  }
      */

      switch (field->magOption) {
	case MAG_OPTION_MAG:
	  switch (field->magLevel) {
	    case MAG_LEVEL_AVE:
	      value.Flt = PhotAve  (field->photcode, average, secfilt, field->magClass, field->magSource); 
	      break;
	    case MAG_LEVEL_REF:
	      // which measure is needed here?
	      // PhotRef  (field->photcode, average, secfilt, measure, field->magClass, field->magSource); 
	      break;
	    case MAG_LEVEL_INST:
	    case MAG_LEVEL_CAT:
	    case MAG_LEVEL_SYS:
	    case MAG_LEVEL_REL:
	    case MAG_LEVEL_CAL:
	    case MAG_LEVEL_NONE:
	      break;
	  }
	  break;

	case MAG_OPTION_ERR:
	  switch (field->magLevel) {
	    case MAG_LEVEL_AVE:
	    case MAG_LEVEL_REF:
	      value.Flt = PhotAveErr (field->photcode, average, secfilt, field->magClass, field->magSource);  
	      break;
	    case MAG_LEVEL_INST:
	    case MAG_LEVEL_CAT:
	    case MAG_LEVEL_SYS:
	    case MAG_LEVEL_REL:
	    case MAG_LEVEL_CAL:
	    case MAG_LEVEL_NONE:
	      break;
	  }
	  break;

	case MAG_OPTION_FLUX:
	  switch (field->magLevel) {
	    case MAG_LEVEL_AVE:
	      value.Flt = PhotFluxAve  (field->photcode, average, secfilt, field->magClass, field->magSource); 
	      break;
	    case MAG_LEVEL_REF:
	      // XXX which measure is needed here?
	      // value.Flt = PhotFluxRef  (field->photcode, average, secfilt, measure, field->magClass, field->magSource); 
	      break;
	    case MAG_LEVEL_INST:
	    case MAG_LEVEL_CAT:
	    case MAG_LEVEL_SYS:
	    case MAG_LEVEL_REL:
	    case MAG_LEVEL_CAL:
	    case MAG_LEVEL_NONE:
	      break;
	  }
	  break;

	case MAG_OPTION_FLUX_ERR:
	  switch (field->magLevel) {
	    case MAG_LEVEL_AVE:
	    case MAG_LEVEL_REF:
	      value.Flt = PhotFluxAveErr (field->photcode, average, secfilt, field->magClass, field->magSource);  
	      break;
	    case MAG_LEVEL_INST:
	    case MAG_LEVEL_CAT:
	    case MAG_LEVEL_SYS:
	    case MAG_LEVEL_REL:
	    case MAG_LEVEL_CAL:
	    case MAG_LEVEL_NONE:
	      break;
	  }
	  break;

	case MAG_OPTION_STDEV:
	  value.Flt = PhotMstdev (field->photcode, average, secfilt, field->magClass, field->magSource);
	  break;
	case MAG_OPTION_CHISQ:
	  value.Flt = PhotXm (field->photcode, average, secfilt);
	  break;
	case MAG_OPTION_MIN:
	  value.Flt = PhotMmin (field->photcode, average, secfilt);
	  break;
	case MAG_OPTION_MAX:
	  value.Flt = PhotMmax (field->photcode, average, secfilt);
	  break;
	case MAG_OPTION_NCODE: {
	  int Nsec = GetPhotcodeNsec (field->photcode->code);
	  if (Nsec == -1) break;
	  value.Int = secfilt[Nsec].Ncode;
	  break;
	}
	case MAG_OPTION_NPHOT: {
	  value.Int = PhotNphot (field->photcode, average, secfilt, field->magClass, field->magSource);
	  break;
	}
	case MAG_OPTION_UC_DIST:
	  value.Flt = PhotUCdist (field->photcode, average, secfilt);
	  break;
	case MAG_OPTION_FLAGS: {
	  int Nsec = GetPhotcodeNsec (field->photcode->code);
	  if (Nsec == -1) break;
	  value.Int = secfilt[Nsec].flags;
	  break;
	}

	  // g:X11_SM_OBJ, etc, are only valid for average 
	case MAG_OPTION_X11_SM_OBJ: { value.Flt = LensValue_X11_sm_obj (field->photcode, lensobj); break; }
	case MAG_OPTION_X12_SM_OBJ: { value.Flt = LensValue_X12_sm_obj (field->photcode, lensobj); break; }
	case MAG_OPTION_X22_SM_OBJ: { value.Flt = LensValue_X22_sm_obj (field->photcode, lensobj); break; }
	case MAG_OPTION_E1_SM_OBJ:  { value.Flt = LensValue_E1_sm_obj  (field->photcode, lensobj); break; }
	case MAG_OPTION_E2_SM_OBJ:  { value.Flt = LensValue_E2_sm_obj  (field->photcode, lensobj); break; }

	case MAG_OPTION_X11_SH_OBJ: { value.Flt = LensValue_X11_sh_obj (field->photcode, lensobj); break; }
	case MAG_OPTION_X12_SH_OBJ: { value.Flt = LensValue_X12_sh_obj (field->photcode, lensobj); break; }
	case MAG_OPTION_X22_SH_OBJ: { value.Flt = LensValue_X22_sh_obj (field->photcode, lensobj); break; }
	case MAG_OPTION_E1_SH_OBJ:  { value.Flt = LensValue_E1_sh_obj  (field->photcode, lensobj); break; }
	case MAG_OPTION_E2_SH_OBJ:  { value.Flt = LensValue_E2_sh_obj  (field->photcode, lensobj); break; }

	case MAG_OPTION_X11_SM_PSF: { value.Flt = LensValue_X11_sm_psf (field->photcode, lensobj); break; }
	case MAG_OPTION_X12_SM_PSF: { value.Flt = LensValue_X12_sm_psf (field->photcode, lensobj); break; }
	case MAG_OPTION_X22_SM_PSF: { value.Flt = LensValue_X22_sm_psf (field->photcode, lensobj); break; }
	case MAG_OPTION_E1_SM_PSF:  { value.Flt = LensValue_E1_sm_psf  (field->photcode, lensobj); break; }
	case MAG_OPTION_E2_SM_PSF:  { value.Flt = LensValue_E2_sm_psf  (field->photcode, lensobj); break; }

	case MAG_OPTION_X11_SH_PSF: { value.Flt = LensValue_X11_sh_psf (field->photcode, lensobj); break; }
	case MAG_OPTION_X12_SH_PSF: { value.Flt = LensValue_X12_sh_psf (field->photcode, lensobj); break; }
	case MAG_OPTION_X22_SH_PSF: { value.Flt = LensValue_X22_sh_psf (field->photcode, lensobj); break; }
	case MAG_OPTION_E1_SH_PSF:  { value.Flt = LensValue_E1_sh_psf  (field->photcode, lensobj); break; }
	case MAG_OPTION_E2_SH_PSF:  { value.Flt = LensValue_E2_sh_psf  (field->photcode, lensobj); break; }

	case MAG_OPTION_NONE:
	  break;

	default:
	  break;
      }
      break;

    case AVE_E_BV:             { value.Flt = starpar->Ebv;      break; }
    case AVE_E_BV_ERR:         { value.Flt = starpar->dEbv;     break; }
    case AVE_DISTANCE_MOD:     { value.Flt = starpar->DistMag;  break; }
    case AVE_DISTANCE_MOD_ERR: { value.Flt = starpar->dDistMag; break; }
    case AVE_M_R:     	       { value.Flt = starpar->M_r;      break; }
    case AVE_M_R_ERR: 	       { value.Flt = starpar->dM_r;     break; }
    case AVE_FEH:     	       { value.Flt = starpar->FeH;      break; }
    case AVE_FEH_ERR: 	       { value.Flt = starpar->dFeH;     break; }
    case AVE_URA_GALMODEL:     { value.Flt = starpar->uRA;      break; }
    case AVE_UDEC_GALMODEL:    { value.Flt = starpar->uDEC;     break; }
    case AVE_RA_GALMODEL:      { value.Flt = starpar->R;        break; }
    case AVE_DEC_GALMODEL:     { value.Flt = starpar->D;        break; }

    case AVE_TYPE:
      break;
    case AVE_TYPEFRAC:
      break;


  }
  return (value);
}  

// XXX int GetProperMotionGroups (Average *average, Measure *measure) {
// XXX   // need the times, excluding ignored detections
// XXX   // sort the images
// XXX   for (i = 0; i < Ntimes - 1; i++)  {
// XXX     if (time[i+1] - time[i] < TRANGE) Ngroup ++;
// XXX   }
// XXX   return Ngroup;
// XXX }
// XXX 
