# include "addstar.h"
# include "WISE.h"

int setWISE_ph_qual (Stars *star, char qual);
int setWISE_var_flag_allsky (Stars *star, char qual);
int setWISE_ext_flag_allsky (Stars **star, char ptr);
int setWISE_var_flag_prelim (Stars *star, char qual);
int setWISE_ext_flag_prelim (Stars **star, char ptr);
int setWISE_cc_flag (Stars *star, char qual);
int setWISE_sat_flag (Stars *star, char *ptr);
int setWISE_blend_flag (Stars **star, char *ptr);

// there are slight format differences between the prelim data dump and the allsky data dump:
// * after ph_qual & det_bit : new field moon_lev
// * for each filter, after w?sigp2 : new fields w?dmag, w?ndf, w?mlq, w?mjdmin, w?mjdmax, w?mjdmean
// * after w4mdjmean : new fields rho12, rho23, rho34, q12, q23, q34
// * after k_msig_2mass : new fields best_use_cntr, ngrp

// fill in the data for a WISE quad star.  takes a pointer to the start of the line the
// RA and DEC have already been set
int loadwise_star_allsky (Stars **star, char *line, int Nmax) {

  int i;
  char *ptr;

  if (line == NULL) Shutdown ("format error in WISE");

  ptr = line;

  // I can assign dRA and dDEC to dX and dY if I can consistently set posangle and pltscale
  star[0][0].measure.posangle = 0.0;
  star[0][0].measure.pltscale = 1.0;

  ptr = skipNbounds (ptr, '|', 3, Nmax); // skip: desig, ra, dec,
  star[0][0].measure.dXccd = ToShortPixels(strtod (ptr, NULL)); // sig_ra
  ptr = nextWISEfield (ptr);
  star[0][0].measure.dYccd = ToShortPixels(strtod (ptr, NULL)); // sig_dec
  ptr = nextWISEfield (ptr);

  // we only know a single set of values for all 4 bands 
  for (i = 1; i < 4; i++) {
      star[i][0].measure.dXccd    = star[0][0].measure.dXccd;
      star[i][0].measure.dYccd    = star[0][0].measure.dYccd;
      star[i][0].measure.posangle = star[0][0].measure.posangle;
      star[i][0].measure.pltscale = star[0][0].measure.pltscale;
  }
  ptr = skipNbounds (ptr, '|', 5, Nmax); // skip: sig_radec, glon, glat, elon, elat

  star[0][0].measure.Xccd = strtod (ptr, NULL); // wx
  ptr = nextWISEfield (ptr); // skip wx
  star[0][0].measure.Yccd = strtod (ptr, NULL); // wy
  ptr = nextWISEfield (ptr); // skip wy

  for (i = 1; i < 4; i++) {
      star[i][0].measure.Xccd    = star[0][0].measure.Xccd;
      star[i][0].measure.Yccd    = star[0][0].measure.Yccd;
  }
  ptr = skipNbounds (ptr, '|', 4, Nmax); // skip: cntr, source_id, coadd_id, src

  // W1
  for (i = 0; i < 4; i++) {
    char *endpoint;
      star[i][0].measure.M  = strtod (ptr, &endpoint); // w?mpro
      if (endpoint == ptr) {
	star[i][0].measure.M  = NAN;
      }
      ptr = nextWISEfield (ptr);
      star[i][0].measure.dM = strtod (ptr, &endpoint); // w?sigmpro
      if (endpoint == ptr) {
	star[i][0].measure.dM  = NAN;
      }
      ptr = skipNbounds (ptr, '|', 2, Nmax); // skip: w?sigmpro, w?snr
      star[i][0].measure.psfChisq = strtod (ptr, NULL); // w?rchi2
      ptr = nextWISEfield (ptr); // skip : w1rchi2

      // init the photFlags field
      star[i][0].measure.photFlags = 0;
  }

  ptr = nextWISEfield (ptr); // skip: rchi2

  // set blend flags for all 4 measures
  setWISE_blend_flag (star, ptr); // nb & na both used here
  ptr = skipNbounds (ptr, '|', 2, Nmax); // skip: nb, na

  for (i = 0; i < 4; i++) {
      setWISE_sat_flag (star[i], ptr); // w1sat
      ptr = nextWISEfield (ptr); 
  }
  ptr = nextWISEfield (ptr); // skip satnum

  for (i = 0; i < 4; i++) {
      setWISE_cc_flag (star[i], ptr[i]); // cc_flg
  }
  ptr = nextWISEfield (ptr); // skip cc_flags

  // set ext flags for all 4 measures
  setWISE_ext_flag_allsky (star, ptr[0]); // ext_flg
  ptr = nextWISEfield (ptr); // skip ext_flags

  for (i = 0; i < 4; i++) {
      setWISE_var_flag_allsky (star[i], ptr[i]); // var_flg
  }
  ptr = nextWISEfield (ptr); // skip var_flags

  for (i = 0; i < 4; i++) {
      setWISE_ph_qual (star[i], ptr[i]); // ph_qual
  }

  ptr = skipNbounds (ptr, '|', 159, Nmax); // skip: det_bit, moon_lev, w?nm, w?m, w?cov, etc, etc.

  for (i = 0; i < 4; i++) {
    ptr = skipNbounds (ptr, '|', 8, Nmax); // skip: det_bit, moon_lev, w?nm, w?m, w?cov, etc, etc.

    // double mjdmin = strtod (ptr, NULL); // mjd min
    // ptr = nextWISEfield (ptr); // skip
    // double mjdmax = strtod (ptr, NULL); // mjd max
    // ptr = nextWISEfield (ptr); // skip mjd max
    double mjdmean = strtod (ptr, NULL); // mjd mean
    ptr = nextWISEfield (ptr); // skip mjd mean

    // fprintf (stderr, "w%d mjd: %f\n", i, mjdmean);

    // the release is based on data taken in the period 14 January 2010 to 29 April 2010
    if (mjdmean == 0.0) {
      star[i][0].measure.t = 0;
    } else {
      star[i][0].measure.t = ohana_mjd_to_sec (mjdmean);
    }
  }

  star[0][0].measure.photcode  = WISE_W1;
  star[0][0].measure.detID   = 0;
  star[0][0].measure.imageID = 0;

  star[1][0].measure.photcode  = WISE_W2;
  star[1][0].measure.detID   = 0;
  star[1][0].measure.imageID = 0;

  star[2][0].measure.photcode  = WISE_W3;
  star[2][0].measure.detID   = 0;
  star[2][0].measure.imageID = 0;

  star[3][0].measure.photcode  = WISE_W4;
  star[3][0].measure.detID   = 0;
  star[3][0].measure.imageID = 0;

  return TRUE;
}

// fill in the data for a WISE quad star.  takes a pointer to the start of the line the
// RA and DEC have already been set
int loadwise_star_prelim (Stars **star, char *line, int Nmax) {

  int i;
  char *ptr;

  if (line == NULL) Shutdown ("format error in WISE");

  ptr = line;

  // I can assign dRA and dDEC to dX and dY if I can consistently set posangle and pltscale
  star[0][0].measure.posangle = 0.0;
  star[0][0].measure.pltscale = 1.0;

  ptr = skipNbounds (ptr, '|', 3, Nmax); // skip: desig, ra, dec,
  star[0][0].measure.dXccd = ToShortPixels(strtod (ptr, NULL)); // sig_ra
  ptr = nextWISEfield (ptr);
  star[0][0].measure.dYccd = ToShortPixels(strtod (ptr, NULL)); // sig_dec
  ptr = nextWISEfield (ptr);

  // we only know a single set of values for all 4 bands 
  for (i = 1; i < 4; i++) {
      star[i][0].measure.dXccd    = star[0][0].measure.dXccd;
      star[i][0].measure.dYccd    = star[0][0].measure.dYccd;
      star[i][0].measure.posangle = star[0][0].measure.posangle;
      star[i][0].measure.pltscale = star[0][0].measure.pltscale;
  }
  ptr = skipNbounds (ptr, '|', 5, Nmax); // skip: sig_radec, glon, glat, elon, elat

  star[0][0].measure.Xccd = strtod (ptr, NULL); // wx
  ptr = nextWISEfield (ptr); // skip wx
  star[0][0].measure.Yccd = strtod (ptr, NULL); // wy
  ptr = nextWISEfield (ptr); // skip wy

  for (i = 1; i < 4; i++) {
      star[i][0].measure.Xccd    = star[0][0].measure.Xccd;
      star[i][0].measure.Yccd    = star[0][0].measure.Yccd;
  }
  ptr = skipNbounds (ptr, '|', 4, Nmax); // skip: cntr, source_id, coadd_id, src

  // W1
  for (i = 0; i < 4; i++) {
      star[i][0].measure.M  = strtod (ptr, NULL); // w?mpro
      ptr = nextWISEfield (ptr);
      star[i][0].measure.dM = strtod (ptr, NULL); // w?sigmpro
      ptr = skipNbounds (ptr, '|', 2, Nmax); // skip: w?sigmpro, w?snr
      star[i][0].measure.psfChisq = strtod (ptr, NULL); // w?rchi2
      ptr = nextWISEfield (ptr); // skip : w1rchi2

      // init the photFlags field
      star[i][0].measure.photFlags = 0;
  }

  ptr = nextWISEfield (ptr); // skip: rchi2

  // set blend flags for all 4 measures
  setWISE_blend_flag (star, ptr); // nb & na both used here
  ptr = skipNbounds (ptr, '|', 2, Nmax); // skip: nb, na

  for (i = 0; i < 4; i++) {
      setWISE_sat_flag (star[i], ptr); // w1sat
      ptr = nextWISEfield (ptr); 
  }
  ptr = nextWISEfield (ptr); // skip satnum

  for (i = 0; i < 4; i++) {
      setWISE_cc_flag (star[i], ptr[i]); // cc_flg
  }
  ptr = nextWISEfield (ptr); // skip cc_flags

  // set ext flags for all 4 measures
  setWISE_ext_flag_prelim (star, *ptr); // ext_flg
  ptr = nextWISEfield (ptr); // skip ext_flags

  for (i = 0; i < 4; i++) {
      setWISE_var_flag_prelim (star[i], ptr[i]); // var_flg
  }
  ptr = nextWISEfield (ptr); // skip var_flags

  for (i = 0; i < 4; i++) {
      setWISE_ph_qual (star[i], ptr[i]); // ph_qual
  }

  double jd = 2455263.0; // NOTE : WISE prelim release does not contain per-detection time info. 
  // the release is based on data taken in the period 14 January 2010 to 29 April 2010
  star[0][0].measure.t = ohana_jd_to_sec (jd);
  star[1][0].measure.t = star[0][0].measure.t;
  star[2][0].measure.t = star[0][0].measure.t;
  star[3][0].measure.t = star[0][0].measure.t;

  star[0][0].measure.photcode  = WISE_W1;
  star[0][0].measure.detID   = 0;
  star[0][0].measure.imageID = 0;

  star[1][0].measure.photcode  = WISE_W2;
  star[1][0].measure.detID   = 0;
  star[1][0].measure.imageID = 0;

  star[2][0].measure.photcode  = WISE_W3;
  star[2][0].measure.detID   = 0;
  star[2][0].measure.imageID = 0;

  star[3][0].measure.photcode  = WISE_W4;
  star[3][0].measure.detID   = 0;
  star[3][0].measure.imageID = 0;

  return TRUE;
}

/* return a pointer to the first char after the next field separator (|) */
char *nextWISEfield (char *line) {

  char *p, *q;

  p = line;
  q = strchr (p, '|');
  if (q == NULL) return (NULL);
  p = q + 1;
  if (*p == 0) return (NULL);
  return (p);
}

# define FLAG_PH_A 	      0x00000001 // quality flag 'A'
# define FLAG_PH_B 	      0x00000002 // quality flag 'B'
# define FLAG_PH_C 	      0x00000004 // quality flag 'C'
# define FLAG_PH_U 	      0x00000008 // quality flag 'U'
# define FLAG_PH_X 	      0x00000010 // quality flag 'X'

# define FLAG_SATURATED_PIX   0x00000100 // sat > 0.0

# define FLAG_CC_PERSIST      0x00010000 // 'p' or 'P'
# define FLAG_CC_HALO         0x00020000 // 'h' or 'H'
# define FLAG_CC_GHOST        0x00020000 // 'o' or 'O' (note overloading)
# define FLAG_CC_SPIKE        0x00040000 // 'd' or 'D'
# define FLAG_CC_SPURIOUS     0x00080000 // Upper Case letters

# define FLAG_BLEND_ACTIVE    0x00100000 // nb > 1, na == 0
# define FLAG_BLEND_PASSIVE   0x00200000 // nb > 1, na == 1

# define FLAG_EXTENDED        0x01000000 // ext == 1
# define FLAG_EXT_IN_XSC      0x02000000 // ext == 1
# define FLAG_EXT_BY_XSC      0x04000000 // ext == 1

# define FLAG_VARIABLE_LEVEL1 0x10000000 // var_flg == 1 to 4
# define FLAG_VARIABLE_LEVEL2 0x20000000 // var_flg == 5 to 7
# define FLAG_VARIABLE_LEVEL3 0x40000000 // var_flg == 8 or 9

int setWISE_blend_flag (Stars **star, char *ptr) {

    int nb = atoi (ptr);
    if (nb == 1) return TRUE;

    ptr = nextWISEfield (ptr); // skip to na
    int na = atoi (ptr);

    if (na == 0) {
	star[0][0].measure.photFlags |= FLAG_BLEND_ACTIVE;
	star[1][0].measure.photFlags |= FLAG_BLEND_ACTIVE;
	star[2][0].measure.photFlags |= FLAG_BLEND_ACTIVE;
	star[3][0].measure.photFlags |= FLAG_BLEND_ACTIVE;
    } else {
	star[0][0].measure.photFlags |= FLAG_BLEND_PASSIVE;
	star[1][0].measure.photFlags |= FLAG_BLEND_PASSIVE;
	star[2][0].measure.photFlags |= FLAG_BLEND_PASSIVE;
	star[3][0].measure.photFlags |= FLAG_BLEND_PASSIVE;
    }
    return TRUE;
}

int setWISE_sat_flag (Stars *star, char *ptr) {

    float sat = strtod (ptr, NULL);
    if (sat > 0.0) {
	star[0].measure.photFlags |= FLAG_SATURATED_PIX;
    }
    return TRUE;
}

int setWISE_cc_flag (Stars *star, char qual) {

  switch (qual) {
    case 'p': star[0].measure.photFlags |= FLAG_CC_PERSIST; break;
    case 'h': star[0].measure.photFlags |= FLAG_CC_HALO;    break;
    case 'd': star[0].measure.photFlags |= FLAG_CC_SPIKE;   break;
    case 'o': star[0].measure.photFlags |= FLAG_CC_GHOST;   break;

    case 'P': star[0].measure.photFlags |= FLAG_CC_PERSIST | FLAG_CC_SPURIOUS; break;
    case 'H': star[0].measure.photFlags |= FLAG_CC_HALO    | FLAG_CC_SPURIOUS; break;
    case 'D': star[0].measure.photFlags |= FLAG_CC_SPIKE   | FLAG_CC_SPURIOUS; break;
    case 'O': star[0].measure.photFlags |= FLAG_CC_GHOST   | FLAG_CC_SPURIOUS; break;

    case '0': break;
    default: 
      fprintf (stderr, "error in cc_flag: %c\n", qual);
  }      
  return (TRUE);
}

int setWISE_ext_flag_allsky (Stars **star, char value) {

  switch (value) {
    case '0':
      return TRUE;
    case '1':
      star[0][0].measure.photFlags |= FLAG_EXTENDED;
      star[1][0].measure.photFlags |= FLAG_EXTENDED;
      star[2][0].measure.photFlags |= FLAG_EXTENDED;
      star[3][0].measure.photFlags |= FLAG_EXTENDED;
      return TRUE;
    case '2':
      star[0][0].measure.photFlags |= FLAG_EXT_IN_XSC;
      star[1][0].measure.photFlags |= FLAG_EXT_IN_XSC;
      star[2][0].measure.photFlags |= FLAG_EXT_IN_XSC;
      star[3][0].measure.photFlags |= FLAG_EXT_IN_XSC;
      return TRUE;
    case '3':
      star[0][0].measure.photFlags |= FLAG_EXTENDED | FLAG_EXT_IN_XSC;
      star[1][0].measure.photFlags |= FLAG_EXTENDED | FLAG_EXT_IN_XSC;
      star[2][0].measure.photFlags |= FLAG_EXTENDED | FLAG_EXT_IN_XSC;
      star[3][0].measure.photFlags |= FLAG_EXTENDED | FLAG_EXT_IN_XSC;
      return TRUE;
    case '4':
      star[0][0].measure.photFlags |= FLAG_EXT_BY_XSC;
      star[1][0].measure.photFlags |= FLAG_EXT_BY_XSC;
      star[2][0].measure.photFlags |= FLAG_EXT_BY_XSC;
      star[3][0].measure.photFlags |= FLAG_EXT_BY_XSC;
      return TRUE;
    case '5':
      star[0][0].measure.photFlags |= FLAG_EXTENDED | FLAG_EXT_BY_XSC;
      star[1][0].measure.photFlags |= FLAG_EXTENDED | FLAG_EXT_BY_XSC;
      star[2][0].measure.photFlags |= FLAG_EXTENDED | FLAG_EXT_BY_XSC;
      star[3][0].measure.photFlags |= FLAG_EXTENDED | FLAG_EXT_BY_XSC;
      return TRUE;
    default: 
      fprintf (stderr, "programming error\n");
      abort();
  }
  return TRUE;
}

int setWISE_ext_flag_prelim (Stars **star, char value) {

  switch (value) {
    case '0':
      return TRUE;
    case '1':
      star[0][0].measure.photFlags |= FLAG_EXTENDED;
      star[1][0].measure.photFlags |= FLAG_EXTENDED;
      star[2][0].measure.photFlags |= FLAG_EXTENDED;
      star[3][0].measure.photFlags |= FLAG_EXTENDED;
      return TRUE;
    default: 
      fprintf (stderr, "programming error\n");
      abort();
  }
  return TRUE;
}

// NOTE: var flag definition changed slightly between prelim & allsky
int setWISE_var_flag_allsky (Stars *star, char qual) {

  switch (qual) {
    case 'n':
	return TRUE;
    case '0':
    case '1':
    case '2':
    case '3':
    case '4':
    case '5':
	star[0].measure.photFlags |= FLAG_VARIABLE_LEVEL1;
	return TRUE;
    case '6':
    case '7':
	star[0].measure.photFlags |= FLAG_VARIABLE_LEVEL2;
	return TRUE;
    case '8':
    case '9':
      star[0].measure.photFlags |= FLAG_VARIABLE_LEVEL3;
      return TRUE;
    default: 
      fprintf (stderr, "programming error\n");
      abort();
  }
}

int setWISE_var_flag_prelim (Stars *star, char qual) {

  switch (qual) {
    case '0':
      return TRUE;
    case '1':
    case '2':
    case '3':
    case '4':
	star[0].measure.photFlags |= FLAG_VARIABLE_LEVEL1;
	return TRUE;
    case '5':
    case '6':
    case '7':
	star[0].measure.photFlags |= FLAG_VARIABLE_LEVEL2;
	return TRUE;
    case '8':
    case '9':
      star[0].measure.photFlags |= FLAG_VARIABLE_LEVEL3;
      return TRUE;
    default: 
      fprintf (stderr, "programming error\n");
      abort();
  }
}

int setWISE_ph_qual (Stars *star, char qual) {

  switch (qual) {
    case 'A': star[0].measure.photFlags |= FLAG_PH_A; break;
    case 'B': star[0].measure.photFlags |= FLAG_PH_B; break;
    case 'C': star[0].measure.photFlags |= FLAG_PH_C; break;
    case 'U': star[0].measure.photFlags |= FLAG_PH_U; break;
    case 'X': star[0].measure.photFlags |= FLAG_PH_X; break;
    default: 
      fprintf (stderr, "error in ph_flag: %c\n", qual);
  }      
  return (TRUE);
}

/* unused photFlag bits:

0x0000.0020
0x0000.0040
0x0000.0080

0x0000.0200
0x0000.0400
0x0000.0800

0x0000.1000
0x0000.2000
0x0000.4000
0x0000.8000

0x0040.0000
0x0080.0000

0x0200.0000
0x0400.0000
0x0800.0000

0x8000.0000

*/
