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

list.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_lcurve.h"

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

/* List of selected stars */
typedef struct _StarList
{
	StarInfo *list;			/**< List of star identifiers */
	int	count;				/**< Number of items */
} StarList;

/* List process context */
struct _CmpackLCurve
{
	int refcnt;				/**< Reference counter */
	CmpackConsole *con;		/**< Console */
	int aperture;			/**< Aperture identifier */
	StarList var;			/**< Measurements of variable stars */
	StarList comp;			/**< Measurements of comparison stars */
	StarList chk;			/**< Measurements of check stars */
	StarList all;			/**< Measurements for all stars */
	int vc_column;			/**< First column with mag data */
};

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

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

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

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

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

static void star_get(StarInfo *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;
			info->err = data.mag_error;
		}
	}
}

/* Get list of stars */
static int stars_indexof(StarList *tab, int id)
{
	int i;
	for (i=0; i<tab->count; i++) {
		if (tab->list[i].id == id)
			return i;
	}
	return -1;
}

static void diffmag_header(CmpackLCurve *lc, CmpackTable *table)
{
	int k = 1, i, j;
	char buf[80];

	if (lc->var.count==1) {
		/* V-C */
		sprintf(buf, "V-C");
		lc->vc_column = cmpack_tab_add_column_dbl(table, buf, MAG_PRECISION, -99.0, 99.0, INVALID_MAG);
		sprintf(buf, "s%d", k++);
		cmpack_tab_add_column_dbl(table, buf, MAG_PRECISION, 0.0, 9.0, INVALID_MAGERR);
		/* V-K1, V-K2, ... */
		for (j=0; j<lc->chk.count; j++) {
			sprintf(buf, "V-K%d", j+1);
			cmpack_tab_add_column_dbl(table, buf, MAG_PRECISION, -99.0, 99.0, INVALID_MAG);
			sprintf(buf, "s%d", k++);
			cmpack_tab_add_column_dbl(table, buf, MAG_PRECISION, 0.0, 9.0, INVALID_MAGERR);
		}
	} else {
		/* V-C */
		for (i=0; i<lc->var.count; i++) {
			sprintf(buf, "V%d-C", i+1);
			j = cmpack_tab_add_column_dbl(table, buf, MAG_PRECISION, -99.0, 99.0, INVALID_MAG);
			if (i==0)
				lc->vc_column = j;
			sprintf(buf, "s%d", k++);
			cmpack_tab_add_column_dbl(table, buf, MAG_PRECISION, 0.0, 9.0, INVALID_MAGERR);
		}
		/* V1-K1, V1-K2 ... V2-K1, V2-K2, ... */
		for (i=0; i<lc->var.count; i++) {
			for (j=0; j<lc->chk.count; j++) {
				sprintf(buf, "V%d-K%d", i+1, j+1);
				cmpack_tab_add_column_dbl(table, buf, MAG_PRECISION, -99.0, 99.0, INVALID_MAG);
				sprintf(buf, "s%d", k++);
				cmpack_tab_add_column_dbl(table, buf, MAG_PRECISION, 0.0, 9.0, INVALID_MAGERR);
			}
		}
	}
	if (lc->comp.count==1) {
		/* C-K1, C-K2 ... */
		for (j=0; j<lc->chk.count; j++) {
			sprintf(buf, "C-K%d", j+1);
			cmpack_tab_add_column_dbl(table, buf, MAG_PRECISION, -99.0, 99.0, INVALID_MAG);
			sprintf(buf, "s%d", k++);
			cmpack_tab_add_column_dbl(table, buf, MAG_PRECISION, 0.0, 9.0, INVALID_MAGERR);
		}
	} else {
		/* C1-K1, C1-K2 ... C2-K1, C2-K2, ... */
		for (i=0; i<lc->comp.count; i++) {
			for (j=0; j<lc->chk.count; j++) {
				sprintf(buf, "C%d-K%d", i+1, j+1);
				cmpack_tab_add_column_dbl(table, buf, MAG_PRECISION, -99.0, 99.0, INVALID_MAG);
				sprintf(buf, "s%d", k++);
				cmpack_tab_add_column_dbl(table, buf, MAG_PRECISION, 0.0, 9.0, INVALID_MAGERR);
			}
		}
	}
	/* K1-K2, ... */
	for (i=0; i<lc->chk.count; i++) {
		for (j=i+1; j<lc->chk.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);
			sprintf(buf, "s%d", k++);
			cmpack_tab_add_column_dbl(table, buf, MAG_PRECISION, 0.0, 9.0, INVALID_MAGERR);
		}
	}

	/* V, C, K */
	sprintf(buf, "V");
	cmpack_tab_add_column_dbl(table, buf, MAG_PRECISION, -99.0, 99.0, INVALID_MAG);
	sprintf(buf, "s%d", k++);
	cmpack_tab_add_column_dbl(table, buf, MAG_PRECISION, 0.0, 9.0, INVALID_MAGERR);
	sprintf(buf, "C");
	cmpack_tab_add_column_dbl(table, buf, MAG_PRECISION, -99.0, 99.0, INVALID_MAG);
	sprintf(buf, "s%d", k++);
	cmpack_tab_add_column_dbl(table, buf, MAG_PRECISION, 0.0, 9.0, INVALID_MAGERR);
	if (lc->comp.count>1) {
		for (i=0; i<lc->comp.count; i++) {
			sprintf(buf, "C%d", i+1);
			cmpack_tab_add_column_dbl(table, buf, MAG_PRECISION, -99.0, 99.0, INVALID_MAG);
			sprintf(buf, "s%d", k++);
			cmpack_tab_add_column_dbl(table, buf, MAG_PRECISION, 0.0, 9.0, INVALID_MAGERR);
		}
	}
	if (lc->chk.count>0) {
		sprintf(buf, "K");
		cmpack_tab_add_column_dbl(table, buf, MAG_PRECISION, -99.0, 99.0, INVALID_MAG);
		sprintf(buf, "s%d", k++);
		cmpack_tab_add_column_dbl(table, buf, MAG_PRECISION, 0.0, 9.0, INVALID_MAGERR);
	}
}

static void diffmag_allstars_header(CmpackLCurve *lc, CmpackTable *table)
{
	int i, j;
	char buf[80];

	/* V<id>-C */
	for (i=0; i<lc->all.count; i++) {
		sprintf(buf, "ID%d-C", lc->all.list[i].id);
		j = cmpack_tab_add_column_dbl(table, buf, MAG_PRECISION, -99.0, 99.0, INVALID_MAG);
		if (i==0)
			lc->vc_column = j;
		sprintf(buf, "s%d", lc->all.list[i].id);
		cmpack_tab_add_column_dbl(table, buf, MAG_PRECISION, 0.0, 9.0, INVALID_MAGERR);
	}

	/* C */
	sprintf(buf, "C");
	cmpack_tab_add_column_dbl(table, buf, MAG_PRECISION, -99.0, 99.0, INVALID_MAG);
	sprintf(buf, "s");
	cmpack_tab_add_column_dbl(table, buf, MAG_PRECISION, 0.0, 9.0, INVALID_MAGERR);
}

/* Process one frame */
static void diffmag_data(CmpackLCurve *lc, CmpackTable *table, const char *filename)
{
	char	msg[MAXLINE];
	int		i, n, j, col = lc->vc_column, *var_ok, comp_ok;
	double	cerr, cmag;
	const StarInfo *var = lc->var.list, *comp = lc->comp.list, *chk = lc->chk.list;

	var_ok = (int*)cmpack_malloc(lc->var.count*sizeof(int));

	/* Check variable star */
	for (i=0; i<lc->var.count; i++) {
		var_ok[i] = var[i].valid;
		if (!var_ok[i]) {
			if (filename)
				sprintf(msg, "%s: The variable star(s) have undefined value of mag. or std. err.", filename);
			else
				strcpy(msg, "The variable star(s) have undefined value of mag. or std. err.");
			printout(lc->con, 0, msg);
		}
	}

	/* Comparison star */
	if (lc->comp.count==1) {
		if (comp[0].valid) {
			cmag = comp[0].mag;
			cerr = comp[0].err;
			comp_ok = 1;
		} else {
			cerr = cmag = 0.0;
			comp_ok = 0;
		}
	} else {
		cmag = cerr = 0.0; 
		n = 0;
		for (i=0; i<lc->comp.count; i++) {
			if (comp[i].valid) {
				cmag += pow(10.0, -0.4*comp[i].mag);
				cerr += comp[i].err;
				n++;
			}
		}
		if (n==lc->comp.count) {
			cmag = -2.5*log10(cmag/n);
			cerr = (cerr/n)/sqrt((double)n);
			comp_ok = 1;
		} else {
			cerr = cmag = 0.0;
			comp_ok = 0;
		}
	}
	if (!comp_ok) {
		if (filename)
			sprintf(msg, "%s: The comparison star(s) have undefined value of mag. or std. err.", filename);
		else
			strcpy(msg, "The comparison star(s) have undefined value of mag. or std. err.");
		printout(lc->con, 0, msg);
	}
	if (is_debug(lc->con)) {
		sprintf(msg, "Comparison star: %.3f +- %.3f mag", cmag, cerr);
		printout(lc->con, 1, msg);
	}

	/* V-C */
	for (i=0; i<lc->var.count; i++) {
		if (var_ok[i] && comp_ok) {
			cmpack_tab_ptdd(table, col, var[i].mag - cmag);
			cmpack_tab_ptdd(table, col+1, sqrt(var[i].err*var[i].err + cerr*cerr));
		}
		col+=2;
	}

	/* V-K1, V-K2, ... */
	for (i=0; i<lc->var.count; i++) {
		for (j=0; j<lc->chk.count; j++) {
			if (var_ok[i] && chk[j].valid) {
				cmpack_tab_ptdd(table, col, var[i].mag - chk[j].mag);
				cmpack_tab_ptdd(table, col+1, sqrt(var[i].err*var[i].err + chk[j].err*chk[j].err));
			}
			col+=2;
		}
	}

	/* C1-K1, C1-K2, ... */
	for (i=0; i<lc->comp.count; i++) {
		for (j=0; j<lc->chk.count; j++) {
			if (comp[i].valid && chk[j].valid) {
				cmpack_tab_ptdd(table, col, comp[i].mag - chk[j].mag);
				cmpack_tab_ptdd(table, col+1, sqrt(comp[i].err*comp[i].err + chk[j].err*chk[j].err));
			}
			col+=2;
		}
	}

	/* K1-K2, ... */
	for (i=0; i<lc->chk.count; i++) {
		for (j=i+1; j<lc->chk.count; j++) {
			if (chk[i].valid && chk[j].valid) {
				cmpack_tab_ptdd(table, col, chk[i].mag - chk[j].mag);
				cmpack_tab_ptdd(table, col+1, sqrt(chk[i].err*chk[i].err + chk[j].err*chk[j].err));
			}
			col+=2;
		}
	}

	/* V, C, K */
	if (var[0].valid) {
		cmpack_tab_ptdd(table, col, var[0].mag);
		cmpack_tab_ptdd(table, col+1, var[0].err);
	}
	col+=2;
	if (comp_ok) {
		cmpack_tab_ptdd(table, col, cmag);
		cmpack_tab_ptdd(table, col+1, cerr);
	}
	col+=2;
	if (lc->comp.count>1) {
		for (i=0; i<lc->comp.count; i++) {
			if (comp[i].valid) {
				cmpack_tab_ptdd(table, col, comp[i].mag);
				cmpack_tab_ptdd(table, col+1, comp[i].err);
			}
			col+=2;
		}
	}
	if (lc->chk.count>0) {
		if (chk[0].valid) {
			cmpack_tab_ptdd(table, col, chk[0].mag);
			cmpack_tab_ptdd(table, col+1, chk[0].err);
		}
		col+=2;
	}

	cmpack_free(var_ok);
}

/* Process one frame */
static void diffmag_allstars_data(CmpackLCurve *lc, CmpackTable *table, const char *filename)
{
	char	msg[MAXLINE];
	int		i, n, col = lc->vc_column, comp_ok;
	double	cerr, cmag;
	const StarInfo *all = lc->all.list, *comp = lc->comp.list;

	/* Comparison star */
	if (lc->comp.count==1) {
		if (comp[0].valid) {
			cmag = comp[0].mag;
			cerr = comp[0].err;
			comp_ok = 1;
		} else {
			cerr = cmag = 0.0;
			comp_ok = 0;
		}
	} else {
		cmag = cerr = 0.0; 
		n = 0;
		for (i=0; i<lc->comp.count; i++) {
			if (comp[i].valid) {
				cmag += pow(10.0, -0.4*comp[i].mag);
				cerr += comp[i].err;
				n++;
			}
		}
		if (n==lc->comp.count) {
			cmag = -2.5*log10(cmag/n);
			cerr = (cerr/n)/sqrt((double)n);
			comp_ok = 1;
		} else {
			cerr = cmag = 0.0;
			comp_ok = 0;
		}
	}
	if (!comp_ok) {
		if (filename)
			sprintf(msg, "%s: The comparison star(s) have undefined value of mag. or std. err.", filename);
		else
			strcpy(msg, "The comparison star(s) have undefined value of mag. or std. err.");
		printout(lc->con, 0, msg);
	} else {
		if (is_debug(lc->con)) {
			sprintf(msg, "Comparison star: %.3f +- %.3f mag", cmag, cerr);
			printout(lc->con, 1, msg);
		}
	}

	/* V<id>-C */
	for (i=0; i<lc->all.count; i++) {
		if (all[i].valid && comp_ok) {
			cmpack_tab_ptdd(table, col, all[i].mag - cmag);
			cmpack_tab_ptdd(table, col+1, sqrt(all[i].err*all[i].err + cerr*cerr));
		}
		col+=2;
	}

	/* C */
	if (comp_ok) {
		cmpack_tab_ptdd(table, col, cmag);
		cmpack_tab_ptdd(table, col+1, cerr);
	}
}

static void instmag_header(CmpackLCurve *lc, CmpackTable *table)
{
	int i, j, k = 1;
	char buf[80];
		
	if (lc->var.count==1) {
		/* V */
		strcpy(buf, "V");
		lc->vc_column = cmpack_tab_add_column_dbl(table, buf, MAG_PRECISION, -99.0, 99.0, INVALID_MAG);
		sprintf(buf, "s%d", k++);
		cmpack_tab_add_column_dbl(table, buf, MAG_PRECISION, 0.0, 9.0, INVALID_MAGERR);
	} else {
		/* V1, V2, ... */
		for (i=0; i<lc->var.count; i++) {
			sprintf(buf, "V%d", i+1);
			j = cmpack_tab_add_column_dbl(table, buf, MAG_PRECISION, -99.0, 99.0, INVALID_MAG);
			if (i==0)
				lc->vc_column = j;
			sprintf(buf, "s%d", k++);
			cmpack_tab_add_column_dbl(table, buf, MAG_PRECISION, 0.0, 9.0, INVALID_MAGERR);
		}
	}
	if (lc->comp.count==1) {
		/* C */
		sprintf(buf, "C");
		cmpack_tab_add_column_dbl(table, buf, MAG_PRECISION, -99.0, 99.0, INVALID_MAG);
		sprintf(buf, "s%d", k++);
		cmpack_tab_add_column_dbl(table, buf, MAG_PRECISION, 0.0, 9.0, INVALID_MAGERR);
	} else {
		/* C1, C2, ... */
		for (i=0; i<lc->comp.count; i++) {
			sprintf(buf, "C%d", i+1);
			cmpack_tab_add_column_dbl(table, buf, MAG_PRECISION, -99.0, 99.0, INVALID_MAG);
			sprintf(buf, "s%d", k++);
			cmpack_tab_add_column_dbl(table, buf, MAG_PRECISION, 0.0, 9.0, INVALID_MAGERR);
		}
	}
	/* K1-K2, ... */
	for (i=0; i<lc->chk.count; i++) {
		sprintf(buf, "K%d", i+1);
		cmpack_tab_add_column_dbl(table, buf, MAG_PRECISION, -99.0, 99.0, INVALID_MAG);
		sprintf(buf, "s%d", k++);
		cmpack_tab_add_column_dbl(table, buf, MAG_PRECISION, 0.0, 9.0, INVALID_MAGERR);
	}
}

static void instmag_allstars_header(CmpackLCurve *lc, CmpackTable *table)
{
	int i, j;
	char buf[80];

	/* V1, V2, ... */
	for (i=0; i<lc->all.count; i++) {
		sprintf(buf, "ID%d", lc->all.list[i].id);
		j = cmpack_tab_add_column_dbl(table, buf, MAG_PRECISION, -99.0, 99.0, INVALID_MAG);
		if (i==0)
			lc->vc_column = j;
		sprintf(buf, "s%d", lc->all.list[i].id);
		cmpack_tab_add_column_dbl(table, buf, MAG_PRECISION, 0.0, 9.0, INVALID_MAGERR);
	}
}

/* Process single frame */
static void instmag_data(CmpackLCurve *lc, CmpackTable *table, const char *filename)
{
	int i, col = lc->vc_column;
	const StarInfo *var = lc->var.list, *comp = lc->comp.list, *chk = lc->chk.list;

	/* Variables */
	for (i=0; i<lc->var.count; i++) {
		if (var[i].valid) {
			cmpack_tab_ptdd(table, col, var[i].mag);
			cmpack_tab_ptdd(table, col+1, var[i].err);
		}
		col+=2;
	}

	/* Comparison stars */
	for (i=0; i<lc->comp.count; i++) {
		if (comp[i].valid) {
			cmpack_tab_ptdd(table, col, comp[i].mag);
			cmpack_tab_ptdd(table, col+1, comp[i].err);
		}
		col+=2;
	}

	/* Check stars */
	for (i=0; i<lc->chk.count; i++) { 
		if (chk[i].valid) {
			cmpack_tab_ptdd(table, col, chk[i].mag);
			cmpack_tab_ptdd(table, col+1, chk[i].err);
		}
		col+=2;
	}
}

/* Process single frame */
static void instmag_allstars_data(CmpackLCurve *lc, CmpackTable *table, const char *filename)
{
	int i, col = lc->vc_column;
	const StarInfo *all = lc->all.list;

	/* Variables */
	for (i=0; i<lc->all.count; i++) {
		if (all[i].valid) {
			cmpack_tab_ptdd(table, col, all[i].mag);
			cmpack_tab_ptdd(table, col+1, all[i].err);
		}
		col+=2;
	}
}

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

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

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

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

/* Attach console */
void cmpack_lcurve_set_console(CmpackLCurve *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 aperture index */
void cmpack_lcurve_set_aperture(CmpackLCurve *proc, int aperture)
{
	proc->aperture = aperture;
}

/* Get aperture index */
int cmpack_lcurve_get_aperture(CmpackLCurve *proc)
{
	return proc->aperture;
}

/* Set list of variable stars */
void cmpack_lcurve_set_var(CmpackLCurve *lc, const int *items, int nitems)
{
	stars_set(&lc->var, items, nitems);
}

/* Set list of variable stars */
void cmpack_lcurve_get_var(CmpackLCurve *lc, int **items, int *nitems)
{
	stars_get(&lc->var, items, nitems);
}

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

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

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

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

/* Open output table, start reading sequence */
int cmpack_lcurve(CmpackLCurve *lc, CmpackFrameSet *fset, CmpackTable **ptable, 
	CmpackLCurveFlags flags)
{	
	int res, i, j, inst_mag, ap_index, frameids, jd_prec, exposure, fname;
	int airmass, altitude, helcorr, jdhel, hjdonly, allstars;
	int	id_column, jd_column, hjd_column, hc_column, a_column, x_column, exp_column, fname_column;
	char msg[MAXLINE];
	CmpackTable *table;
	CmpackFrameSetInfo info;
	CmpackPhtAperture aperture;
	CmpackFrameInfo frame;

	*ptable = NULL;
	lc->vc_column = 0;
	
	inst_mag = (flags & CMPACK_LCURVE_INSTMAG)!=0;
	frameids = (flags & CMPACK_LCURVE_FRAME_IDS)!=0;
	airmass  = (flags & CMPACK_LCURVE_AIRMASS)!=0;
	altitude = (flags & CMPACK_LCURVE_ALTITUDE)!=0;
	helcorr  = (flags & CMPACK_LCURVE_HELCORR)!=0;
	jdhel    = (flags & CMPACK_LCURVE_HJD)!=0;
	hjdonly  = (flags & CMPACK_LCURVE_HJDONLY)!=0;
	allstars = (flags & CMPACK_LCURVE_ALLSTARS)!=0;
	exposure = (flags & CMPACK_LCURVE_EXPOSURE)!=0;
	fname    = (flags & CMPACK_LCURVE_FILENAME)!=0;

	cmpack_fset_get_info(fset, CMPACK_FS_JD_PREC, &info);
	jd_prec = info.jd_prec;

	/* Check aperture */
	ap_index = cmpack_fset_find_aperture(fset, lc->aperture);
	if (ap_index<0) {
		printout(lc->con, 0, "Invalid aperture identifier");
		return CMPACK_ERR_INVALID_PAR;
	}
	aperture.id = -1;
	cmpack_fset_get_aperture(fset, ap_index, CMPACK_PA_ID, &aperture);

	/* Check stars */
	if (!inst_mag) {
		if (lc->var.count!=1 && !allstars) {
			printout(lc->con, 0, "Exactly one variable star must be selected");
			return CMPACK_ERR_INVALID_PAR;
		}
		if (lc->comp.count<1) {
			printout(lc->con, 0, "At least one comparison star must be selected");
			return CMPACK_ERR_INVALID_PAR;
		}
	} else {
		int nstar = lc->var.count + lc->comp.count + lc->chk.count;
		if (nstar<1 && !allstars) {
			printout(lc->con, 0, "At least one star must be selected");
			return CMPACK_ERR_INVALID_PAR;
		}
	}
	for (i=0; i<lc->var.count; i++)
		lc->var.list[i].index = cmpack_fset_find_object(fset, lc->var.list[i].id);
	for (i=0; i<lc->comp.count; i++)
		lc->comp.list[i].index = cmpack_fset_find_object(fset, lc->comp.list[i].id);
	for (i=0; i<lc->chk.count; i++)
		lc->chk.list[i].index = cmpack_fset_find_object(fset, lc->chk.list[i].id);
	
	cmpack_free(lc->all.list);
	lc->all.list = NULL;
	lc->all.count = 0;
	if (allstars) {
		/* All stars except the comparison stars */
		int j = 0, obj_count = cmpack_fset_object_count(fset);
		if (obj_count>0) {
			lc->all.list = (StarInfo*)cmpack_calloc(obj_count, sizeof(StarInfo));
			for (i=0; i<obj_count; i++) {
				CmpackCatObject info;
				cmpack_fset_get_object(fset, i, CMPACK_OM_ID, &info);
				if (inst_mag || stars_indexof(&lc->comp, info.id)<0) {
					lc->all.list[j].id = info.id;
					lc->all.list[j].index = i;
					j++;
				}
			}
			lc->all.count = j;
		}
	}
	
	/* Print parameters */
	if (is_debug(lc->con)) {
		sprintf(msg, "Aperture: %d", aperture.id);
		printout(lc->con, 1, msg);
	}

	/* Create table */
	table = cmpack_tab_init(inst_mag ? CMPACK_TABLE_LCURVE_INST : CMPACK_TABLE_LCURVE_DIFF);
	cmpack_tab_pkyi(table, "Aperture", aperture.id);
	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 = -1;
	if (frameids)
		id_column = cmpack_tab_add_column_int(table, "FRAME", 0, INT_MAX, -1);

	jd_column = hjd_column = -1;
	if (!jdhel || !hjdonly)
		jd_column = cmpack_tab_add_column_dbl(table, "JD", jd_prec, 1e6, 1e99, INVALID_JD);
	if (jdhel)
		hjd_column = cmpack_tab_add_column_dbl(table, "JDHEL", jd_prec, 1e6, 1e99, INVALID_JD);

	if (!inst_mag) {
		if (!allstars)
			diffmag_header(lc, table);
		else
			diffmag_allstars_header(lc, table);
	} else {
		if (!allstars) 
			instmag_header(lc, table);
		else
			instmag_allstars_header(lc, table);
	}

	exp_column = fname_column = -1;
	if (exposure)
		exp_column = cmpack_tab_add_column_dbl(table, "EXPOSURE", EXP_PRECISION, 0.0, 1e99, INVALID_EXPTIME);
	if (fname)
		fname_column = cmpack_tab_add_column_str(table, "FILENAME");

	hc_column = x_column = a_column = -1;
	if (helcorr)
		hc_column = cmpack_tab_add_column_dbl(table, "HELCOR", jd_prec, -1.0, 1.0, INVALID_HCORR);
	if (airmass)
		x_column = cmpack_tab_add_column_dbl(table, "AIRMASS", AMASS_PRECISION, 0.0, 1e99, INVALID_AMASS);
	if (altitude)
		a_column = cmpack_tab_add_column_dbl(table, "ALTITUDE", ALT_PRECISION, -90.0, 90.0, INVALID_ALT);

	/* Process frames */
	res = cmpack_fset_rewind(fset);
	while (res==0) {
		cmpack_tab_append(table);
		cmpack_fset_get_frame(fset, CMPACK_FI_ID | CMPACK_FI_JULDAT | CMPACK_FI_HELCOR | CMPACK_FI_AIRMASS_ALT | CMPACK_FI_FILENAME | CMPACK_FI_EXPTIME, &frame);
		/* Check frame header */
		if (frameids && frame.frame_id<0) {
			if (frame.filename) 
				sprintf(msg, "%s: Invalid frame identifier", frame.filename);
			else
				strcpy(msg, "Invalid frame identifier");
			printout(lc->con, 0, msg);
		}
		if (frame.juldat<=0) {
			if (frame.filename) 
				sprintf(msg, "%s: Invalid Julian date of observation", frame.filename);
			else
				strcpy(msg, "Invalid Julian date of observation");
			printout(lc->con, 0, msg);
		}
		if ((helcorr || jdhel) && !frame.valid_helcor) {
			if (frame.filename)
				sprintf(msg, "%s: Invalid value of heliocentric correction observation", frame.filename);
			else
				strcpy(msg, "Invalid value of heliocentric correction observation");
			printout(lc->con, 0, msg);
		}
		if (altitude && (frame.altitude<-90 || frame.altitude>90)) {
			if (frame.filename)
				sprintf(msg, "%s: Invalid value of altitude", frame.filename);
			else
				strcpy(msg, "Invalid value of altitude");
			printout(lc->con, 0, msg);
		}
		if (airmass && frame.airmass<1.0) {
			if (frame.filename)
				sprintf(msg, "%s: Invalid value of air mass coefficient", frame.filename);
			else
				strcpy(msg, "Invalid value of air mass coefficient");
			printout(lc->con, 0, msg);
		}
		cmpack_tab_ptdi(table, id_column, frame.frame_id);
		cmpack_tab_ptdd(table, jd_column, frame.juldat);
		cmpack_tab_ptdd(table, hc_column, (frame.valid_helcor ? frame.helcor : -99.9));
		cmpack_tab_ptdd(table, hjd_column, (frame.valid_helcor ? frame.juldat + frame.helcor : 0));
		cmpack_tab_ptdd(table, a_column, frame.altitude);
		cmpack_tab_ptdd(table, x_column, frame.airmass);
		cmpack_tab_ptdd(table, exp_column, frame.exptime);
		cmpack_tab_ptds(table, fname_column, frame.filename);
		/* Read magnitudes for selected stars */
		for (j=0; j<lc->var.count; j++) 
			star_get(&lc->var.list[j], fset, ap_index);
		for (j=0; j<lc->comp.count; j++)
			star_get(&lc->comp.list[j], fset, ap_index);
		for (j=0; j<lc->chk.count; j++)
			star_get(&lc->chk.list[j], fset, ap_index);
		for (j=0; j<lc->all.count; j++)
			star_get(&lc->all.list[j], fset, ap_index);
		/* Debug printout */
		if (is_debug(lc->con)) {
			if (frame.filename)
				sprintf(msg, "FRAME: %d (%s)", frame.frame_id, frame.filename);
			else
				sprintf(msg, "FRAME: %d", frame.frame_id);
			printout(lc->con, 1, msg);
			sprintf(msg, "JD: %.*f", jd_prec, frame.juldat); 
			printout(lc->con, 1, msg);
			for (i=0; i<lc->var.count; i++) {
				sprintf(msg, "Variable star #%d:", i+1); 
				printout(lc->con, 1, msg);
				if (lc->var.list[i].valid) {
					sprintf(msg, "\tMagnitude: %.3f +- %.3f mag", lc->var.list[i].mag, lc->var.list[i].err);
					printout(lc->con, 1, msg);
				}
			}
			for (i=0; i<lc->comp.count; i++) {
				sprintf(msg, "Comparison star #%d:", i+1); 
				printout(lc->con, 1, msg);
				if (lc->comp.list[i].valid) {
					sprintf(msg, "\tMagnitude: %.3f +- %.3f mag", lc->comp.list[i].mag, lc->comp.list[i].err);
					printout(lc->con, 1, msg);
				}
			}
			for (i=0; i<lc->chk.count; i++) {
				sprintf(msg, "Check star #%d:", i+1); 
				printout(lc->con, 1, msg);
				if (lc->chk.list[i].valid) {
					sprintf(msg, "\tMagnitude: %.3f +- %.3f mag", lc->chk.list[i].mag, lc->chk.list[i].err);
					printout(lc->con, 1, msg);
				}
			}
		}
		if (!inst_mag) {
			if (!allstars)
				diffmag_data(lc, table, frame.filename);
			else
				diffmag_allstars_data(lc, table, frame.filename);
		} else {
			if (!allstars)
				instmag_data(lc, table, frame.filename);
			else
				instmag_allstars_data(lc, table, frame.filename);
		}
		res = cmpack_fset_next(fset);
	}

	*ptable = table;
	return CMPACK_ERR_OK;
}
