# 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, 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:
      value.Flt = average[0].refColorBlue;
      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;
      }

      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:
	      // why is measure 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:
	      // value.Flt = PhotFluxRef  (equiv, 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 (equiv, 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:
	case MAG_OPTION_CHISQ:
	case MAG_OPTION_MIN:
	case MAG_OPTION_MAX:
	case MAG_OPTION_NCODE: {
	  int Nsec = GetPhotcodeNsec (field->photcode->code);
	  if (Nsec == -1) break;
	  value.Int = secfilt[Nsec].Ncode;
	  break;
	}
	case MAG_OPTION_NPHOT: {
	  int Nsec = GetPhotcodeNsec (field->photcode->code);
	  if (Nsec == -1) break;
	  value.Int = secfilt[Nsec].Nused;
	  break;
	}
	case MAG_OPTION_UC_DIST:
	case MAG_OPTION_STACK_DET_ID:
	  value.Int = PhotStackID (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;
	}
	case MAG_OPTION_NONE:
	  break;
      }
      break;

# if (0)
      switch (field->magMode) {
	case MAG_AVE:
	  value.Flt = PhotAve  (field->photcode, average, secfilt);
	  break;
	case MAG_REF:
	  value.Flt = PhotRef  (field->photcode, average, secfilt, measure);
	  break;
	case MAG_INST:
	case MAG_CAT:
	case MAG_SYS:
	case MAG_REL:
	case MAG_CAL:
	  // XXX need to code this correctly: this returns just the first matching value
	  value.Flt = NAN;
	  for (i = 0; i < average[0].Nmeasure; i++) {
	    if (field->photcode->code != measure[i].photcode) continue;
	    value.Flt = measure[i].M;
	    break;
	  }
	  break;
	case MAG_CHISQ:
	  value.Flt = PhotXm (field->photcode, average, secfilt);
	  break;
	case MAG_ERR:
	  if ((field->photcode->type == PHOT_REF) || (field->photcode->type == PHOT_DEP)) {
	    for (i = 0; i < average[0].Nmeasure; i++) {
	      if (field->photcode->code != measure[i].photcode) continue;
	      value.Flt = measure[i].dM;
	      break;
	    }
	  } else {
	    value.Flt = PhotAveErr (field->photcode, average, secfilt);
	  }
	  break;
	case MAG_STDEV:
	  value.Flt = PhotMstdev (field->photcode, average, secfilt);
	  break;
	case MAG_PHOT_FLAGS:
	  if ((field->photcode->type == PHOT_REF) || (field->photcode->type == PHOT_DEP)) {
	    for (i = 0; i < average[0].Nmeasure; i++) {
	      if (field->photcode->code != measure[i].photcode) continue;
	      value.Int = measure[i].photFlags;
	      break;
	    }
	  } else {
	    int Nsec = GetPhotcodeNsec (field->photcode->code);
	    if (Nsec == -1) break;
	    value.Int = secfilt[Nsec].flags;
	  }
	  break;
	case MAG_NCODE:
	  // XXX push these into dvo_photcode_ops APIs
	  // XXX do I need to allow for conversion to equiv?
	  int Nsec = GetPhotcodeNsec (field->photcode->code);
	  if (Nsec == -1) break;
	  value.Int = secfilt[Nsec].Ncode;
	  break;
	case MAG_NPHOT:
	  int Nsec = GetPhotcodeNsec (field->photcode->code);
	  if (Nsec == -1) break;
	  value.Int = secfilt[Nsec].Nused;
	  break;

	case MAG_APER:
	case MAG_APER_AVE:
	  value.Flt = PhotAperAve (field->photcode, average, secfilt);
	  break;

	case MAG_APER_REF:
	  value.Flt = PhotAperRef (field->photcode, average, secfilt, measure);
	  break;

	case MAG_KRON:
	case MAG_KRON_AVE:
	  value.Flt = PhotKronAve (field->photcode, average, secfilt);
	  break;

	case MAG_KRON_REF:
	  value.Flt = PhotKronRef (field->photcode, average, secfilt, measure);
	  break;

	case MAG_KRON_ERR:
	  value.Flt = PhotKronAveErr (field->photcode, average, secfilt);
	  break;

	case MAG_MIN:
	  value.Flt = PhotMmin (field->photcode, average, secfilt);
	  break;
	case MAG_MAX:
	  value.Flt = PhotMmax (field->photcode, average, secfilt);
	  break;
	case MAG_UC_DIST:
	  value.Flt = PhotUCdist (field->photcode, average, secfilt);
	  break;
	case MAG_STACK_DET_ID:
	  value.Int = PhotStackID (field->photcode, average, secfilt);
	  break;

	case MAG_FLUX_PSF:
	  value.Flt = PhotAveFluxPSF (field->photcode, average, secfilt);
	  break;
	case MAG_FLUX_PSF_ERR:
	  value.Flt = PhotAvedFluxPSF (field->photcode, average, secfilt);
	  break;
	case MAG_FLUX_KRON:
	  value.Flt = PhotAveFluxKron (field->photcode, average, secfilt);
	  break;
	case MAG_FLUX_KRON_ERR:
	  value.Flt = PhotAvedFluxKron (field->photcode, average, secfilt);
	  break;
      }
      break;
    case AVE_dMAG:
      value.Flt = PhotAveErr (field->photcode, average, secfilt);
      break;
    case AVE_Xm:
      value.Flt = PhotXm (field->photcode, average, secfilt);
      break;
# endif
    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 
