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

kombine.c (C-Munipack project)
Combine frames
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 <float.h>
#include <math.h>

#include "config.h"
#include "comfun.h"
#include "console.h"
#include "trajd.h"
#include "ccdfile.h"
#include "image.h"
#include "cmpack_common.h"
#include "cmpack_kombine.h"

/*******************   DATA TYPES   ***************************/

/* Kombine output file context */
struct _CmpackKombine
{
	int refcnt;					/**< Reference counter */
	CmpackConsole *con;			/**< Console */
	CmpackCcdFile *outfile;		/**< Output file context */
	CmpackBitpix bitpix;   		/**< Required output image data type */
	CmpackBorder border;		/**< Border size */
	CmpackBitpix in_bitpix;   	/**< Output data format */
	double minvalue, maxvalue;	/**< Bad pixel value, overexposed value */
	int in_width;             	/**< Width of output frame */
	int in_height;             	/**< Height of output frame */
	double sjd;					/**< Accumulated JD */
	double exptime;				/**< Accumulated exposure duration */
	int nframe;					/**< Number of source frames */
	double *data;				/**< Accumulation buffer */
	int datalen;				/**< Size of buffer in bytes */
	char *stat;					/**< Status bits for each pixel */
	CmpackImageHeader header;	/**< Header of first frame */
	CmpackKombineFlags flags;	/**< Processing flags */
};

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

#define I(x,y)	ccd[(x)+nx*(y)]
#define D(x,y)	lc->data[(x)+nx*(y)]
#define S(x,y)	lc->stat[(x)+nx*(y)]

/* Clean frame context */
static void kombine_clear(CmpackKombine *lc)
{
	cmpack_image_header_destroy(&lc->header);
	if (lc->data) {
		cmpack_free(lc->data);
		lc->data = NULL;
	}
	lc->datalen = 0;
	if (lc->stat) {
		cmpack_free(lc->stat);
		lc->stat = NULL;
	}
	if (lc->outfile) {
		cmpack_ccd_destroy(lc->outfile);
		lc->outfile = NULL;
	}
	if (lc->con) {
		cmpack_con_destroy(lc->con);
		lc->con = NULL;
	}
}

static int read_align(CmpackKombine *lc, CmpackCcdFile *ccdfile, CmpackPhtFile *phtfile);
static int read_noalign(CmpackKombine *lc, CmpackCcdFile *ccdfile);

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

/* Reads first image and opens destination file */
CmpackKombine *cmpack_kombine_init(void)
{
	CmpackKombine *f = (CmpackKombine*)cmpack_calloc(1, sizeof(CmpackKombine));
	f->refcnt = 1;
	f->minvalue = 0;
	f->maxvalue = 65535.0;
	return f;
}

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

/* Decrement reference counter / detroy the instance */
void cmpack_kombine_destroy(CmpackKombine *ctx)
{
	if (ctx) {
		ctx->refcnt--;
		if (ctx->refcnt==0) {
			kombine_clear(ctx);
			cmpack_free(ctx);
		}
	}
}

/* Attach console */
void cmpack_kombine_set_console(CmpackKombine *lc, CmpackConsole *con)
{
	if (con!= lc->con) {
		if (lc->con)
			cmpack_con_destroy(lc->con);
		lc->con = con;
		if (lc->con)
			cmpack_con_reference(lc->con);
	}
}

/* Set processing flags */
CmpackKombineFlags cmpack_kombine_set_flags(CmpackKombine *lc, CmpackKombineFlags flags, int state)
{
	if (state)
		lc->flags |= flags;
	else
		lc->flags &= ~flags;
	return lc->flags;
}

/* Set output image data format */
void cmpack_kombine_set_bitpix(CmpackKombine *lc, CmpackBitpix bitpix)
{
	lc->bitpix = bitpix;
}

/* Set output image data format */
CmpackBitpix cmpack_kombine_get_bitpix(CmpackKombine *lc)
{
	return lc->bitpix;
}

/* Set image border */
void cmpack_kombine_set_border(CmpackKombine *lc, const CmpackBorder *border)
{
	if (border)
		lc->border = *border;
	else
		memset(&lc->border, 0, sizeof(CmpackBorder));
}

/* Set image border */
void cmpack_kombine_get_border(CmpackKombine *lc, CmpackBorder *border)
{
	*border = lc->border = *border;
}

/* Set image border */
void cmpack_kombine_set_thresholds(CmpackKombine *lc, double minvalue, double maxvalue)
{
	lc->minvalue = minvalue;
	lc->maxvalue = maxvalue;
}

/* Set minimum pixel value */
void cmpack_kombine_set_minvalue(CmpackKombine *lc, double minvalue)
{
	lc->minvalue = minvalue;
}

/* Set minimum pixel value */
double cmpack_kombine_get_minvalue(CmpackKombine *lc)
{
	return lc->minvalue;
}

/* Set maximum pixel value */
void cmpack_kombine_set_maxvalue(CmpackKombine *lc, double maxvalue)
{
	lc->maxvalue = maxvalue;
}

/* Set minimum pixel value */
double cmpack_kombine_get_maxvalue(CmpackKombine *lc)
{
	return lc->maxvalue;
}

/* Reads first image and opens destination file */
int cmpack_kombine_open(CmpackKombine *lc, CmpackCcdFile *outfile)
{
	/* Print configuration parameters */
	if (is_debug(lc->con)) {
		printout(lc->con, 1, "Kombine parameters:");
		printpars(lc->con, "Image data format", 1, pixformat(lc->bitpix));
		printparvi(lc->con, "Border", 1, 4, (int*)(&lc->border));
		printpard(lc->con, "Bad pixel threshold", 1, lc->minvalue, 2); 
		printpard(lc->con, "Overexp. pixel threshold", 1, lc->maxvalue, 2); 
	}
	/* Clear accumulators */
	lc->outfile = cmpack_ccd_reference(outfile);
	cmpack_image_header_destroy(&lc->header);
	lc->in_bitpix = CMPACK_BITPIX_UNKNOWN;
	lc->in_width = lc->in_height = 0;
	lc->sjd = lc->exptime = 0.0;
	lc->nframe = 0;
    return 0;
}

/* Reads first image and opens destination file */
int cmpack_kombine_read(CmpackKombine *lc, CmpackCcdFile *ccdfile, CmpackPhtFile *phtfile)
{
	if (!(lc->flags & CMPACK_KOMBINE_NOALIGN))
		return read_align(lc, ccdfile, phtfile);
	else
		return read_noalign(lc, ccdfile);
}

int read_align(CmpackKombine *lc, CmpackCcdFile *ccdfile, CmpackPhtFile *phtfile)
{
	int			res, nx, ny, i, j, ii, jj, minx, miny, maxx, maxy;
    double		*ccd, jd, exptime, minvalue, maxvalue, sx, sy, fx, fy;
	double		x00, x10, x01, x11;
    char		msg[MAXLINE];
	unsigned	mask;
	CmpackCcdParams params;
	CmpackMatrix m;
	CmpackPhtInfo pht;
	CmpackBitpix bitpix;
	CmpackImage *image;

    /* Check source files */
    if (!ccdfile) {
        printout(lc->con, 0, "Invalid image frame context");
 	    return CMPACK_ERR_INVALID_PAR;
    }
    if (!phtfile) {
        printout(lc->con, 0, "Invalid photometry file context");
 	    return CMPACK_ERR_INVALID_PAR;
    }

	/* Read frame offset */
	cmpack_pht_get_info(phtfile, CMPACK_PI_MATCH_PARAMS | CMPACK_PI_TRAFO, &pht);
	if (!pht.matched) {
	    sprintf(msg, "Missing frame offset information in the photometry file");
	    printout(lc->con, 0, msg);
        return CMPACK_ERR_READ_ERROR;
	}

    /* Read parameters */
	mask = CMPACK_CM_IMAGE | CMPACK_CM_EXPOSURE | CMPACK_CM_DATETIME | CMPACK_CM_JD;
	if (cmpack_ccd_get_params(ccdfile, mask, &params)!=0) {
		printout(lc->con, 0, "Failed to read image parameters from the file.");
		return CMPACK_ERR_READ_ERROR;
	}
	nx = params.image_width;
	ny = params.image_height;
	if (nx<=0 || ny<=0 || nx>=65536 || ny>=65536) {
		printout(lc->con, 0, "Invalid size of the source image");
	    return CMPACK_ERR_INVALID_SIZE;
	}
	bitpix = params.image_format;
	if (bitpix==0) {
		printout(lc->con, 0, "Invalid data format of the source image");
		return CMPACK_ERR_INVALID_BITPIX;
	}
	jd = params.jd;
	if (jd<=0.0) {
		printout(lc->con, 0, "Invalid Julian date of observation in the source image");
		return CMPACK_ERR_INVALID_DATE;
	}
	exptime = params.exposure;
	if (exptime < 0) {
		printout(lc->con, 0, "Invalid exposure duration in the source image");
		return CMPACK_ERR_INVALID_EXPTIME;
	}

	if (is_debug(lc->con)) {
		CmpackDateTime dt = params.date_time;
		printpars(lc->con, "Image data format", 1, pixformat(bitpix));
  		sprintf(msg, "%d x %d pixels", nx, ny); 
  		printpars(lc->con, "Image size", 1, msg);
		sprintf(msg, "%04d-%02d-%02d %02d:%02d:%02d.%03d UT", dt.date.year, dt.date.month, 
			dt.date.day, dt.time.hour, dt.time.minute, dt.time.second, dt.time.milisecond);
  		printpars(lc->con, "Date & time", 1, msg);
		printpard(lc->con, "Exposure", exptime >= 0, exptime, 2);
	}

	/* If transformation matrix is invalid, use offsets */
	m = pht.trafo;
	if (m.xx==0 && m.xy==0 && m.yx==0 && m.yy==0) {
		cmpack_matrix_init(&m, 1, 0, 0, 1, pht.offset[0], pht.offset[1]);
	}
	
	/* Convert jd to center of exposure */
	if (exptime > 0)
		jd += (0.5 * exptime / 86400.0);

    if (lc->in_width==0 && lc->in_height==0) {
	    /* first image, sets the variables */
	    lc->in_width = nx;
	    lc->in_height = ny;
		lc->sjd = jd;
		lc->exptime = exptime;
		lc->in_bitpix = bitpix;
		cmpack_image_header_init(&lc->header);
		ccd_save_header(ccdfile, &lc->header, lc->con);
	}
	else {
		/* Next image */
		if (nx != lc->in_width || ny != lc->in_height) {
			printout(lc->con, 0, "The size of the source image is different from the previous images");
			return CMPACK_ERR_DIFF_SIZE_SRC;
		}
		if (bitpix != lc->in_bitpix) {
			printout(lc->con, 0, "The image format of the source image is different from the previous images");
			return CMPACK_ERR_DIFF_BITPIX_SRC;
		}
		lc->sjd += jd;
		if (exptime > 0)
			lc->exptime += exptime;
	}

    /* Read image data */
	res = cmpack_ccd_to_image(ccdfile, CMPACK_BITPIX_DOUBLE, &image);
	if (res!=0) {
		return res;
	}
	ccd = (double*)cmpack_image_data(image);

	minvalue = lc->minvalue;
	maxvalue = lc->maxvalue;
	minx = lc->border.left;
	maxx = nx - lc->border.right - 1;
	miny = lc->border.top;
	maxy = ny - lc->border.bottom - 1;
	if (!lc->data && !lc->stat) {
	    /* first image, init accumulation buffer */
		lc->datalen = nx*ny*sizeof(double);
		lc->data = cmpack_malloc(lc->datalen);
	 	lc->stat = cmpack_malloc(nx*ny*sizeof(char));
	  	for (j=0;j<ny;j++) {
	      	for (i=0;i<nx;i++) {
				sx = i*m.xx + j*m.xy + m.x0;
				sy = i*m.yx + j*m.yy + m.y0;
		        if (sx<minx || sy<miny || sx>maxx || sy>maxy) {
		      		S(i,j) = -2;									/* Outside of source image */
	      	    } else {
					ii = (int)floor(sx);
					jj = (int)floor(sy);
					if (ii==maxx && jj==maxy) {
						x00 = x01 = x10 = x11 = I(ii, jj);		/* Bottom left pixel */
					} else if (ii==maxx) {
						x10 = x00 = I(ii, jj);					/* Last column */
						x01 = x11 = I(ii, jj+1);
					} else if (jj==maxy) {
						x01 = x00 = I(ii, jj);					/* Last row */
						x10 = x11 = I(ii+1, jj);
					} else {
						x00 = I(ii, jj);	x01 = I(ii, jj+1);
						x10 = I(ii+1, jj);	x11 = I(ii+1, jj+1);
					}
					if (x00>=maxvalue || x10>=maxvalue || x01>=maxvalue || x11>=maxvalue) {
	              		S(i,j) = 1;								/* Overexposed pixel */
	           		} else 
	           		if (x00<=minvalue || x10<=minvalue || x01<=minvalue || x11<=minvalue) {
		            	S(i,j) = -1;								/* Bad pixel */
	            	} else {
		            	S(i,j) = 0;
						fx = sx - ii;
						fy = sy - jj;
		            	D(i,j) = x00*(1-fx)*(1-fy) + x10*fx*(1-fy) + x01*(1-fx)*fy + x11*fx*fy;
	            	}
	  	      	}
	      	}
      	}
  	} else {
	 	/* Add to the accumulation buffer */
	  	for (j=0;j<ny;j++) {
	      	for (i=0;i<nx;i++) {
		        if (S(i,j)==0) {
					sx = i*m.xx + j*m.xy + m.x0;
					sy = i*m.yx + j*m.yy + m.y0;
					if (sx<minx || sy<miny || sx>maxx || sy>maxy) {
		      			S(i,j) = -2;								/* Outside of source image */
	      			} else {
						ii = (int)floor(sx);
						jj = (int)floor(sy);
						if (ii==maxx && jj==maxy) {				
							x00 = x01 = x10 = x11 = I(ii, jj);	/* Bottom left pixel */
						} else if (ii==maxx) {
							x10 = x00 = I(ii, jj);				/* Last column */
							x01 = x11 = I(ii, jj+1);
						} else if (jj==maxy) {					/* Last row */
							x01 = x00 = I(ii, jj);
							x10 = x11 = I(ii+1, jj);
						} else {
							x00 = I(ii, jj);	x01 = I(ii, jj+1);
							x10 = I(ii+1, jj);	x11 = I(ii+1, jj+1);
						}
						if (x00>=maxvalue || x10>=maxvalue || x01>=maxvalue || x11>=maxvalue) {
	              			S(i,j) = 1;							/* Overexposed pixel */
	           			} else 
	           			if (x00<=minvalue || x10<=minvalue || x01<=minvalue || x11<=minvalue) {
		            		S(i,j) = -1;							/* Bad pixel */
	            		} else {
							fx = sx - ii;
							fy = sy - jj;
			            	D(i,j) += x00*(1-fx)*(1-fy) + x10*fx*(1-fy) + x01*(1-fx)*fy + x11*fx*fy;
						}
					}
	  	      	}
	      	}
	  	}
  	}
  	cmpack_image_destroy(image);
	lc->nframe++;
    return 0;
}

int read_noalign(CmpackKombine *lc, CmpackCcdFile *ccdfile)
{
	int			res, nx, ny, i, j, minx, miny, maxx, maxy;
	double		*ccd, jd, exptime, minvalue, maxvalue;
	char		msg[MAXLINE];
	unsigned	mask;
	CmpackCcdParams params;
	CmpackBitpix bitpix;
	CmpackImage *image;

	/* Check source files */
	if (!ccdfile) {
		printout(lc->con, 0, "Invalid image frame context");
		return CMPACK_ERR_INVALID_PAR;
	}

	/* Read parameters */
	mask = CMPACK_CM_IMAGE | CMPACK_CM_EXPOSURE | CMPACK_CM_DATETIME | CMPACK_CM_JD;
	if (cmpack_ccd_get_params(ccdfile, mask, &params) != 0) {
		printout(lc->con, 0, "Failed to read image parameters from the file.");
		return CMPACK_ERR_READ_ERROR;
	}
	nx = params.image_width;
	ny = params.image_height;
	if (nx <= 0 || ny <= 0 || nx >= 65536 || ny >= 65536) {
		printout(lc->con, 0, "Invalid size of the source image");
		return CMPACK_ERR_INVALID_SIZE;
	}
	bitpix = params.image_format;
	if (bitpix == 0) {
		printout(lc->con, 0, "Invalid data format of the source image");
		return CMPACK_ERR_INVALID_BITPIX;
	}
	jd = params.jd;
	if (jd <= 0.0) {
		printout(lc->con, 0, "Invalid Julian date of observation in the source image");
		return CMPACK_ERR_INVALID_DATE;
	}
	exptime = params.exposure;
	if (exptime < 0) {
		printout(lc->con, 0, "Invalid exposure duration in the source image");
		return CMPACK_ERR_INVALID_EXPTIME;
	}

	if (is_debug(lc->con)) {
		CmpackDateTime dt = params.date_time;
		printpars(lc->con, "Image data format", 1, pixformat(bitpix));
		sprintf(msg, "%d x %d pixels", nx, ny);
		printpars(lc->con, "Image size", 1, msg);
		sprintf(msg, "%04d-%02d-%02d %02d:%02d:%02d.%03d UT", dt.date.year, dt.date.month,
			dt.date.day, dt.time.hour, dt.time.minute, dt.time.second, dt.time.milisecond);
		printpars(lc->con, "Date & time", 1, msg);
		printpard(lc->con, "Exposure", exptime >= 0, exptime, 2);
	}

	/* Convert jd to center of exposure */
	if (exptime > 0)
		jd += (0.5 * exptime / 86400.0);

	if (lc->in_width == 0 && lc->in_height == 0) {
		/* first image, sets the variables */
		lc->in_width = nx;
		lc->in_height = ny;
		lc->sjd = jd;
		lc->exptime = exptime;
		lc->in_bitpix = bitpix;
		cmpack_image_header_init(&lc->header);
		ccd_save_header(ccdfile, &lc->header, lc->con);
	}
	else {
		/* Next image */
		if (nx != lc->in_width || ny != lc->in_height) {
			printout(lc->con, 0, "The size of the source image is different from the previous images");
			return CMPACK_ERR_DIFF_SIZE_SRC;
		}
		if (bitpix != lc->in_bitpix) {
			printout(lc->con, 0, "The image format of the source image is different from the previous images");
			return CMPACK_ERR_DIFF_BITPIX_SRC;
		}
		lc->sjd += jd;
		if (exptime > 0)
			lc->exptime += exptime;
	}

	/* Read image data */
	res = cmpack_ccd_to_image(ccdfile, CMPACK_BITPIX_DOUBLE, &image);
	if (res != 0) {
		return res;
	}
	ccd = (double*)cmpack_image_data(image);

	minvalue = lc->minvalue;
	maxvalue = lc->maxvalue;
	minx = lc->border.left;
	maxx = nx - lc->border.right - 1;
	miny = lc->border.top;
	maxy = ny - lc->border.bottom - 1;
	if (!lc->data && !lc->stat) {
		/* first image, init accumulation buffer */
		lc->datalen = nx * ny * sizeof(double);
		lc->data = cmpack_malloc(lc->datalen);
		lc->stat = cmpack_malloc(nx*ny * sizeof(char));
		for (j = 0; j < ny; j++) {
			for (i = 0; i < nx; i++) {
				if (i<minx || j<miny || i>maxx || j>maxy) {
					S(i, j) = -2;									/* Outside of source image */
				}
				else {
					double x00 = I(i, j);							/* Bottom left pixel */
					if (x00 >= maxvalue) 
						S(i, j) = 1;								/* Overexposed pixel */
					else if (x00 <= minvalue) 
						S(i, j) = -1;								/* Bad pixel */
					else {
						S(i, j) = 0;
						D(i, j) = x00;
					}
				}
			}
		}
	}
	else {
		/* Add to the accumulation buffer */
		for (j = 0; j < ny; j++) {
			for (i = 0; i < nx; i++) {
				if (S(i, j) == 0) {
					if (i<minx || j<miny || i>maxx || j>maxy) {
						S(i, j) = -2;								/* Outside of source image */
					}
					else {
						double x00 = I(i, j);		/* Bottom left pixel */
						if (x00 >= maxvalue) 
							S(i, j) = 1;							/* Overexposed pixel */
						else if (x00 <= minvalue) 
							S(i, j) = -1;							/* Bad pixel */
						else 
							D(i, j) += x00;
					}
				}
			}
		}
	}
	cmpack_image_destroy(image);
	lc->nframe++;
	return 0;
}

/* Computes the output image and writes it to the file */
int cmpack_kombine_close(CmpackKombine *lc)
{
  	int res, i, j, nx, ny;
  	int underflow = 0, overflow = 0, overexposed = 0, badpixels = 0;
  	char msg[MAXLINE];
  	double val, minvalue, maxvalue;
	CmpackCcdParams params;
	CmpackImage *image;
	CmpackBitpix bitpix;

	/* Check context */
	if (!lc->outfile) {
		printout(lc->con, 0, "The output file is not opened");
		return CMPACK_ERR_NO_OUTPUT_FILE;
	}

  	/* Check data */
  	if (lc->nframe<=0 || !lc->data || !lc->stat) {
		cmpack_ccd_destroy(lc->outfile);
		lc->outfile = NULL;
		printout(lc->con, 0, "No source files defined");
	  	return CMPACK_ERR_NO_INPUT_FILES;
	}

	/* Check image size */
	nx = lc->in_width;
	ny = lc->in_height;
	if (nx<=0 || ny<=0) {
		cmpack_ccd_destroy(lc->outfile);
		lc->outfile = NULL;
		printout(lc->con, 0, "Invalid size of the destination image");
		return CMPACK_ERR_INVALID_SIZE;
	}

	if (lc->bitpix==CMPACK_BITPIX_AUTO) {
		/* The output frame have the same format as the first source frame */
		bitpix = lc->in_bitpix;
	} else {
		/* Use required image data format */
		bitpix = lc->bitpix;
	}
	if (bitpix==CMPACK_BITPIX_UNKNOWN) {
		/* Invalid image data format */
		cmpack_ccd_destroy(lc->outfile);
		lc->outfile = NULL;
		printout(lc->con, 0, "Invalid data format of the destination image");
		return CMPACK_ERR_INVALID_BITPIX;
	}

	/* Write header information */
	ccd_prepare(lc->outfile, nx, ny, bitpix);
	ccd_restore_header(lc->outfile, &lc->header, lc->con);
	memset(&params, 0, sizeof(CmpackCcdParams));
	params.exposure = lc->exptime;
	params.jd = lc->sjd/lc->nframe - (0.5*lc->exptime/86400.0);
	params.subframes_avg = lc->nframe;
	cmpack_ccd_set_params(lc->outfile, CMPACK_CM_JD | CMPACK_CM_EXPOSURE | 
		CMPACK_CM_SUBFRAMES, &params);
	ccd_set_origin(lc->outfile);
	ccd_set_pcdate(lc->outfile);
	
	if (is_debug(lc->con)) {
		CmpackDateTime dt;

		sprintf(msg, "Data format  : %d", bitpix); 
		printout(lc->con, 1, msg);
  		sprintf(msg,"No. of frames: %d frames" , lc->nframe); 
  		printout(lc->con, 1, msg);
		cmpack_decodejd(params.jd, &dt);
		sprintf(msg, "Date & time  : %04d-%02d-%02d %02d:%02d:%02d.%03d UT", 
			dt.date.year, dt.date.month, dt.date.day, dt.time.hour, dt.time.minute, dt.time.second, dt.time.milisecond); 
  		printout(lc->con, 1, msg);
  		sprintf(msg,"Exposure     : %.2f s", lc->exptime); 
  		printout(lc->con, 1, msg);
	}

  	/* Computation output image data */
	minvalue = lc->minvalue;
	maxvalue = lc->maxvalue;
  	for (j=0;j<ny;j++) {
      	for (i=0;i<nx;i++) {
          	/* Compute the output value */
          	val = D(i,j)/lc->nframe;
          	if (S(i,j)==-2) {
	          	D(i,j) = 0.0;			/* Out of source image */
          	} else
          	if (S(i,j)==-1) {
	          	D(i,j) = minvalue;		/* Source image contain bad pixel */
	          	badpixels++;
          	} else
          	if (S(i,j)==1) {
	          	D(i,j) = maxvalue;		/* Source image contain overexposed pixel */
      			overexposed++;
          	} else
          	if (val < minvalue) {
	          	D(i,j) = minvalue;		/* Out of range in target image format */
	          	underflow++;
          	} else
          	if (val > maxvalue) {
	          	D(i,j) = maxvalue;		/* Out of range in target image format */
	          	overflow++;
          	} else {
	          	D(i,j) = val;
          	}
      	}
  	}

	/* Write image data */
	image = cmpack_image_from_data(nx, ny, CMPACK_BITPIX_DOUBLE, lc->data, lc->datalen);
	res = ccd_write_image(lc->outfile, image);
	cmpack_image_destroy(image);

	/* Range check */
	if (overflow>0) {
		sprintf(msg,"Warning: An overflow has been occurred on %d of %d pixels during computation (max.=%.12g).", 
			overflow, nx*ny, maxvalue);
		printout(lc->con, 0, msg);
	}
	if (underflow>0) {
		sprintf(msg,"Warning: An underflow has been occurred on %d of %d pixels during computation (min.=%.12g).", 
			underflow, nx*ny, minvalue);
		printout(lc->con, 0, msg);
	}

	/* Clear buffers */
	cmpack_image_header_destroy(&lc->header);
	cmpack_free(lc->data);
	lc->data = NULL;
	lc->datalen = 0;
	cmpack_free(lc->stat);
	lc->stat = NULL;
	cmpack_ccd_destroy(lc->outfile);
	lc->outfile = NULL;
	lc->nframe = 0;
	return res;
}
