# include "dvo.h"
# include "dvodb.h"

// to add a new field to the extractions:
// 1) add the selection for the field below
// 2) add the field to the ParseMeasureField in dbFields.c
// 3) add the field to the measure enum list in dvoshell.h

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

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

static int REMOTE_CLIENT = FALSE;

// the following values are calculated together in a single function, eg.,
// ApplyTransform() returning Glon & Glat.  for a single measurement, we want to do this
// calculation once and save both values in case both are requested (usually both are if
// either is)
static int haveGalacticAve = FALSE;
static double GLON_AVE = 0.0;
static double GLAT_AVE = 0.0;

static int haveEclipticAve = FALSE;
static double ELON_AVE = 0.0;
static double ELAT_AVE = 0.0;

static int haveGalacticMeas = FALSE;
static double GLON_MEAS = 0.0;
static double GLAT_MEAS = 0.0;

static int haveEclipticMeas = FALSE;
static double ELON_MEAS = 0.0;
static double ELAT_MEAS = 0.0;

static int haveMosaicMeas = FALSE;
static double XMOS_MEAS = 0.0;
static double YMOS_MEAS = 0.0;

static int haveFieldMeas = FALSE;
static double XFIELD_MEAS = 0.0;
static double YFIELD_MEAS = 0.0;

int dbExtractMeasuresInit (int isRemoteClient) {
  REMOTE_CLIENT = isRemoteClient;
  GetTimeFormat (&TimeReference, &TimeFormat);
  return (TRUE);
}

// define a locally-static transform
int dbExtractMeasuresInitTransform (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 dbExtractMeasuresInitAve () {
  haveGalacticAve = FALSE;
  haveEclipticAve = FALSE;
  return (TRUE);
}

int dbExtractMeasuresInitMeas () {
  haveMosaicMeas   = FALSE;
  haveGalacticMeas = FALSE;
  haveEclipticMeas = FALSE;
  return (TRUE);
}

/* return measure.field based on the selection */
dbValue dbExtractMeasures (Average *average, SecFilt *secfilt, Measure *measure, dbField *field) {

  int Nsec;
  dbValue value;
  double dT;

  Coords *mosaic, *fieldc;

  PhotCode *equiv = NULL;

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

  switch (field->ID) {
    case MEAS_MAG: { /* magnitudes are already determined above */
      PhotCode *myEquiv = GetPhotcodeEquivbyCode (measure[0].photcode);

      // if we request mag:ave, use equiv for photcode
      if  (field->photcode->type == PHOT_MAG) {
	equiv = myEquiv;
	goto valid_photcode;
      }

      // if we ask for 2MASS_K, etc (REF values), return NAN unless measure->code matches
      if ((field->photcode->type == PHOT_REF) && (measure[0].photcode == field->photcode->code)) goto valid_photcode;

      // if we ask for GPC1.g.XY03:rel, etc (DEP values), return NAN unless measure->code matches
      if ((field->photcode->type == PHOT_DEP) && (measure[0].photcode == field->photcode->code)) goto valid_photcode;

      // if we ask for g:ave, or other SEC-level values, return the corresponding field 
      if (field->photcode->type == PHOT_SEC) {
	switch (field->magMode) {
	  // measure-like : return non-NAN if measure.equiv.photcode matches field.photcode
	  case MAG_INST:
	  case MAG_CAT:
	  case MAG_SYS:
	  case MAG_REL:
	  case MAG_CAL:
	  case MAG_ERR:

	  case MAG_APER_INST:
	  case MAG_APER_CAT:
	  case MAG_APER_SYS:
	  case MAG_APER_REL:
	  case MAG_APER_CAL:
	  case MAG_APER_ERR:

	  case MAG_KRON_INST:
	  case MAG_KRON_CAT:
	  case MAG_KRON_SYS:
	  case MAG_KRON_REL:
	  case MAG_KRON_CAL:
	  case MAG_KRON_ERR:

	  case MAG_PHOT_FLAGS:
	    equiv = myEquiv;
	    if (equiv && (equiv->code == field->photcode->code)) goto valid_photcode;
	    break;

	    // mean-like : return value for the given photcode
	  case MAG_AVE:
	  case MAG_REF:
	  case MAG_STDEV:
	  case MAG_AVE_ERR:
	  case MAG_CHISQ:
	  case MAG_FLUX_PSF:
	  case MAG_FLUX_PSF_ERR:

	  case MAG_APER_AVE:

	  case MAG_KRON_AVE:
	  case MAG_FLUX_KRON:
	  case MAG_FLUX_KRON_ERR:

	  case MAG_20:
	  case MAG_80:
	  case MAG_UC_DIST:
	  case MAG_STACK_DET_ID:

	  case MAG_NCODE:
	  case MAG_NPHOT:
	    equiv = field->photcode;
	    goto valid_photcode;
	    break;
	  default:
	    fprintf (stderr, "error");
	    return value;
	}
      }
      break;

  valid_photcode:
      switch (field->magMode) {
	case MAG_INST:
	  value.Flt = PhotInst (measure);  
	  break;
	case MAG_CAT:
	  value.Flt = PhotCat  (measure); 
	  break;
	case MAG_SYS:
	  value.Flt = PhotSys  (measure, average, secfilt); 
	  break;
	case MAG_REL:
	  value.Flt = PhotRel  (measure, average, secfilt); 
	  break;
	case MAG_CAL:
	  value.Flt = PhotCal  (measure, average, secfilt, measure, equiv); 
	  break;
	case MAG_AVE:
	  value.Flt = PhotAve  (equiv, average, secfilt); 
	  break;
	case MAG_REF:
	  value.Flt = PhotRef  (equiv, average, secfilt, measure); 
	  break;
	case MAG_ERR:
	  value.Flt = measure[0].dM;
	  break;
	case MAG_AVE_ERR:
	  value.Flt = PhotAveErr  (equiv, average, secfilt); 
	  break;

	case MAG_APER_INST:
	  value.Flt = PhotAperInst (measure); 
	  break;
	case MAG_APER_CAT:
	  value.Flt = PhotAperCat (measure); 
	  break;
	case MAG_APER_SYS:
	  value.Flt = PhotAperSys (measure, average, secfilt); 
	  break;
	case MAG_APER:
	case MAG_APER_REL:
	  value.Flt = PhotAperRel (measure, average, secfilt); 
	  break;
	case MAG_APER_CAL:
	  value.Flt = PhotAperCal (measure, average, secfilt, measure, equiv); 
	  break;
	case MAG_APER_AVE:
	  value.Flt = PhotAperAve (equiv, average, secfilt);
	  break;
	case MAG_APER_REF:
	  value.Flt = PhotAperRef (equiv, average, secfilt, measure); 
	  break;
	case MAG_APER_ERR:
	  value.Flt = measure[0].dM;
	  break;

	case MAG_KRON_INST:
	  value.Flt = PhotKronInst (measure); 
	  break;
	case MAG_KRON_CAT:
	  value.Flt = PhotKronCat (measure); 
	  break;
	case MAG_KRON_SYS:
	  value.Flt = PhotKronSys (measure, average, secfilt); 
	  break;
	case MAG_KRON:
	case MAG_KRON_REL:
	  value.Flt = PhotKronRel (measure, average, secfilt); 
	  break;
	case MAG_KRON_CAL:
	  value.Flt = PhotKronCal (measure, average, secfilt, measure, equiv); 
	  break;
	case MAG_KRON_AVE:
	  value.Flt = PhotKronAve (equiv, average, secfilt);
	  break;
	case MAG_KRON_REF:
	  value.Flt = PhotKronRef (equiv, average, secfilt, measure); 
	  break;
	case MAG_KRON_ERR:
	  value.Flt = measure[0].dMkron;
	  break;

	case MAG_PHOT_FLAGS:
	  if ((field->photcode->type == PHOT_REF) || (field->photcode->type == PHOT_DEP)) {
	    value.Int = measure[0].photFlags;
	  }
	  break;

	case MAG_NCODE:
	  if (equiv == NULL) break;
	  Nsec = GetPhotcodeNsec (equiv->code);
	  if (Nsec == -1) break;
	  value.Int = secfilt[Nsec].Ncode;
	  break;
	case MAG_NPHOT:
	  if (equiv == NULL) break;
	  Nsec = GetPhotcodeNsec (equiv->code);
	  if (Nsec == -1) break;
	  value.Int = secfilt[Nsec].Nused;
	  break;

	case MAG_CHISQ:
	  value.Flt = PhotXm (equiv, average, secfilt); 
	  break;
	case MAG_STDEV:
	  value.Flt = PhotMstdev (equiv, average, secfilt);
	  break;
	case MAG_20:
	  value.Flt = PhotM20 (equiv, average, secfilt);
	  break;
	case MAG_80:
	  value.Flt = PhotM80 (equiv, average, secfilt);
	  break;
	case MAG_UC_DIST:
	  value.Flt = PhotUCdist (equiv, 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 MEAS_RA: /* OK */
      value.Flt = average[0].R - measure[0].dR / 3600.0;
      break;
    case MEAS_DEC: /* OK */
      value.Flt = average[0].D - measure[0].dD / 3600.0;
      break;
    case MEAS_RA_AVE: /* OK */
      value.Flt = average[0].R;
      break;
    case MEAS_DEC_AVE: /* OK */
      value.Flt = average[0].D;
      break;

    case MEAS_GLON:
      if (!haveGalacticMeas) {
	ApplyTransform (&GLON_MEAS, &GLAT_MEAS, average[0].R - measure[0].dR / 3600.0, average[0].D - measure[0].dD / 3600.0, celestial_to_galactic);
	haveGalacticMeas = TRUE;
      }
      value.Flt = GLON_MEAS;
      break;
    case MEAS_GLAT:
      if (!haveGalacticMeas) {
	ApplyTransform (&GLON_MEAS, &GLAT_MEAS, average[0].R - measure[0].dR / 3600.0, average[0].D - measure[0].dD / 3600.0, celestial_to_galactic);
	haveGalacticMeas = TRUE;
      }
      value.Flt = GLAT_MEAS;
      break;
    case MEAS_ELON:
      if (!haveEclipticMeas) {
	ApplyTransform (&ELON_MEAS, &ELAT_MEAS, average[0].R - measure[0].dR / 3600.0, average[0].D - measure[0].dD / 3600.0, celestial_to_ecliptic);
	haveEclipticMeas = TRUE;
      }
      value.Flt = ELON_MEAS;
      break;
    case MEAS_ELAT:
      if (!haveEclipticMeas) {
	ApplyTransform (&ELON_MEAS, &ELAT_MEAS, average[0].R - measure[0].dR / 3600.0, average[0].D - measure[0].dD / 3600.0, celestial_to_ecliptic);
	haveEclipticMeas = TRUE;
      }
      value.Flt = ELAT_MEAS;
      break;

    case MEAS_GLON_AVE:
      if (!haveGalacticAve) {
	ApplyTransform (&GLON_AVE, &GLAT_AVE, average[0].R, average[0].D, celestial_to_galactic);
	haveGalacticAve = TRUE;
      }
      value.Flt = GLON_AVE;
      break;
    case MEAS_GLAT_AVE:
      if (!haveGalacticAve) {
	ApplyTransform (&GLON_AVE, &GLAT_AVE, average[0].R, average[0].D, celestial_to_galactic);
	haveGalacticAve = TRUE;
      }
      value.Flt = GLAT_AVE;
      break;
    case MEAS_ELON_AVE:
      if (!haveEclipticAve) {
	ApplyTransform (&ELON_AVE, &ELAT_AVE, average[0].R, average[0].D, celestial_to_ecliptic);
	haveEclipticAve = TRUE;
      }
      value.Flt = ELON_AVE;
      break;
    case MEAS_ELAT_AVE:
      if (!haveEclipticAve) {
	ApplyTransform (&ELON_AVE, &ELAT_AVE, average[0].R, average[0].D, celestial_to_ecliptic);
	haveEclipticAve = TRUE;
      }
      value.Flt = ELAT_AVE;
      break;

    case MEAS_RA_AVE_ERR: /* OK */
      value.Flt = average[0].dR;
      break;
    case MEAS_DEC_AVE_ERR: /* OK */
      value.Flt = average[0].dD;
      break;
    case MEAS_U_RA: /* OK */
      value.Flt = average[0].uR;
      break;
    case MEAS_U_DEC: /* OK */
      value.Flt = average[0].uD;
      break;
    case MEAS_U_RA_ERR: /* OK */
      value.Flt = average[0].duR;
      break;
    case MEAS_U_DEC_ERR: /* OK */
      value.Flt = average[0].duD;
      break;
    case MEAS_PAR: /* OK */
      value.Flt = average[0].P;
      break;
    case MEAS_PAR_ERR: /* OK */
      value.Flt = average[0].dP;
      break;
    case MEAS_CHISQ_POS: /* OK */
      value.Flt = average[0].ChiSqAve;
      break;
    case MEAS_CHISQ_PM: /* OK */
      value.Flt = average[0].ChiSqPM;
      break;
    case MEAS_CHISQ_PAR: /* OK */
      value.Flt = average[0].ChiSqPar;
      break;
    case MEAS_TMEAN: /* OK */
      value.Flt = TimeValue (average[0].Tmean, TimeReference, TimeFormat);
      break;
    case MEAS_TRANGE: /* OK */
      value.Flt = TimeValue (average[0].Trange, 0, TimeFormat);
      break;
    case MEAS_NMEAS: /* OK */
      value.Int = average[0].Nmeasure;
      break;
    case MEAS_NMISS: /* OK */
      value.Int = average[0].Nmissing;
      break;
    case MEAS_NPOS: /* OK */
      value.Int = average[0].Npos;
      break;
    case MEAS_OBJ_FLAGS: /* OK */
      value.Int = average[0].flags;
      break;
    case MEAS_SECFILT_FLAGS: /* OK */
      equiv = GetPhotcodeEquivbyCode (measure[0].photcode);
      if (!equiv) break;
      Nsec = GetPhotcodeNsec (equiv->code);
      if (Nsec == -1) break;
      value.Int = secfilt[Nsec].flags;
      break;

    // note that these represent the ra displacement relative to the average, not 
    // the error.
    case MEAS_RA_OFFSET: /* OK */
      value.Flt = measure[0].dR;
      break;
    case MEAS_DEC_OFFSET: /* OK */
      value.Flt = measure[0].dD;
      break;
    case MEAS_RA_FIT_OFFSET: /* OK */
      dT = (measure[0].t - average[0].Tmean) / (86400*365.25);
      value.Flt = average[0].uR * dT + measure[0].dR;
      break;
    case MEAS_DEC_FIT_OFFSET: /* OK */
      dT = (measure[0].t - average[0].Tmean) / (86400*365.25);
      value.Flt = average[0].uD * dT + measure[0].dD;
      break;
    case MEAS_RA_OFFSET_ERR: /* OK */
      value.Flt = NAN;
      break;
    case MEAS_DEC_OFFSET_ERR: /* OK */
      value.Flt = NAN;
      break;
    case MEAS_AIRMASS: /* OK */
      value.Flt = measure[0].airmass;
      break;
    case MEAS_MEAN_AIRMASS: /* OK */
      if (REMOTE_CLIENT) {
	ImageMetadata *image = MatchImageMetadataDVO (measure[0].imageID);
	if (image == NULL) break;
	value.Flt = image[0].secz;
      } else {
	Image *image = MatchImageDVO (measure[0].t, measure[0].photcode, measure[0].imageID);
	if (image == NULL) break;
	value.Flt = image[0].secz;
      }
      break;
    case MEAS_AZ: /* OK */
      value.Flt = measure[0].az;
      break;
    case MEAS_ALT: /* OK */
      value.Flt = 90.0 - DEG_RAD*acos(1.0/measure[0].airmass);
      break;
    case MEAS_EXPTIME: /* OK */
      value.Flt = pow (10.0, measure[0].dt * 0.4);
      break;
    case MEAS_PHOTCODE_EQUIV: /* OK */
      value.Int = GetPhotcodeEquivCodebyCode (measure[0].photcode);
      break;
    case MEAS_PHOTCODE: /* OK */
      value.Int = measure[0].photcode;
      break;
    case MEAS_PHOTCODE_KLAM: /* OK */
      { 
	PhotCode *code = GetPhotcodebyCode (measure[0].photcode);
	if (!code) break;
	value.Flt = code->K;
	break;
      }
    case MEAS_PHOTCODE_C: /* OK */
      { 
	PhotCode *code = GetPhotcodebyCode (measure[0].photcode);
	if (!code) break;
	value.Flt = code->C;
	break;
      }
    case MEAS_TIME: /* OK */
      value.Flt = TimeValue (measure[0].t, TimeReference, TimeFormat);
      break;
    case MEAS_FWHM: /* OK */
      value.Flt = FromShortPixels(measure[0].FWx + measure[0].FWy) / 2.0;
      break;
    case MEAS_FWHM_MAJ: /* OK */
      value.Flt = FromShortPixels(measure[0].FWx);
      break;
    case MEAS_FWHM_MIN: /* OK */
      value.Flt = FromShortPixels(measure[0].FWy);
      break;
    case MEAS_THETA: /* OK */
      value.Flt = FromShortDegrees(measure[0].theta);
      break;

    case MEAS_POSANGLE: /* OK */
      value.Flt = FromShortDegrees(measure[0].posangle);
      break;
    case MEAS_PLATESCALE: /* OK */
      value.Flt = measure[0].pltscale;
      break;

    case MEAS_MXX: /* OK */
      value.Flt = FromShortPixels(measure[0].Mxx);
      break;
    case MEAS_MXY: /* OK */
      value.Flt = FromShortPixels(measure[0].Mxy);
      break;
    case MEAS_MYY: /* OK */
      value.Flt = FromShortPixels(measure[0].Myy);
      break;
    case MEAS_DOPHOT: /* OK */
      value.Int = (measure[0].photFlags >> 16);
      break;
    case MEAS_DB_FLAGS: /* ? */
      value.Int = measure[0].dbFlags;
      break;
    case MEAS_PHOT_FLAGS: /* ? */
      value.Int = measure[0].photFlags;
      break;
    case MEAS_XCCD: /* OK */
# if MEASURE_HAS_XCCD
      value.Flt = measure[0].Xccd;
# else
      { 
	// I'm keeping this code because it gives a way of handing dvo dbs that don't have
	// measure.xccd if we need it
	Image *image;
	ra  = average[0].R - measure[0].dR / 3600.0;
	dec = average[0].D - measure[0].dD / 3600.0;
	image = MatchImageDVO (measure[0].t, measure[0].photcode, measure[0].imageID);
	if (image == NULL) break;
	RD_to_XY (&x, &y, ra, dec, &image[0].coords);
	value.Flt = x;
      }
# endif
      break;
    case MEAS_YCCD: /* OK */
# if MEASURE_HAS_XCCD
      value.Flt = measure[0].Yccd;
# else
      {
	Image *image;
	ra  = average[0].R - measure[0].dR / 3600.0;
	dec = average[0].D - measure[0].dD / 3600.0;
	image = MatchImageDVO (measure[0].t, measure[0].photcode, measure[0].imageID);
	if (image == NULL) break;
	RD_to_XY (&x, &y, ra, dec, &image[0].coords);
	value.Flt = y;
      }
# endif
      break;
    case MEAS_XFIX: /* OK */
      value.Flt = measure[0].Xfix;
      break;
    case MEAS_YFIX: /* OK */
      value.Flt = measure[0].Yfix;
      break;
    case MEAS_XCCD_ERR: /* OK */
      value.Flt = FromShortPixels(measure[0].dXccd);
      break;
    case MEAS_YCCD_ERR: /* OK */
      value.Flt = FromShortPixels(measure[0].dYccd);
      break;
    case MEAS_POS_SYS_ERR: /* OK */
      value.Flt = FromShortPixels(measure[0].dRsys);
      break;

    case MEAS_XFIELD: /* offset relative to exposure center in ra,dec space */
      if (!haveFieldMeas) {
	if (REMOTE_CLIENT) {
	  fieldc = MatchFieldMetadata (measure[0].imageID);
	} else {
	  // fprintf (stderr, "non-parallel Xmos broken\n");
	  // abort();
	  // fieldc = MatchField (measure[0].t, measure[0].photcode);
	  fieldc = MatchFieldMetadata (measure[0].imageID);
	}
	if (fieldc == NULL) break;
	double Rm = average[0].R - measure[0].dR / 3600.0;
	double Dm = average[0].D - measure[0].dD / 3600.0;
	RD_to_XY (&XFIELD_MEAS, &YFIELD_MEAS, Rm, Dm, fieldc);
      }
      value.Flt = XFIELD_MEAS;
      break;
    case MEAS_YFIELD: /* OK */
      if (!haveFieldMeas) {
	if (REMOTE_CLIENT) {
	  fieldc = MatchFieldMetadata (measure[0].imageID);
	} else {
	  // fprintf (stderr, "non-parallel Xmos broken\n");
	  // abort();
	  // fieldc = MatchField (measure[0].t, measure[0].photcode);
	  fieldc = MatchFieldMetadata (measure[0].imageID);
	}
	if (fieldc == NULL) break;
	double Rm = average[0].R - measure[0].dR / 3600.0;
	double Dm = average[0].D - measure[0].dD / 3600.0;
	RD_to_XY (&XFIELD_MEAS, &YFIELD_MEAS, Rm, Dm, fieldc);
      }
      value.Flt = YFIELD_MEAS;
      break;

    case MEAS_XMOSAIC: /* offset relative to exposure center in camera coords */
      if (!haveMosaicMeas) {
	if (REMOTE_CLIENT) {
	  mosaic = MatchMosaicMetadata (measure[0].imageID);
	} else {
	  // fprintf (stderr, "non-parallel Xmos broken\n");
	  // abort();
	  // mosaic = MatchMosaic (measure[0].t, measure[0].photcode);
	  mosaic = MatchMosaicMetadata (measure[0].imageID);
	}
	if (mosaic == NULL) break;
	double Rm = average[0].R - measure[0].dR / 3600.0;
	double Dm = average[0].D - measure[0].dD / 3600.0;
	RD_to_XY (&XMOS_MEAS, &YMOS_MEAS, Rm, Dm, mosaic);
      }
      value.Flt = XMOS_MEAS;
      break;
    case MEAS_YMOSAIC: /* OK */
      if (!haveMosaicMeas) {
	if (REMOTE_CLIENT) {
	  mosaic = MatchMosaicMetadata (measure[0].imageID);
	} else {
	  // fprintf (stderr, "non-parallel Xmos broken\n");
	  // abort();
	  // mosaic = MatchMosaic (measure[0].t, measure[0].photcode);
	  mosaic = MatchMosaicMetadata (measure[0].imageID);
	}
	if (mosaic == NULL) break;
	double Rm = average[0].R - measure[0].dR / 3600.0;
	double Dm = average[0].D - measure[0].dD / 3600.0;
	RD_to_XY (&XMOS_MEAS, &YMOS_MEAS, Rm, Dm, mosaic);
      }
      value.Flt = YMOS_MEAS;
      break;

    case MEAS_SKY: /* OK */
      value.Flt = measure[0].Sky;
      break;
    case MEAS_dSKY: /* OK */
      value.Flt = measure[0].dSky;
      break;
    case MEAS_DET_ID: /* OK */
      value.Int = measure[0].detID;
      break;
    case MEAS_OBJ_ID: /* OK */
      value.Int = average[0].objID;
      break;
    case MEAS_CAT_ID: /* OK */
      value.Int = average[0].catID;
      break;
    case MEAS_IMAGE_ID: /* OK */
      value.Int = measure[0].imageID;
      break;
    case MEAS_PSF_QF: /* OK */
      value.Flt = measure[0].psfQF;
      break;
    case MEAS_PSF_QF_PERFECT: /* OK */
      value.Flt = NAN;
      break;
    case MEAS_PSF_CHISQ: /* OK */
      value.Flt = measure[0].psfChisq;
      break;
    case MEAS_PSF_NDOF: /* OK */
      value.Int = measure[0].psfNdof;
      break;
    case MEAS_PSF_NPIX: /* OK */
      value.Int = measure[0].psfNpix;
      break;
    case MEAS_CR_NSIGMA: /* OK */
      value.Flt = measure[0].crNsigma;
      break;
    case MEAS_EXT_NSIGMA: /* OK */
      value.Flt = measure[0].extNsigma;
      break;
    case MEAS_EXTERN_ID: /* OK */
      if (REMOTE_CLIENT) {
	ImageMetadata *image = MatchImageMetadataDVO (measure[0].imageID);
	if (image == NULL) break;
	value.Int = image->externID;
      } else {
	Image *image = MatchImageDVO (measure[0].t, measure[0].photcode, measure[0].imageID);
	if (image == NULL) break;
	value.Int = image[0].externID;
      }
      break;

    case MEAS_EXPNAME_AS_INT:
      if (REMOTE_CLIENT) {
	ImageMetadata *image = MatchImageMetadataDVO (measure[0].imageID);
	if (image == NULL) break;
	value.Int = image->expname;
      } else {
	Image *image = MatchImageDVO (measure[0].t, measure[0].photcode, measure[0].imageID);
	if (image == NULL) break;
	// XXX very crude: if this matches oNNNNgNNNNo, then convert to an int
	// otherwise, leave as 0
	if ((image->name[0] == 'o') && (image->name[5] == 'g') && (image->name[10] == 'o')) {
	  int mjd  = atoi(&image->name[1]);
	  int Nexp = atoi(&image->name[6]);
	  value.Int = mjd * 10000 + Nexp;
	}
      }
      break;

    case MEAS_MCAL_OFFSET: /* OK */
      value.Flt = measure[0].Mcal;
      break;

    case MEAS_FLAT: /* OK */
      // flat = measure.Mcal - image.Mcal
      if (REMOTE_CLIENT) {
	ImageMetadata *image = MatchImageMetadataDVO (measure[0].imageID);
	if (image == NULL) break;
	value.Flt = measure[0].Mcal - image[0].Mcal;
      } else {
	Image *image = MatchImageDVO (measure[0].t, measure[0].photcode, measure[0].imageID);
	if (image == NULL) break;
	value.Flt = measure[0].Mcal - image[0].Mcal;
      }
      break;

      // we have measure[0].Xccd,Yccd and image[0].NX,NY.  Find the distance to the center
    case MEAS_CENTER_OFFSET: /* OK */
      if (REMOTE_CLIENT) {
	ImageMetadata *image = MatchImageMetadataDVO (measure[0].imageID);
	if (image == NULL) break;
	float Xcenter = image[0].Xcenter;
	float Ycenter = image[0].Ycenter;
	float distance = hypot (measure[0].Xccd - Xcenter, measure[0].Yccd - Ycenter);
	value.Flt = distance;
      } else {
	Image *image = MatchImageDVO (measure[0].t, measure[0].photcode, measure[0].imageID);
	if (image == NULL) break;
	// XXX we may hypotetically have images with -NX to +NX here (eg, projection center), but 
	// we do not get a detection from that type of image
	float Xcenter = 0.5*image[0].NX;
	float Ycenter = 0.5*image[0].NY;
	float distance = hypot (measure[0].Xccd - Xcenter, measure[0].Yccd - Ycenter);
	value.Flt = distance;
      }
      break;

    case MEAS_FLUX_PSF: /* OK */
      value.Flt = measure[0].FluxPSF;
      break;
    case MEAS_FLUX_PSF_ERR: /* OK */
      value.Flt = measure[0].dFluxPSF;
      break;
    case MEAS_FLUX_KRON: /* OK */
      value.Flt = measure[0].FluxKron;
      break;
    case MEAS_FLUX_KRON_ERR: /* OK */
      value.Flt = measure[0].dFluxKron;
      break;
  }
  return (value);
}

/** the mosaic entries do not use the registered mosaic found 
    by MatchImage (via FindMosaicForImage).  Rather, they use
    a coordinate frame saved by SetImageSelection 
**/
