# include "relphot.h"

static int Nmax;
static double *list;
static double *dlist;

void initMrel (Catalog *catalog, int Ncatalog) {

  int i, j;
  
  Nmax = 0;
  for (i = 0; i < Ncatalog; i++) {
    for (j = 0; j < catalog[i].Naverage; j++) {
      Nmax = MAX (Nmax, catalog[i].average[j].Nmeasure);
    }
  }

  ALLOCATE (list, double, MAX (1, Nmax));
  ALLOCATE (dlist, double, MAX (1, Nmax));
}  

float getMrel (Catalog *catalog, int meas, int cat) {

  int ave;
  float value;

  ave = catalog[cat].measure[meas].averef;
  if (catalog[cat].average[ave].code & STAR_BAD) return (NAN);  
 
  value = catalog[cat].secfilt[PhotNsec*ave+PhotSec].M;
  return (value);
}

int setMrel (Catalog *catalog, int Ncatalog) {

  int i, j, k, m, N, Nfew, Nsys, Nbad, Ncal, Nmos, Ngrid;
  float Msys, Mcal, Mmos, Mgrid;
  StatType stats;

  Nfew = Nsys = Nbad = Ncal = Nmos = Ngrid = 0;

  for (i = 0; i < Ncatalog; i++) {
    for (j = 0; j < catalog[i].Naverage; j++) {

      /* calculate the average value for a single star */
      if (catalog[i].average[j].code & STAR_BAD) continue;  
      m = catalog[i].average[j].measureOffset;

      N = 0;
      for (k = 0; k < catalog[i].average[j].Nmeasure; k++, m++) {
	if (catalog[i].measure[m].dbFlags & MEAS_BAD) {
	  Nbad ++;
	  continue;
	}
	// XXX allow REF stars (no Image Entry) to be included in the calculation this
	// should be optionally set, and should allow for REF stars to be downweighted by
	// more than their reported errors.  how such info is carried is unclear...
	if (getImageEntry (m, i) < 0) {
	  Mcal = Mmos = Mgrid = 0;
	} else {
	  Mcal  = getMcal  (m, i);
	  if (isnan(Mcal)) { 
	    Ncal ++;
	    continue;
	  }
	  Mmos  = getMmos  (m, i);
	  if (isnan(Mmos)) {
	    Nmos ++;
	    continue;
	  }
	  Mgrid = getMgrid (m, i);
	  if (isnan(Mgrid)) {
	    Ngrid++;
	    continue;
	  }
	}

	Msys = PhotSys (&catalog[i].measure[m], &catalog[i].average[j], &catalog[i].secfilt[j*PhotNsec]);
	if (isnan(Msys)) {
	  Nsys++;
	  continue;
	}
	list[N] = Msys - Mcal - Mmos - Mgrid;
	dlist[N] = MAX (catalog[i].measure[m].dM, MIN_ERROR);
	N++;
      }

      // when performing the grid analysis, STAR_TOOFEW will be set to 1;

      if (N <= STAR_TOOFEW) { /* too few measurements */
	catalog[i].average[j].code |= ID_STAR_FEW;
	Nfew ++;
      } else {
	catalog[i].average[j].code &= ~ID_STAR_FEW;
      }	

      liststats (list, dlist, N, &stats);

      catalog[i].secfilt[PhotNsec*j+PhotSec].M  = stats.mean;
      catalog[i].secfilt[PhotNsec*j+PhotSec].dM = stats.sigma;
      catalog[i].secfilt[PhotNsec*j+PhotSec].Xm = (stats.Nmeas > 1) ? 100.0*log10(stats.chisq) : NAN_S_SHORT;
    }
  }
  fprintf (stderr, "%d stars marked having too few measurements (Nbad: %d, Ncal: %d, Nmos: %d, Ngrid: %d, Nsys: %d)\n", Nfew, Nbad, Ncal, Nmos, Ngrid, Nsys);

  return (TRUE);
}

int setMrelOutput (Catalog *catalog, int Ncatalog, int mark) {

  int i, j, k, m, N, Nmax;
  float Msys, Mcal, Mmos, Mgrid;
  double *list, *dlist;
  StatType stats;

  /* Nmeasure is now different, need to reallocate */
  Nmax = 0;
  for (i = 0; i < Ncatalog; i++) {
    for (j = 0; j < catalog[i].Naverage; j++) {
      Nmax = MAX (Nmax, catalog[i].average[j].Nmeasure);
    }
  }
  ALLOCATE (list, double, MAX (1, Nmax));
  ALLOCATE (dlist, double, MAX (1, Nmax));

  for (i = 0; i < Ncatalog; i++) {
    for (j = 0; j < catalog[i].Naverage; j++) {

      /* skip stars already calibrated */
      if (catalog[i].found[j]) continue;  

      N = 0;
      m = catalog[i].average[j].measureOffset;
      for (k = 0; k < catalog[i].average[j].Nmeasure; k++, m++) {
	if (catalog[i].measure[m].dbFlags & MEAS_BAD) continue;
	// XXX allow REF stars (no Image Entry) to be included in the calculation this
	// should be optionally set, and should allow for REF stars to be downweighted by
	// more than their reported errors.  how such info is carried is unclear...
	if (getImageEntry (m, i) < 0) {
	  Mcal = Mmos = Mgrid = 0;
	} else {
	  Mcal  = getMcal  (m, i);
	  if (isnan(Mcal)) continue;
	  Mmos  = getMmos  (m, i);
	  if (isnan(Mmos)) continue;
	  Mgrid = getMgrid (m, i);
	  if (isnan(Mgrid)) continue;
	}

	Msys = PhotSys (&catalog[i].measure[m], &catalog[i].average[j], &catalog[i].secfilt[j*PhotNsec]);
	list[N] = Msys - Mcal - Mmos - Mgrid;
	dlist[N] = MAX (catalog[i].measure[m].dM, MIN_ERROR);
	N++;
      }
      if (N < 1) continue;

      liststats (list, dlist, N, &stats);
      if (mark) catalog[i].found[j] = TRUE;

      /* use sigma or error in dM for output? */
      catalog[i].secfilt[PhotNsec*j+PhotSec].M  = stats.mean;
      catalog[i].secfilt[PhotNsec*j+PhotSec].dM = MAX (stats.error, stats.sigma);
      catalog[i].secfilt[PhotNsec*j+PhotSec].Xm = (stats.Nmeas > 1) ? 100.0*log10(stats.chisq) : NAN_S_SHORT;
    }
  }

  free (list);
  free (dlist);
  return (TRUE);
}

// for each average object, set the average mags based on existing equiv photometry
int setMave (Catalog *catalog, int Ncatalog) {

  int i, j, k, m, Ns, Nsecfilt, N, Nmax, Nc;
  float Msys;
  double *list, *dlist;
  StatType stats;
  PhotCode *code;

  // pre-allocate a list for stats purposes
  Nmax = 0;
  for (i = 0; i < Ncatalog; i++) {
    for (j = 0; j < catalog[i].Naverage; j++) {
      Nmax = MAX (Nmax, catalog[i].average[j].Nmeasure);
    }
  }
  ALLOCATE (list, double, MAX (1, Nmax));
  ALLOCATE (dlist, double, MAX (1, Nmax));

  Nsecfilt = catalog[0].Nsecfilt;

  for (i = 0; i < Ncatalog; i++) {
    for (j = 0; j < catalog[i].Naverage; j++) {
      for (Ns = 0; Ns < Nsecfilt; Ns++) {

	code = GetPhotcodebyNsec (Ns);
	Nc = code[0].code;
	
	N = 0;
	m = catalog[i].average[j].measureOffset;
	for (k = 0; k < catalog[i].average[j].Nmeasure; k++, m++) {
	  if (catalog[i].measure[m].dbFlags & MEAS_BAD) continue;
	  if (GetPhotcodeEquivCodebyCode (catalog[i].measure[m].photcode) != Nc) continue;

	  Msys = PhotSys (&catalog[i].measure[m], &catalog[i].average[j], &catalog[i].secfilt[j*PhotNsec]);
	  if (isnan(Msys)) continue;

	  list[N] = Msys - catalog[i].measure[m].Mcal;
	  dlist[N] = MAX (catalog[i].measure[m].dM, MIN_ERROR);
	  N++;
	}
	if (N < 1) continue;

	liststats (list, dlist, N, &stats);

	/* use sigma or error in dM for output? */
	catalog[i].secfilt[Nsecfilt*j+Ns].M  = stats.mean;
	catalog[i].secfilt[Nsecfilt*j+Ns].dM = MAX (stats.error, stats.sigma);
	catalog[i].secfilt[Nsecfilt*j+Ns].Xm = (stats.Nmeas > 1) ? 100.0*log10(stats.chisq) : NAN_S_SHORT;
	catalog[i].secfilt[Nsecfilt*j+Ns].Ncode = N;
	catalog[i].secfilt[Nsecfilt*j+Ns].Nused = stats.Nmeas;
      }
    }
  }

  free (list);
  free (dlist);
  return (TRUE);
}

/* set measure.Mcal for all measures except ID_MEAS_NOCAL and ID_IMAGE_NOCAL */
int setMcalOutput (Catalog *catalog, int Ncatalog) {

  int i, j, k, m;
  float Mcal, Mmos, Mgrid;

  MEAS_BAD = ID_MEAS_NOCAL;
  IMAGE_BAD = ID_IMAGE_NOCAL;

  for (i = 0; i < Ncatalog; i++) {
    for (j = 0; j < catalog[i].Naverage; j++) {

      m = catalog[i].average[j].measureOffset;
      for (k = 0; k < catalog[i].average[j].Nmeasure; k++, m++) {
	if (catalog[i].measure[m].dbFlags & MEAS_BAD) continue;
	Mcal  = getMcal  (m, i);
	if (isnan(Mcal)) continue;
	Mmos  = getMmos  (m, i);
	if (isnan(Mmos)) continue;
	Mgrid = getMgrid (m, i);
	if (isnan(Mgrid)) continue;
	catalog[i].measure[m].Mcal = Mcal + Mmos + Mgrid;
      }
    }
  }
  return (TRUE);
}

void clean_stars (Catalog *catalog, int Ncatalog) {

  int i, j, Ndel, Nave, Ntot, mark;
  float dM, Xm;
  double Chisq, MaxScatter, MaxChisq;
  double *xlist, *slist, *dlist;
  StatType stats;

  if (VERBOSE) fprintf (stderr, "marking poor stars\n");

  /* find Xm median -> ChiSq lim must be > median */
  for (i = Ntot = 0; i < Ncatalog; i++) {
    Ntot += catalog[i].Naverage; 
  }
  ALLOCATE (xlist, double, Ntot);
  ALLOCATE (slist, double, Ntot);
  ALLOCATE (dlist, double, Ntot);
  for (i = Ntot = 0; i < Ncatalog; i++) {
    for (j = 0; j < catalog[i].Naverage; j++) {
      if (catalog[i].average[j].code & STAR_BAD) continue;
      Xm = catalog[i].secfilt[PhotNsec*j+PhotSec].Xm;
      if (Xm == -1) continue;
      Chisq = pow (10.0, 0.01*Xm);
      xlist[Ntot] = Chisq;
      slist[Ntot] = catalog[i].secfilt[PhotNsec*j+PhotSec].dM;
      dlist[Ntot] = 1;
      Ntot ++;
    }
  }
  
  initstats ("MEAN");
  liststats (xlist, dlist, Ntot, &stats);
  MaxChisq = MAX (STAR_CHISQ, 2*stats.median);
  liststats (slist, dlist, Ntot, &stats);
  MaxScatter = MAX (STAR_SCATTER, 2*stats.median);
  fprintf (stderr, "Max Scatter: %f, Max Chisq: %f\n", MaxScatter, MaxChisq);

  Ndel = Nave = 0;
  for (i = 0; i < Ncatalog; i++) {
    for (j = 0; j < catalog[i].Naverage; j++) {
      dM = catalog[i].secfilt[PhotNsec*j+PhotSec].dM;
      Xm = catalog[i].secfilt[PhotNsec*j+PhotSec].Xm;
      Chisq = pow (10.0, 0.01*Xm);
      mark = (dM > MaxScatter) || (Xm == NAN_S_SHORT) || (Chisq > MaxChisq);
      if (mark) {
	catalog[i].average[j].code |= ID_STAR_POOR;
	Ndel ++;
      } else {
	catalog[i].average[j].code &= ~ID_STAR_POOR;
      }
      Nave ++;
    }
  }
  fprintf (stderr, "%d stars marked variable, %d total\n", Ndel, Nave);
  initstats (STATMODE);
  free (xlist);
  free (slist);
  free (dlist);
}

void clean_measures (Catalog *catalog, int Ncatalog, int final) {

  int i, j, k, m, N, Ndel, Nave, Nmax, image_bad, TOOFEW;
  int *ilist;
  double *tlist, *list, *dlist, Ns;
  float Msys, Mcal, Mmos, Mgrid;
  StatType stats;

  if (VERBOSE) fprintf (stderr, "marking poor measures\n");
  /* Nmeasure is now different, need to reallocate */
  Nmax = 0;
  for (i = 0; i < Ncatalog; i++) {
    for (j = 0; j < catalog[i].Naverage; j++) {
      Nmax = MAX (Nmax, catalog[i].average[j].Nmeasure);
    }
  }
  ALLOCATE (list, double, MAX (1, Nmax));
  ALLOCATE (dlist, double, MAX (1, Nmax));
  ALLOCATE (ilist, int, Nmax);
  ALLOCATE (tlist, double, Nmax);
  
  /* it makes no sense to mark 3-sigma outliers with <5 measurements */
  TOOFEW = MAX (5, STAR_TOOFEW);

  Ns = 3;
  Ndel = Nave = 0;
  for (i = 0; i < Ncatalog; i++) {
    for (j = 0; j < catalog[i].Naverage; j++) {

      /* skip bad stars to prevent them from becoming good (on inner sample) */
      if (catalog[i].average[j].code & STAR_BAD) continue;  

      /* on final processing, skip stars already measured */
      if (final && catalog[i].found[j]) continue;  

      /* accumulate list of valid measurements */
      m = catalog[i].average[j].measureOffset;
      N = 0;
      for (k = 0; k < catalog[i].average[j].Nmeasure; k++, m++) {
	/* if (catalog[i].measure[m].dbFlags & MEAS_BAD) continue; */
	Mcal  = getMcal  (m, i);
	if (isnan(Mcal)) continue;
	Mmos  = getMmos  (m, i);
	if (isnan(Mmos)) continue;
	Mgrid = getMgrid (m, i);
	if (isnan(Mgrid)) continue;

	Msys = PhotSys (&catalog[i].measure[m], &catalog[i].average[j], &catalog[i].secfilt[j*PhotNsec]);
	list[N] = Msys - Mcal - Mmos - Mgrid;
	dlist[N] = MAX (catalog[i].measure[m].dM, MIN_ERROR);
	N++;
      }
      if (N <= TOOFEW) continue;

      /* 3-sigma clip based on stats of inner 50% */
      initstats ("INNER_MEAN");
      liststats (list, dlist, N, &stats);
      stats.sigma = MAX (MIN_ERROR, stats.sigma); /* if measurements agree too well, sigma -> 0.0 */
      for (k = m = 0; k < N; k++) {
	if (fabs (list[k] - stats.median) < Ns*stats.sigma) {
	  list[m] = list[k];
	  m++;
	}
      }
      initstats ("MEAN");
      liststats (list, dlist, m, &stats);
      stats.sigma = MAX (MIN_ERROR, stats.sigma);

      /* apply to list of all relevant measurements, including IMAGE_POOR & IMAGE_FEW */
      image_bad = IMAGE_BAD;
      IMAGE_BAD = ID_IMAGE_NOCAL;
      m = catalog[i].average[j].measureOffset;
      N = 0;
      for (k = 0; k < catalog[i].average[j].Nmeasure; k++, m++) {
	/* if (catalog[i].measure[m].dbFlags & MEAS_BAD) continue; */
	Mcal  = getMcal  (m, i);
	if (isnan(Mcal)) continue;
	Mmos  = getMmos  (m, i);
	if (isnan(Mmos)) continue;
	Mgrid = getMgrid (m, i);
	if (isnan(Mgrid)) continue;

	Msys = PhotSys (&catalog[i].measure[m], &catalog[i].average[j], &catalog[i].secfilt[j*PhotNsec]);
	list[N] = Msys - Mcal - Mmos - Mgrid;
	dlist[N] = MAX (catalog[i].measure[m].dM, MIN_ERROR);
	ilist[N] = m;
	N++;
	Nave ++;
      }
      if (N < TOOFEW) continue;

      /* mark bad measures */
      for (k = 0; k < N; k++) {
	if (fabs (list[k] - stats.median) > Ns*stats.sigma) {
	  catalog[i].measure[ilist[k]].dbFlags |= ID_MEAS_POOR_PHOTOM;
	  Ndel ++;
	}
      }
      IMAGE_BAD = image_bad;
    }
  }
  initstats (STATMODE);
  if (VERBOSE) fprintf (stderr, "%d measures marked poor, %d total\n", Ndel, Nave);
  free (ilist);
  free (tlist);
}

StatType statsStarN (Catalog *catalog, int Ncatalog) {

  int i, j, k, m, n, N, Ntot;
  double *list, *dlist;
  float Mcal, Mmos, Mgrid;
  StatType stats;

  Ntot = 0;
  for (i = 0; i < Ncatalog; i++) {
    Ntot += catalog[i].Naverage; 
  }

  ALLOCATE (list, double, Ntot);
  ALLOCATE (dlist, double, Ntot);

  n = 0;
  for (i = 0; i < Ncatalog; i++) {
    for (j = 0; j < catalog[i].Naverage; j++) {

      /* calculate the average value for a single star */
      if (catalog[i].average[j].code & STAR_BAD) continue;  
      m = catalog[i].average[j].measureOffset;

      N = 0;
      for (k = 0; k < catalog[i].average[j].Nmeasure; k++, m++) {
	Mcal = getMcal  (m, i);
	if (isnan(Mcal)) continue;
	Mmos = getMmos  (m, i);
	if (isnan(Mmos)) continue;
	Mgrid = getMgrid (m, i);
	if (isnan(Mgrid)) continue;
	N++;
      }
      
      list[n] = N;
      dlist[n] = 1;
      n++;
    }
  }

  liststats (list, dlist, n, &stats);
  free (list);
  free (dlist);
  return (stats);
}

StatType statsStarX (Catalog *catalog, int Ncatalog) {

  int i, j, n, Ntot, Xm;
  double *list, *dlist;
  StatType stats;

  Ntot = 0;
  for (i = 0; i < Ncatalog; i++) {
    Ntot += catalog[i].Naverage; 
  }

  ALLOCATE (list, double, Ntot);
  ALLOCATE (dlist, double, Ntot);

  n = 0;
  for (i = 0; i < Ncatalog; i++) {
    for (j = 0; j < catalog[i].Naverage; j++) {

      /* calculate the average value for a single star */
      if (catalog[i].average[j].code & STAR_BAD) continue;  

      Xm = catalog[i].secfilt[PhotNsec*j+PhotSec].Xm;
      if (Xm == NAN_S_SHORT) continue;
      list[n] = pow (10.0, 0.01*Xm);
      dlist[n] = 1;
      n++;
    }
  }

  liststats (list, dlist, n, &stats);
  free (list);
  free (dlist);
  return (stats);
}

StatType statsStarS (Catalog *catalog, int Ncatalog) {

  int i, j, n, Ntot;
  double *list, *dlist;
  float dM;
  StatType stats;

  Ntot = 0;
  for (i = 0; i < Ncatalog; i++) {
    Ntot += catalog[i].Naverage; 
  }

  ALLOCATE (list, double, Ntot);
  ALLOCATE (dlist, double, Ntot);

  n = 0;
  for (i = 0; i < Ncatalog; i++) {
    for (j = 0; j < catalog[i].Naverage; j++) {

      /* calculate the average value for a single star */
      if (catalog[i].average[j].code & STAR_BAD) continue;  

      dM = catalog[i].secfilt[PhotNsec*j+PhotSec].dM;
      list[n] = dM;
      dlist[n] = 1;
      n++;
    }
  }

  liststats (list, dlist, n, &stats);
  free (list);
  free (dlist);
  return (stats);
}

void plot_stars (Catalog *catalog, int Ncatalog) {

  int i, j, bin;
  float dMrel;
  double *xlist, *Mlist;
  Graphdata graphdata;

# define NBIN 200
  ALLOCATE (xlist, double, NBIN);
  ALLOCATE (Mlist, double, NBIN);

  for (i = 0; i < NBIN; i++) xlist[i] = 0.00025*i;
  bzero (Mlist, NBIN*sizeof(double));
  for (i = 0; i < Ncatalog; i++) {
    for (j = 0; j < catalog[i].Naverage; j++) {
      if (catalog[i].average[j].code & STAR_BAD) continue;  
      dMrel = catalog[i].secfilt[PhotNsec*j+PhotSec].dM;
      bin = dMrel / 0.00025;
      bin = MAX (0, MIN (NBIN-1, bin));
      Mlist[bin] += 1.0;
    }
  }

  plot_defaults (&graphdata);
  graphdata.style = 1;
  plot_list (&graphdata, xlist, Mlist, NBIN, "dMrel hist", "%s.dMhist.png", OUTROOT);

  free (xlist);
  free (Mlist);
}

void plot_chisq (Catalog *catalog, int Ncatalog) {

  int i, j, N, Ntotal, value;
  double *xlist, *ylist;
  Graphdata graphdata;

  Ntotal = 0;
  for (i = 0; i < Ncatalog; i++) Ntotal += catalog[i].Naverage;

  ALLOCATE (xlist, double, Ntotal);
  ALLOCATE (ylist, double, Ntotal);

  N = 0;
  for (i = 0; i < Ncatalog; i++) {
    for (j = 0; j < catalog[i].Naverage; j++) {
      if (catalog[i].average[j].code & STAR_BAD) continue;
      xlist[N] = catalog[i].secfilt[PhotNsec*j+PhotSec].M;
      value    = catalog[i].secfilt[PhotNsec*j+PhotSec].Xm;
      if (value == NAN_S_SHORT) continue;
      ylist[N] = 0.01*value;
      N++;
    }
  }

  plot_defaults (&graphdata);
  graphdata.ymin = -3.0;
  plot_list (&graphdata, xlist, ylist, N, "chisq", "%s.chisq.png", OUTROOT);
  free (xlist);
  free (ylist);
}

void plot_star_coords (Catalog *catalog, int Ncatalog) {

  int i, j, N;
  double *xlist, *ylist;
  double Xmin, Ymin, Xmax, Ymax;
  Graphdata graphdata;

  N = 0; 
  for (i = 0; i < Ncatalog; i++) {
    N += catalog[i].Naverage;
  }
  ALLOCATE (xlist, double, N);
  ALLOCATE (ylist, double, N);

  N = 0;
  Xmin = Ymin = +360.0;
  Xmax = Ymax = -360.0;
  for (i = 0; i < Ncatalog; i++) {
    for (j = 0; j < catalog[i].Naverage; j++) {
      xlist[N] = catalog[i].average[j].R;
      ylist[N] = catalog[i].average[j].D;
      N++;
    }
  }
  plot_defaults (&graphdata);
  plot_list (&graphdata, xlist, ylist, N, "coords", "%s.coords.png", OUTROOT);

  free (xlist);
  free (ylist);
}

