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

attach.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 <math.h>

#include "comfun.h"
#include "trajd.h"
#include "console.h"
#include "image.h"
#include "cmpack_common.h"
#include "phot.h"
#include "ccdfile.h"
#include "phfun.h"
#include "attach.h"

int Attach(CmpackPhot *kc, CmpackCcdFile *file)
/* Reads lc->image data from fits file into memory buffer */
{
	int i, j, jx, jy, nhalf, ncol, nrow, nx, ny, res, left, top, right, bottom, nbox, pixels, rowwidth, height, width;
	unsigned mask;
	CmpackCcdParams params;
	CmpackPhotFrame *frame = &kc->frame;
	double *d;		/* image */
	double *g;		/* subarray with Gaussian curve */
	double *h;		/* central heights of the best fitting Gaussian function for each pixel */
	char *skip;		/* which pixels will be skipped */
	double radius, wt, p, sigsq, sumg, sumgsq, jsq, rsq, denom, sgop, relerr, sgd, sd, sg, sgsq;
	double skymod, readns, phpadu, hmin, dat;

	cmpack_free(frame->filter);
	frame->filter = NULL;
	cmpack_free(frame->object);
	frame->object = NULL;
	frame->exptime = 0.0;
	frame->ccdtemp = INVALID_TEMP;
	frame->frsum = frame->fravg = 1;
	if (frame->image) {
		cmpack_image_destroy(frame->image);
		frame->image = NULL;
	}
	cmpack_free(frame->skip);
	frame->skip = NULL;

	ClearStarList(frame);

	memset(frame, 0, sizeof(CmpackPhotFrame));
	frame->ccdtemp = INVALID_TEMP;
	frame->frsum = frame->fravg = 1;
	frame->border = kc->border;
	for (i = 0; i < MAXAP; i++) {
		if (kc->ap[i] >= 1.0) {
			kc->frame.apid[kc->frame.naper] = i + 1;
			kc->frame.aper[kc->frame.naper] = kc->ap[i];
			kc->frame.naper++;
		}
	}
	frame->datalo = kc->datalo;
	frame->datahi = kc->datahi;

	/* Check parameters */
	if (frame->naper == 0) {
		printout(kc->con, 0, "There are no valid apertures defined");
		return CMPACK_ERR_INVALID_PAR;
	}

	/* Check source file */
	if (!file) {
		printout(kc->con, 0, "Invalid CCD frame context");
		return CMPACK_ERR_INVALID_PAR;
	}
	
    /* Read frame attributes */
	mask = CMPACK_CM_IMAGE | CMPACK_CM_JD | CMPACK_CM_FILTER | CMPACK_CM_OBJECT | 
		CMPACK_CM_EXPOSURE | CMPACK_CM_CCDTEMP | CMPACK_CM_SUBFRAMES;
	if (cmpack_ccd_get_params(file, mask, &params)!=0) {
		printout(kc->con, 0, "Failed to read image parameters from the source file.");
		return CMPACK_ERR_READ_ERROR;
	}
	frame->jd = params.jd;
	if (frame->jd<=0.0) {
		printout(kc->con, 0, "Invalid date and time of observation in the source file");
		return CMPACK_ERR_INVALID_DATE;
	}
	nx = params.image_width;
	ny = params.image_height;
	if (nx<=0 || nx>=65536 || ny<=0 || ny>=65536) {
		printout(kc->con, 0, "Invalid dimensions of the source image");
		return CMPACK_ERR_INVALID_SIZE;
	}
	if (params.filter)
		frame->filter = cmpack_strdup(params.filter);
	if (params.object.designation)
		frame->object = cmpack_strdup(params.object.designation);
	frame->exptime = params.exposure;
	frame->ccdtemp = params.ccdtemp;
	frame->fravg = params.subframes_avg;
	if (frame->fravg<=0)
		frame->fravg = 1;
	frame->frsum = params.subframes_sum;
	if (frame->frsum<=0)
		frame->frsum = 1;

	/* Compute JD for the center of exposure */
    frame->jd += 0.5*frame->exptime/86400.0;

	/* Read image data */
	res = cmpack_ccd_to_image(file, CMPACK_BITPIX_DOUBLE, &frame->image);
	if (res != 0)
		return res;

	height = cmpack_image_height(frame->image);
	width = cmpack_image_width(frame->image);

	top = frame->border.top, left = frame->border.left;
	bottom = cmpack_image_height(frame->image) - frame->border.bottom - 1;
	right = cmpack_image_width(frame->image) - frame->border.right - 1;
	if (left > right || top > bottom) {
		printout(kc->con, 0, "The borders are too large. Nothing is left from the source frame.");
		return CMPACK_ERR_INVALID_PAR;
	}

	skymod = frame->skymod;
	if (frame->fravg <= 0) {
		printout(kc->con, 0, "Number of frames averaged must be greater than zero");
		return CMPACK_ERR_INVALID_PAR;
	}
	if (frame->frsum <= 0) {
		printout(kc->con, 0, "Number of frames summed must be greater than zero");
		return CMPACK_ERR_INVALID_PAR;
	}

	/* Get the source image */
	d = (double*)cmpack_image_data(frame->image);
	rowwidth = cmpack_image_width(frame->image);
   	 
	/* SECTION 1 - PREPARE SUBARRAY WITH CIRCULAR GAUSSIAN FUNCTION */

	radius = fmax(2.001, 0.637*kc->fwhm);
	nhalf = (int)radius;		/* number of pixels between the central pixel (exclusive)
										and the edge of the box (inclusive) */
	nbox = 2 * nhalf + 1;			/* length of the side of the subarray */
	sigsq = (kc->fwhm / 2.35482)*(kc->fwhm / 2.35482);
	radius = radius * radius;
	sumg = 0.0;                  /* sum of the values of the Gaussian function */
	sumgsq = 0.0;                /* sum of the squares of the values of the Gaussian function */
	pixels = 0;                  /* number of pixels in the circular area */

	g = (double*)cmpack_malloc(nbox*nbox * sizeof(double));
	skip = (char *)cmpack_malloc(nbox*nbox * sizeof(char));
	for (j = 0; j < nbox; j++) {
		jsq = (j - nhalf)*(j - nhalf);
		for (i = 0; i < nbox; i++) {
			rsq = (i - nhalf)*(i - nhalf) + jsq;
			G(i, j) = exp(-0.5*rsq / sigsq);
			if (rsq <= radius) {
				SKIP(i, j) = 0;
				sumg += G(i, j);
				sumgsq += G(i, j)*G(i, j);
				pixels++;
			}
			else {
				SKIP(i, j) = 1;
			}
		}
	}
	denom = sumgsq - (sumg*sumg) / pixels;    /* denominator of the fraction defining h */
	sgop = sumg / pixels;                     /* contains [G]/n */

	relerr = sqrt(1.0 / denom);
	readns = kc->readns*kc->readns*((double)frame->frsum / frame->fravg);
	phpadu = kc->gain*frame->fravg;
	hmin = sqrt(readns + fmax(0.0, skymod) / phpadu);
	hmin = 0.01*((int)(100.0*kc->thresh*relerr*hmin + 0.5));
	readns = sqrt(readns);

	/* SECTION 2 - Compute central heights of the best fitting Gaussian function */

	ncol = right - left + 1, nrow = bottom - top + 1;
	h = (double*)cmpack_malloc(ncol*nrow * sizeof(double));
	for (jy = 0; jy < nrow; jy++) {
		for (jx = 0; jx < ncol; jx++) {
			sgd = 0.0;
			sd = 0.0;
			sgsq = sumgsq;
			sg = sumg;
			p = pixels;

			/* Compute the local brightness enhancement for the pixel [jx,jy] */
			for (j = 0; j < nbox; j++) {
				int iy = jy - nhalf + j;
				for (i = 0; i < nbox; i++) {
					int ix = jx - nhalf + i;
					if (!SKIP(i, j)) {
						wt = G(i, j);
						if (iy >= 0 && iy < nrow && ix >= 0 && ix < ncol) {
							dat = D(left + ix, top + iy);
							if (frame->datalo < dat && dat < frame->datahi) {
								sgd += wt * dat;
								sd += dat;
							}
							else {
								sgsq -= wt * wt;
								sg -= wt;
								p--;
							}
						}
						else {
							sgsq -= wt * wt;
							sg -= wt;
							p--;
						}
					}
				}
			}

			if (p > 1) {
				/* Compute central height of the best fitting Gaussian function */
				if (p < pixels) {
					sgsq = sgsq - (sg*sg) / p;
					if (sgsq != 0.0)
						sgd = (sgd - sg * sd / p) / sgsq;
					else
						sgd = 0.0;
				}
				else {
					sgd = (sgd - sgop * sd) / denom;
				}
			}
			else {
				sgd = 0.0;
			}
			H(jx, jy) = sgd;   /* central height of the best fitting Gaussian function */
		}
	}
	SKIP(nhalf, nhalf) = 1;

	frame->left = left;
	frame->ncol = ncol;
	frame->top = top;
	frame->nrow = nrow;
	frame->nhalf = nhalf;
	frame->relerr = relerr;
	frame->phpadu = phpadu;
	frame->noise = readns;
	frame->hmin = hmin;
	frame->sigsq = sigsq;
	frame->g = g;
	frame->h = h;
	frame->skip = skip;

	return CMPACK_ERR_OK;
}
