/**************************************************************

adcurve.c (C-Munipack project)
Copyright (C) 2003 David Motl, dmotl@volny.cz

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

**************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <limits.h>

#include "comfun.h"
#include "console.h"
#include "cmpack_common.h"
#include "cmpack_adcurve.h"

/* Star attributes */
typedef struct _ADStarInfo
{
	int id;					/**< Star identifier */
	int index;				/**< Frameset's object index */
	int valid;				/**< Nonzero if magnitude and error are valid */
	double mag;				/**< Magnitude and error */
} ADStarInfo;

/* List of selected stars */
typedef struct _ADStarList
{
	ADStarInfo *list;			/**< List of star identifiers */
	int	count;				/**< Number of items */
} ADStarList;

/* List process context */
struct _CmpackADCurve
{
	int refcnt;				/**< Reference counter */
	CmpackConsole *con;		/**< Console */
	ADStarList comp;			/**< Measurements of comparison stars */
	ADStarList chk;			/**< Measurements of check stars */
};

/*********************   LOCAL FUNCTIONS   ****************************/

/* Set list of stars */
static void stars_set(ADStarList *table, const int *list, int count)
{
	int i;

	if (table->list) {
		cmpack_free(table->list);
		table->list = NULL;
		table->count = 0;
	}
	if (list && count>0) {
		table->list = (ADStarInfo*)cmpack_calloc(count, sizeof(ADStarInfo));
		for (i=0; i<count; i++)
			table->list[i].id = list[i];
		table->count = count;
	}
}

/* Get list of stars */
static void stars_get(ADStarList *table, int **plist, int *pcount)
{
	int i, *list;

	if (table->list) {
		list = cmpack_malloc(table->count*sizeof(int));
		for (i=0; i<table->count; i++)
			list[i] = table->list[i].id;
		*plist = list;
		*pcount = table->count;
	} else {
		*plist = NULL;
		*pcount = 0;
	}
}

static int star_get(ADStarInfo *info, CmpackFrameSet *fset, int ap_index)
{
	CmpackPhtData data;

	info->valid = 0;
	if (info->index>=0) {
		data.mag_valid = 0;
		cmpack_fset_get_data(fset, info->index, ap_index, &data);
		if (data.mag_valid) {
			info->valid = data.mag_valid;
			info->mag = data.magnitude;
			return 1;
		}
	}
	return 0;
}

static int comp_star(CmpackFrameSet *fset, ADStarInfo *objs, int count, double *mag)
{
	int i, n;
	double sum;

	if (count>1) {
		/* Compute artificial comparison star */
		sum = 0.0; n = 0;
		for (i=0; i<count; i++) {
			if (objs[i].valid) {
				sum += pow(10.0, -0.4*objs[i].mag);
				n++;
			}
		}
		if (n==count) {
			*mag = -2.5*log10(sum/n);
			return 1;
		}
	} else {
		/* Single comparison star */
		if (objs[0].valid) {
			*mag = objs[0].mag;
			return 1;
		}
	}
	return 0;
}

/********************   PUBLIC FUNCTIONS   **************************/

/* Make new listing context */
CmpackADCurve *cmpack_adcurve_init(void)
{
	CmpackADCurve *pc = (CmpackADCurve*)cmpack_calloc(1, sizeof(CmpackADCurve));
	pc->refcnt = 1;
	return pc;
}

/* Increment the reference counter */
CmpackADCurve *cmpack_adcurve_reference(CmpackADCurve *ctx)
{
	ctx->refcnt++;
	return ctx;
}

/* Decrement reference counter / detroy the instance */
void cmpack_adcurve_destroy(CmpackADCurve *ctx)
{
	if (ctx) {
		ctx->refcnt--;
		if (ctx->refcnt==0) {
			cmpack_free(ctx->comp.list);
			cmpack_free(ctx->chk.list);
			if (ctx->con) {
				cmpack_con_destroy(ctx->con);
				ctx->con = NULL;
			}
			cmpack_free(ctx);
		}
	}
}

/* Attach console */
void cmpack_adcurve_set_console(CmpackADCurve *ctx, CmpackConsole *con)
{
	if (con!=ctx->con) {
		if (ctx->con) 
			cmpack_con_destroy(ctx->con);
		ctx->con = con;
		if (ctx->con) 
			cmpack_con_reference(ctx->con);
	}
}

/* Set list of comparison stars */
void cmpack_adcurve_set_comp(CmpackADCurve *lc, const int *items, int nitems)
{
	stars_set(&lc->comp, items, nitems);
}

/* Set list of comparison stars */
void cmpack_adcurve_get_comp(CmpackADCurve *lc, int **items, int *nitems)
{
	stars_get(&lc->comp, items, nitems);
}

/* Set list of check stars */
void cmpack_adcurve_set_check(CmpackADCurve *lc, const int *items, int nitems)
{
	stars_set(&lc->chk, items, nitems);
}

/* Set list of check stars */
void cmpack_adcurve_get_check(CmpackADCurve *lc, int **items, int *nitems)
{
	stars_get(&lc->chk, items, nitems);
}

/* Open output table, start reading sequence */
int cmpack_adcurve(CmpackADCurve *lc, CmpackFrameSet *fset, CmpackTable **ptable, 
	CmpackADCurveFlags flags)
{	
	int		i, j, k, n, res, col;
	int		nframes, napertures, comp_count, check_count, id_column;
	char	buf[80];
	double mean, stddev, *aux, cmag;
	CmpackTable *table;
	CmpackPhtAperture aper;
	CmpackFrameInfo frame;
	ADStarInfo *comp, *chk;

	*ptable = NULL;

	/* Check parameters */
	comp_count = lc->comp.count;
	if (comp_count<1) {
		printout(lc->con, 0, "At least one comparison star must be selected");
		return CMPACK_ERR_INVALID_PAR;
	}
	check_count = lc->chk.count;
	if (check_count<1) {
		printout(lc->con, 0, "At least one check star must be selected");
		return CMPACK_ERR_INVALID_PAR;
	}
	nframes = cmpack_fset_frame_count(fset);
	if (nframes<2) {
		printout(lc->con, 0, "At least one frame must be in the frameset");
		return CMPACK_ERR_INVALID_PAR;
	}
	comp = lc->comp.list;
	for (i=0; i<lc->comp.count; i++)
		comp[i].index = cmpack_fset_find_object(fset, comp[i].id);
	chk = lc->chk.list;
	for (i=0; i<lc->chk.count; i++)
		chk[i].index = cmpack_fset_find_object(fset, chk[i].id);
	aux = (double*)cmpack_malloc(nframes*sizeof(double));
	if (!aux) {
		printout(lc->con, 0, "Memory allocation error");
		return CMPACK_ERR_MEMORY;
	}
	
	/* Create table */
	table = cmpack_tab_init(CMPACK_TABLE_APERTURES);
	cmpack_fset_rewind(fset);
	if (cmpack_fset_get_frame(fset, CMPACK_FI_FILTER, &frame)==0) {
		if (frame.filter && frame.filter[0]!='\0')
			cmpack_tab_pkys(table, "Filter", frame.filter);
	}
	id_column = cmpack_tab_add_column_int(table, "APERTURE", 0, INT_MAX, -1);
	if (comp_count>0) {
		for (j=0; j<check_count; j++) {
			sprintf(buf, "C-K%d", j+1);
			cmpack_tab_add_column_dbl(table, buf, MAG_PRECISION, -99.0, 99.0, INVALID_MAG);
		}
	}
	for (i=0; i<check_count; i++) {
		for (j=i+1; j<check_count; j++) {
			sprintf(buf, "K%d-K%d", i+1, j+1);
			cmpack_tab_add_column_dbl(table, buf, MAG_PRECISION, -99.0, 99.0, INVALID_MAG);
		}
	}

	/* Process apertures */
	napertures = cmpack_fset_aperture_count(fset);
	for (k=0; k<napertures; k++) {
		if (cmpack_fset_get_aperture(fset, k, CMPACK_PA_ID, &aper)==0) {
			cmpack_tab_append(table);
			cmpack_tab_ptdi(table, id_column, aper.id);
			/* C-K1, C-K2, ... */
			col = 1;
			if (comp_count>0) {
				for (j=0; j<check_count; j++) {
					n = 0;
					res = cmpack_fset_rewind(fset);
					while (res==0) {
						/* Read magnitudes for selected stars */
						for (i=0; i<lc->comp.count; i++)
							star_get(&comp[i], fset, k);
						if (comp_star(fset, comp, comp_count, &cmag)) {
							if (star_get(&chk[j], fset, k))
								aux[n++] = chk[j].mag - cmag;
						}
						res = cmpack_fset_next(fset);
					}
					if (n>1)
						cmpack_robustmean(n, aux, &mean, &stddev);
					else
						stddev = -1.0;
					cmpack_tab_ptdd(table, col++, stddev);
				}
			}
			/* K1-K2, ... */
			for (j=0; j<check_count; j++) {
				for (i=j+1; i<check_count; i++) {
					n = 0;
					res = cmpack_fset_rewind(fset);
					while (res==0) {
						if (star_get(&chk[i], fset, k) && star_get(&chk[j], fset, k))
							aux[n++] = chk[i].mag - chk[j].mag;
						res = cmpack_fset_next(fset);
					}
					if (n>1)
						cmpack_robustmean(n, aux, &mean, &stddev);
					else
						stddev = -1.0;
					cmpack_tab_ptdd(table, col++, stddev);
				}
			}
		}
	}
	cmpack_free(aux);
	*ptable = table;
	return CMPACK_ERR_OK;
}
