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

fotometr.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 <math.h>
#include <float.h>

#include "comfun.h"
#include "console.h"
#include "robmean.h"
#include "cmpack_common.h"
#include "phot.h"
#include "phfun.h"
#include "fotometr.h"

/* Interpolation routine used for computing of FWHM value */
static double inter(double y1, double y, double y2)
{
	if (fabs(y2-y1)<1e-6) return 0.5;
	                 else return (y-y1) / (y2-y1);
}

int PhotSB(CmpackPhot *kc, CmpackPhotFrame *frame, int min_sky, int max_sky)
/* Aperture photometry */
{
	double *d, *sky, skymn, skysig, *fwhm, fwhm_med, fwhm_err;
	double apmax, apmxsq, rinsq, routsq, maglim, magsq, sumwt, skyvar, sigsq, halfmax;
	double apmag[MAXAP], magerr[MAXAP], area[MAXAP], xc, yc, datum, fractn;
	double err1, err2, err3, edge, dysq, rsq, dmag, r, wt, sum;
	int   skyerr_reported = 0, overexp_reported = 0, badpxl_reported = 0;
	int   left, top, right, bottom, rowwidth, i, j, k, s, nmag, lx, ly, mx, my, nsky, nfwhm, count;
	CmpackError apcode[MAXAP];

	/* SECTION 1 - Read parameter file, open input and output */

	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;
	}

    /* Compute the outermost edge of the largest aperture */
	apmax = 0;
	for (k=0;k<frame->naper;k++) 
		apmax = fmax(apmax, frame->aper[k]);
	apmxsq = (apmax+0.5)*(apmax+0.5);

	/* Now define the other variables whose values are in the table. */
    rinsq  = kc->is*kc->is;                    /* Inner sky radius squared */
    routsq = kc->os*kc->os;                    /* Outer sky radius squared */
    if (kc->is<=1) {
	    printout(kc->con, 0, "Inner sky radius must be greater than 1");
	    return CMPACK_ERR_INVALID_PAR;
    }
    if (kc->os<=1) {
	    printout(kc->con, 0, "Outer sky radius must be greater than 1");
	    return CMPACK_ERR_INVALID_PAR;
    }
    if (routsq > (1.0*max_sky/M_PI + rinsq)) {
	    printout(kc->con, 0, "You have specified too big sky annulus");
	    return CMPACK_ERR_INVALID_PAR;
    }
    if (routsq <= rinsq) {
	    printout(kc->con, 0, "Your outer sky radius is no bigger than the inner radius");
	    return CMPACK_ERR_INVALID_PAR;
    }
	sky  = (double*) cmpack_malloc(max_sky*sizeof(double));
	fwhm = (double*) cmpack_malloc(frame->nstar*sizeof(double));
		
	d    = (double*)cmpack_image_data(frame->image);
	rowwidth = cmpack_image_width(frame->image);

	/* SECTION 2 - Derive aperture photometry object by object. */
    maglim= 0.0;
    magsq = 0.0;
    sumwt = 0.0;
    nmag  = 0;
    nfwhm = 0;

	for (s=0; s<frame->nstar; s++) {
		CmpackPhotStar *star = frame->list[s];

   	    /* Get the coordinates of next object to be measured. */
	    xc = star->xcen - 0.5;
	    yc = star->ycen - 0.5;

	    /* Initalize magnitudes and errors to default values */
        for (k=0; k<frame->naper; k++) {
	        star->apmag[k] = star->magerr[k] = DBL_MAX;
			star->code[k] = CMPACK_ERR_OK;
        }
	    
        /* Compute the limits of the submatrix. */
		lx = (int)ceil(xc - fmax(apmax+0.5, kc->os));
		if (lx < left)
			lx = left;
		mx = (int)floor(xc + fmax(apmax+0.5, kc->os));
		if (mx > right)
			mx = right;
		ly = (int)ceil(yc - fmax(apmax+0.5, kc->os));
		if (ly < top)
			ly = top;
		my = (int)floor(yc + fmax(apmax+0.5, kc->os));
		if (my > bottom)
			my = bottom;

	    /* Initialize star counts and aperture area. */
        edge = fmin(fmin(xc-left, right-xc), fmin(yc-top, bottom-yc));
        for (k=0;k<frame->naper;k++) {
            apmag[k]  = 0.0;
            magerr[k] = 0.0;
            area[k]   = 0.0;
			if (frame->aper[k]<=0) 
				apcode[k] = CMPACK_ERR_INVALID_AP_SIZE;
			else if (edge < frame->aper[k] + 0.5)
				apcode[k] = CMPACK_ERR_CLOSE_TO_BORDER;
			else
				apcode[k] = CMPACK_ERR_OK;
		}

        /* Now read through the submatrix, picking out the data we want. */
        nsky = 0;
        for (j=ly;j<=my;j++) {
            dysq = (j - yc)*(j - yc);
            for (i=lx;i<=mx;i++) {
                rsq = dysq + (i - xc)*(i - xc);    /* squared distance from [i,j] to the center of the star */
                datum = D(i,j);
                if (!((rsq<rinsq)||(rsq>routsq)||(nsky>max_sky)||(datum<=frame->datalo)||(datum>=frame->datahi))) {
	                /* distance is between inner and outer sky radius */
                    sky[nsky] = datum;
                    nsky++;
                }
                if (rsq<=apmxsq) {
	                /* this pixel fits to largest aperture radius */
                    r = sqrt(rsq);          /* distance from the center */
                    for (k=0;k<frame->naper;k++) {
						if (!apcode[k]) {
							fractn = frame->aper[k] - r + 0.5;
							if (fractn>0) {
								if (datum >= frame->datahi) {
									/* bad datum value */
									if (!overexp_reported) {
		    							printout(kc->con, 1, "There are overexposed pixels in the aperture");
										overexp_reported = 1;
									}
									apcode[k] = CMPACK_ERR_OVEREXPOSED_PXLS;
								}
								if (datum <= frame->datalo) {
									/* bad datum value */
									if (!badpxl_reported && datum<=0) {
		    							printout(kc->con, 1, "There are bad pixels in the aperture");
										badpxl_reported = 1;
									}
									apcode[k] = CMPACK_ERR_BAD_PIXELS;
								}
								fractn = fmin(fractn, 1.0);
								apmag[k] += fractn*datum;   /* sum of pixel values that fits to the aperture k */
								area[k]  += fractn;         /* size of the area of the aperture k */
							}
						}
                    }
                }
            }
        }

	    if (nsky<min_sky) {
		    if (!skyerr_reported) {
				printout(kc->con, 0, "Warning: There aren't enough pixels in the sky annulus. Are you sure your bad pixel thresholds are all right? If so, then you need a larger outer sky radius.");
		    	skyerr_reported = 1;
	    	}
			skymn = skysig = skyvar = sigsq = 0;
			star->skymod = star->skysig = star->skyskw = 0;
			for (k=0;k<frame->naper;k++)
				apcode[k] = CMPACK_ERR_SKY_NOT_MEASURED;
	    } else {
			/*  Obtain the mode, standard deviation, and skewness of the peak in the sky histogram */
			cmpack_robustmean(nsky, sky, &skymn, &skysig);
			skyvar=skysig*skysig;    /* variance of the sky brightness */
			sigsq =skyvar/nsky;      /* square of the standard error of the mean sky brightness */
			star->skymod = skymn;
			star->skysig = skysig;
			star->skyskw = 0;
		}

        /* Subtract the sky from the integrated brightnesses in the apertures, convert the results */
        /* to magnitudes, and compute standard errors. Detect bad values. */
        for (k=0;k<frame->naper;k++) {
	        CmpackError code = apcode[k];
            if (!code) { 
	            apmag[k] = apmag[k] - skymn*area[k];
            	if (apmag[k]<=0) { 
					code = CMPACK_ERR_ZERO_INTENSITY;
				} else {
            		err1 = area[k]*skyvar;
            		err2 = apmag[k]/frame->phpadu;
            		err3 = sigsq*area[k]*area[k];
            		magerr[k] = fmin(9.999, 1.0857*sqrt(err1 + err2 + err3)/apmag[k]);
            		apmag[k] = 25.0 - 2.5*log10(apmag[k]);
            		if (apmag[k] < -99 || apmag[k] > 99) 
						code = CMPACK_ERR_OUT_OF_RANGE;
				}
    		}
    		if (!code) {
       			star->apmag[k] = apmag[k];
        		star->magerr[k] = magerr[k];
    		} else {
       			star->apmag[k] = DBL_MAX;
        		star->magerr[k] = DBL_MAX;
    		}	
			star->code[k] = code;
        }

        if ((star->apmag[0]<99.0)&&(star->magerr[0]<9.0)) {
	        /* Compute FWHM of the star */
	        halfmax = (star->height + star->skymod) / 2.0;
	        count = 0;
			sum = 0.0;
	        for (i=star->xmax, j=star->ymax; i>=left; i--) {
		        if (D(i,j)<halfmax && D(i+1,j)>=halfmax) {
			        sum += star->xcen - i - inter(D(i,j), halfmax, D(i+1,j));
					count++;
			        break;
		        }
	        }
	        for (i=star->xmax, j=star->ymax; i<=right; i++) {
		        if (D(i,j)<halfmax && D(i-1,j)>=halfmax) {
			        sum += i - star->xcen - inter(D(i,j), halfmax, D(i-1,j));
					count++;
			        break;
		        }
	        }
	        for (i=star->xmax, j=star->ymax; j>=top; j--) {
		        if (D(i,j)<halfmax && D(i,j+1)>=halfmax) {
			        sum += star->ycen - j - inter(D(i,j), halfmax, D(i,j+1));
					count++;
			        break;
		        }
	        }
	        for (i=star->xmax, j=star->ymax; j<=bottom; j++) {
		        if (D(i,j)<halfmax && D(i,j-1)>=halfmax) {
			        sum += j - star->ycen - inter(D(i,j), halfmax, D(i,j-1));
					count++;
			        break;
		        }
	        }
			if (count>=2 && sum>0) {
				star->fwhm = 2.0*sum/count;
				fwhm[nfwhm++] = star->fwhm;
			} else {
				star->fwhm = -1.0;
			}
	        /* Compute magnidute limit */
	        if (star->magerr[0]>0.00001) {
	        	dmag    = -2.5*log10(star->height/frame->hmin);
            	wt      = (2.0/(2.0 - dmag))*(100.0/star->magerr[0])*(100.0/star->magerr[0]);
            	maglim += wt*(star->apmag[0] - dmag);
            	magsq  += wt*(star->apmag[0] - dmag)*(star->apmag[0] - dmag);
            	sumwt  += wt;
            	nmag++;
        	}
        }
    }

    /* Compute magnitude limit */
    if (sumwt>0.0) {
        maglim = maglim/sumwt;
        magsq  = magsq/sumwt - maglim*maglim;
        magsq  = sqrt(fmax(0.0, magsq));
        frame->maglim = maglim;
        frame->magsq  = magsq;
    } else {
	    frame->maglim = DBL_MAX;
	    frame->magsq  = DBL_MAX;
    }
    /* Compute robust mean FWHM value */
    if (nfwhm>0) {
	    cmpack_robustmean(nfwhm, fwhm, &fwhm_med, &fwhm_err);
	    frame->fwhm_med = fwhm_med;
	    frame->fwhm_err = fwhm_err;
    } else {
	    frame->fwhm_med = 0.0;
	    frame->fwhm_err = 0.0;
    }

    /* Normal return */
    cmpack_free(sky);
    cmpack_free(fwhm);
    return 0;
}
