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

phot.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 <stdlib.h>
#include <stdio.h>
#include <string.h>

#include "comfun.h"
#include "console.h"
#include "cmpack_common.h"
#include "cmpack_phot.h"
#include "phot.h"
#include "attach.h"
#include "find.h"
#include "sky.h"
#include "phwrite.h"
#include "fotometr.h"
#include "sort.h"
#include "phfun.h"
#include "center.h"

/* Max. no. of pixels used in sky level computation */
#define MAXSKY   10000           
	
/* Min. no. of pixels used in sky level computation */
#define MINSKY   20              

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

/* Context initialization */
CmpackPhot *cmpack_phot_init(void)
{
	CmpackPhot *lc = (CmpackPhot*)cmpack_calloc(1, sizeof(CmpackPhot));
	lc->refcnt = 1;
	lc->readns = 15.0;
	lc->gain   = 2.3;
	lc->datalo = 0.0;
	lc->datahi = 65535.0;
	lc->fwhm   = 2.5;
	lc->thresh = 5.0;
	lc->shrplo = 0.2;
	lc->shrphi = 1.0;
	lc->rndlo  = -1.0;
	lc->rndhi  = 1.0;
	lc->ap[0]  = 2.000;
	lc->ap[1]  = 2.558;
	lc->ap[2]  = 3.272;
	lc->ap[3]  = 4.186;
	lc->ap[4]  = 5.354;
	lc->ap[5]  = 6.849;
	lc->ap[6]  = 8.761;
	lc->ap[7]  = 11.206;
	lc->ap[8]  = 14.334;
	lc->ap[9]  = 18.335;
	lc->ap[10] = 23.453;
	lc->ap[11] = 30.000;
	lc->is     = 20.0;
	lc->os     = 30.0;
	lc->maxstar = 10000;

	memset(&lc->frame, 0, sizeof(CmpackPhotFrame));
	lc->frame.ccdtemp = INVALID_TEMP;
	lc->frame.frsum = lc->frame.fravg = 1;

	return lc;
}

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

/* Decrement reference counter / detroy the instance */
void cmpack_phot_destroy(CmpackPhot *ctx)
{
	if (ctx) {
		ctx->refcnt--;
		if (ctx->refcnt==0) {
			cmpack_free(ctx->usr_coords);
			ClearStarList(&ctx->frame);
			cmpack_free(ctx->frame.g);
			cmpack_free(ctx->frame.h);
			cmpack_free(ctx->frame.skip);
			cmpack_free(ctx->frame.filter);
			cmpack_free(ctx->frame.object);
			if (ctx->frame.image)
				cmpack_image_destroy(ctx->frame.image);
			if (ctx->con) 
				cmpack_con_destroy(ctx->con);
			cmpack_free(ctx);
		}
	}
}

/* Attach console */
void cmpack_phot_set_console(CmpackPhot *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 output image data format */
void cmpack_phot_set_rnoise(CmpackPhot *lc, double rnoise)
{
	lc->readns = rnoise;
	lc->frame.find_processed = 0;
}

/* Set output image data format */
double cmpack_phot_get_rnoise(CmpackPhot *lc)
{
	return lc->readns;
}

/* Set output image data format */
void cmpack_phot_set_adcgain(CmpackPhot *lc, double adcgain)
{
	lc->gain = adcgain;
	lc->frame.find_processed = 0;
}

/* Set output image data format */
double cmpack_phot_get_adcgain(CmpackPhot *lc)
{
	return lc->gain;
}

/* Set output image data format */
void cmpack_phot_set_fwhm(CmpackPhot *lc, double fwhm)
{
	lc->fwhm = fwhm;
	lc->frame.find_processed = 0;
}

/* Set output image data format */
double cmpack_phot_get_fwhm(CmpackPhot *lc)
{
	return lc->fwhm;
}

/* Set output image data format */
void cmpack_phot_set_thresh(CmpackPhot *lc, double thresh)
{
	lc->thresh = thresh;
	lc->frame.find_processed = 0;
}

/* Set output image data format */
double cmpack_phot_get_thresh(CmpackPhot *lc)
{
	return lc->thresh;
}

/* Set output image data format */
void cmpack_phot_set_minshrp(CmpackPhot *lc, double minshrp)
{
	lc->shrplo = minshrp;
	lc->frame.find_processed = 0;
}

/* Set output image data format */
double cmpack_phot_get_minshrp(CmpackPhot *lc)
{
	return lc->shrplo;
}

/* Set output image data format */
void cmpack_phot_set_maxshrp(CmpackPhot *lc, double maxshrp)
{
	lc->shrphi = maxshrp;
	lc->frame.find_processed = 0;
}

/* Set output image data format */
double cmpack_phot_get_maxshrp(CmpackPhot *lc)
{
	return lc->shrphi;
}

/* Set output image data format */
void cmpack_phot_set_minrnd(CmpackPhot *lc, double minrnd)
{
	lc->rndlo = minrnd;
	lc->frame.find_processed = 0;
}

/* Set output image data format */
double cmpack_phot_get_minrnd(CmpackPhot *lc)
{
	return lc->rndlo;
}

/* Set output image data format */
void cmpack_phot_set_maxrnd(CmpackPhot *lc, double maxrnd)
{
	lc->rndhi = maxrnd;
	lc->frame.find_processed = 0;
}

/* Set output image data format */
double cmpack_phot_get_maxrnd(CmpackPhot *lc)
{
	return lc->rndhi;
}

/* Set output image data format */
void cmpack_phot_set_minval(CmpackPhot *lc, double minval)
{
	lc->datalo = minval;
	lc->frame.find_processed = 0;
}

/* Set output image data format */
double cmpack_phot_get_minval(CmpackPhot *lc)
{
	return lc->datalo;
}

/* Set output image data format */
void cmpack_phot_set_maxval(CmpackPhot *lc, double maxval)
{
	lc->datahi = maxval;
	lc->frame.find_processed = 0;
}

/* Set output image data format */
double cmpack_phot_get_maxval(CmpackPhot *lc)
{
	return lc->datahi;
}

/* Set image border */
void cmpack_phot_set_border(CmpackPhot *lc, const CmpackBorder *border)
{
	if (border)
		lc->border = *border;
	else
		memset(&lc->border, 0, sizeof(CmpackBorder));
	lc->frame.find_processed = 0;
}

/* Set image border */
void cmpack_phot_get_border(CmpackPhot *lc, CmpackBorder *border)
{
	if (border)
		*border = lc->border;
}

/* Set output image data format */
void cmpack_phot_set_skyin(CmpackPhot *lc, double skyin)
{
	lc->is = skyin;
}

/* Set output image data format */
double cmpack_phot_get_skyin(CmpackPhot *lc)
{
	return lc->is;
}

/* Set output image data format */
void cmpack_phot_set_skyout(CmpackPhot *lc, double skyout)
{
	lc->os = skyout;
}

void cmpack_phot_set_limit(CmpackPhot *lc, int maxstar)
{
	lc->maxstar = maxstar;
	lc->frame.find_processed = 0;
}

/* Set output image data format */
double cmpack_phot_get_skyout(CmpackPhot *lc)
{
	return lc->os;
}

/* Set output image data format */
void cmpack_phot_set_aper(CmpackPhot *lc, const double *items, int count)
{
	int i;
	double x;

	memset(lc->ap, 0, MAXAP*sizeof(double));
	if (count>MAXAP)
		count = MAXAP;
	for (i=0; i<count; i++) {
		x = items[i];
		lc->ap[i] = ((x>=1 && x<65536) ? x : 0);
	}
}

/* Set output image data format */
void cmpack_phot_get_aper(CmpackPhot *lc, double **items, int *count)
{
	*items = (double*)cmpack_malloc(MAXAP*sizeof(double));
	memcpy(*items, lc->ap, MAXAP*sizeof(double));
	*count = MAXAP;
}

int cmpack_phot_read(CmpackPhot *lc, CmpackCcdFile *infile)
{
	char  msg[MAXLINE];
	int   res = 0;

	/* Print configuration parameters */
	if (is_debug(lc->con)) {
		printout(lc->con, 1, "Photometry context:");
		printpard(lc->con, "Readout noise", 1, lc->readns, 2);
		printpard(lc->con, "ADC gain", 1, lc->gain, 2);
		printpard(lc->con, "Min value", 1, lc->datalo, 2);
		printpard(lc->con, "Max value", 1, lc->datahi, 2);
		printpard(lc->con, "FWHM", 1, lc->fwhm, 2);
		printpard(lc->con, "Threshold", 1, lc->thresh, 2);
		printpard(lc->con, "Min sharpness", 1, lc->shrplo, 2);
		printpard(lc->con, "Max sharpness", 1, lc->shrphi, 2);
		printpard(lc->con, "Min roundness", 1, lc->rndlo, 2);
		printpard(lc->con, "Max roundness", 1, lc->rndhi, 2);
		printparvd(lc->con, "Apertures", 1, MAXAP, lc->ap, 4);
		printpard(lc->con, "Inner sky radius", 1, lc->is, 2);
		printpard(lc->con, "Outer sky radius", 1, lc->os, 2);
		printpari(lc->con, "Max. objects", 1, lc->maxstar);
		printparvi(lc->con, "Border", 1, 4, (int*)(&lc->border));
	}

	/* Attach the image */
	res = Attach(lc, infile);
	if (res == 0) {
		sprintf(msg, "JD (UT)      : %.7f", lc->frame.jd);
		printout(lc->con, 1, msg);
		if (lc->frame.fravg > 1) {
			sprintf(msg, "Frames avgd  : %d", lc->frame.fravg);
			printout(lc->con, 1, msg);
		}
		if (lc->frame.frsum > 1) {
			sprintf(msg, "Frames sumd  : %d", lc->frame.frsum);
			printout(lc->con, 1, msg);
		}
	}

	/* Sky */
	if (res == 0) {
		res = Sky(&lc->frame);
		if (res == 0) {
			sprintf(msg, "Sky value    : %.1f +- %.1f ADU", lc->frame.skymod, lc->frame.skysig);
			printout(lc->con, 1, msg);
		}
	}

	return res;
}

/* Make photometry of 'scifile' to 'outfile'. Returns Julian date and number of stars */
int cmpack_phot(CmpackPhot *lc, CmpackCcdFile *infile, CmpackPhtFile *outfile, int *nstars)
{
	char  msg[MAXLINE];
	int   res;

	if (nstars)
		*nstars = 0;
	if (outfile)
		cmpack_pht_clear(outfile);

	/* Read source frame */
	res = cmpack_phot_read(lc, infile);

	/* Find stars */
	if (res == 0) {
		if (!lc->useobjectlist)
			res = Find(lc);
		else
			res = FindMax(lc);
		if (res == 0) {
			sprintf(msg, "Rel. error   : %.2f", lc->frame.relerr);
			printout(lc->con, 1, msg);
			sprintf(msg, "Stars found  : %d", lc->frame.nstar);
			printout(lc->con, 1, msg);
		}
	}

	/* Aperture photometry */
	if (res == 0) {
		res = PhotSB(lc, &lc->frame, MINSKY, MAXSKY);
		if ((res == 0) && (lc->frame.maglim < 99.0) && (lc->frame.magsq < 9.0)) {
			sprintf(msg, "Mag. limit   : %.2f +- %.2f per star in aperture #1.", lc->frame.maglim, lc->frame.magsq);
			printout(lc->con, 1, msg);
			sprintf(msg, "Mean FWHM    : %.2f +- %.2f pixels.", lc->frame.fwhm_med, lc->frame.fwhm_err);
			printout(lc->con, 1, msg);
		}
	}

	/* Sort of file according to key(s) */
	if (res == 0) {
		res = Sorter(&lc->frame);
		if (res == 0 && lc->frame.nstar > lc->maxstar) {
			sprintf(msg, "Warning: Number of stars found exceeds the limit, truncating the file to %d stars.", lc->maxstar);
			printout(lc->con, 0, msg);
			lc->frame.nstar = lc->maxstar;
		}
	}

	/* Write output to photometry file */
	if (res == 0) {
		/* Check photometry file */
		if (!outfile) {
			printout(lc->con, 0, "Invalid photometry file context");
			res = CMPACK_ERR_INVALID_PAR;
		}
		else {
			/* Copy WCS data */
			CmpackWcs *wcs;
			if (cmpack_ccd_get_wcs(infile, &wcs) == 0)
				cmpack_pht_set_wcs(outfile, wcs);
			/* Save objects */
			res = SrtWrite(lc, &lc->frame, outfile);
		}
	}

	/* Pass out the number of stars found */
	if (res == 0 && nstars)
		*nstars = lc->frame.nstar;

	/* Free allocated memory */
	ClearStarList(&lc->frame);
	if (lc->frame.image) {
		cmpack_image_destroy(lc->frame.image);
		lc->frame.image = NULL;
	}
	cmpack_free(lc->frame.object);
	lc->frame.object = NULL;
	cmpack_free(lc->frame.filter);
	lc->frame.filter = NULL;
	cmpack_free(lc->frame.g);
	lc->frame.g = NULL;
	cmpack_free(lc->frame.h);
	lc->frame.h = NULL;
	cmpack_free(lc->frame.skip);
	lc->frame.skip = NULL;

	return res;
}

int cmpack_phot_pos(CmpackPhot *lc, double *x, double *y)
{
	return CenterPos(lc, x, y);
}

int cmpack_phot_find_first(CmpackPhot *lc)
{
	char  msg[MAXLINE];
	int res = CMPACK_ERR_OK;

	if (!lc->frame.find_processed) {
		res = Find(lc);
		if (res == 0) {
			sprintf(msg, "Rel. error   : %.2f", lc->frame.relerr);
			printout(lc->con, 1, msg);
			sprintf(msg, "Stars found  : %d", lc->frame.nstar);
			printout(lc->con, 1, msg);
		}
	}
	if (res == 0) {
		lc->frame.current_pos = 0;
		if (lc->frame.nstar == 0)
			res = CMPACK_ERR_OUT_OF_RANGE;
	}
	return res;
}

int cmpack_phot_find_next(CmpackPhot *lc)
{
	lc->frame.current_pos++;
	if (lc->frame.current_pos >= lc->frame.nstar)
		return CMPACK_ERR_OUT_OF_RANGE;
	return CMPACK_ERR_OK;
}

void cmpack_phot_find_close(CmpackPhot *lc)
{
	ClearStarList(&lc->frame);
}

int cmpack_phot_get_data(CmpackPhot *lc, unsigned flags, CmpackPhotObject *data)
{
	if (lc->frame.current_pos >= lc->frame.nstar)
		return CMPACK_ERR_OUT_OF_RANGE;

	const CmpackPhotStar *star = lc->frame.list[lc->frame.current_pos];
	if (flags & CMPACK_PHI_XY) {
		data->center_x = star->xcen;
		data->center_y = star->ycen;
		data->max_x = star->xmax;
		data->max_y = star->ymax;
	}
	return CMPACK_ERR_OK;
}

void cmpack_phot_set_use_object_list(CmpackPhot *ctx, int value)
{
	ctx->useobjectlist = (value != 0);
}

void cmpack_phot_set_object_list(CmpackPhot *ctx, int length, const CmpackPhotObject *list)
{
	int i, j;

	if (ctx->usr_alloc < length) {
		cmpack_free(ctx->usr_coords);
		ctx->usr_coords = (double*)cmpack_malloc(length * 2 * sizeof(double));
		ctx->usr_alloc = length;
	}
	for (i = 0, j = 0; i < length; i++, j += 2) {
		ctx->usr_coords[j] = list[i].center_x;
		ctx->usr_coords[j + 1] = list[i].center_y;
	}
	ctx->usr_length = length;
}
