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

konve.c (C-Munipack project)
Conversion tool for CCD 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 <stdint.h>
#include <float.h>
#include <math.h>
#include <limits.h>

#include "comfun.h"
#include "console.h"
#include "ccdfile.h"
#include "cmpack_common.h"
#include "cmpack_ccdfile.h"
#include "cmpack_konve.h"
#include "cmpack_image.h"

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

/* Conversion context */
struct _CmpackKonv
{
	int refcnt;					/**< Reference cuonter */
	CmpackConsole *con;			/**< Console */
	CmpackBitpix bitpix;		/**< Output data format */
	int hflip, vflip;			/**< Image flipping and rotation flags */
	int hbin, vbin;				/**< Binning factor */
	CmpackBorder border;		/**< Border size */
	double minvalue, maxvalue;	/**< Bad pixel value, overexposed value */
	CmpackChannel channel;		/**< Color image conversion */
	double toffset;				/**< Time of observation offset in seconds */
};

/***********************   Public functions   ****************************/

/* Create and init the context */
CmpackKonv *cmpack_konv_init(void)
{
	CmpackKonv *f = (CmpackKonv*)cmpack_calloc(1, sizeof(CmpackKonv));
	f->refcnt = 1;
	f->hbin = f->vbin = 1;
	f->maxvalue = 65535.0;
	f->channel = CMPACK_CHANNEL_SUM;
	return f;
}

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

/* Decrement reference counter / detroy the instance */
void cmpack_konv_destroy(CmpackKonv *ctx)
{
	if (ctx) {
		ctx->refcnt--;
		if (ctx->refcnt==0) {
			if (ctx->con) {
				cmpack_con_destroy(ctx->con);
				ctx->con = NULL;
			}
			cmpack_free(ctx);
		}
	}
}

/* Attach console */
void cmpack_konv_set_console(CmpackKonv *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 image flip flags */
void cmpack_konv_set_transposition(CmpackKonv *lc, int hflip, int vflip)
{
	lc->hflip = hflip;
	lc->vflip = vflip;
}

/* Set image flip flags */
void cmpack_konv_set_toffset(CmpackKonv *lc, double seconds)
{
	lc->toffset = seconds;
}

/* Set image flip flags */
void cmpack_konv_set_bitpix(CmpackKonv *lc, CmpackBitpix bitpix)
{
	lc->bitpix = bitpix;
}

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

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

/* Set color channel conversion */
void cmpack_konv_set_channel(CmpackKonv *lc, CmpackChannel channel)
{
	lc->channel = channel;
}

/* Set binning factor */
void cmpack_konv_set_binning(CmpackKonv *lc, int hbin, int vbin)
{
	lc->hbin = (hbin>=1 ? hbin : 1);
	lc->vbin = (vbin>=1 ? vbin : 1);
}

/* Converts 'in' to 'out' */
int cmpack_konv(CmpackKonv *lc, CmpackCcdFile *infile, CmpackCcdFile *outfile)
{
	return cmpack_konv_with_wcs(lc, infile, NULL, outfile);
}

/* Converts 'in' + 'wcs' to 'out' */
int cmpack_konv_with_wcs(CmpackKonv *lc, CmpackCcdFile *infile, CmpackCcdFile *wcsfile, CmpackCcdFile *outfile)
{
	int res, width, height;
	CmpackBitpix out_bitpix;
	char msg[MAXLINE];
	CmpackImage *img;

  	/* Check parameters */
	if (!infile) {
		printout(lc->con, 0, "Invalid input frame context");
		return CMPACK_ERR_INVALID_PAR;
	}
	if (!outfile) {
		printout(lc->con, 0, "Invalid output frame context");
		return CMPACK_ERR_INVALID_PAR;
	}

	/* Print configuration parameters */
	if (is_debug(lc->con)) {
		printpars(lc->con, "Format", 1, pixformat(lc->bitpix));
		sprintf(msg, "%s%s", (lc->hflip ? "x" : ""), (lc->vflip ? "y" : ""));
		printpars(lc->con, "Flip", 1, msg);
		printparvi(lc->con, "Border", 1, 4, (int*)(&lc->border));
		sprintf(msg, "%d %d", lc->hbin, lc->vbin);
		printpars(lc->con, "Binning", 1, msg);
		printpard(lc->con, "Bad pixel threshold", 1, lc->minvalue, 2); 
		printpard(lc->con, "Overexp. pixel threshold", 1, lc->maxvalue, 2); 
		printpard(lc->con, "Time offset", 1, lc->toffset, 3);
	}

	/* Determine target image depth */
	if (lc->bitpix==CMPACK_BITPIX_AUTO) {
		out_bitpix = cmpack_ccd_bitpix(infile);
		if (out_bitpix==CMPACK_BITPIX_USHORT && lc->minvalue < 0) 
			out_bitpix = CMPACK_BITPIX_SLONG;
		if (out_bitpix==CMPACK_BITPIX_ULONG && lc->minvalue < 0) 
			out_bitpix = CMPACK_BITPIX_SLONG;
		if (out_bitpix==CMPACK_BITPIX_SSHORT && lc->minvalue < INT16_MIN) 
			out_bitpix = CMPACK_BITPIX_SLONG;
		if (out_bitpix==CMPACK_BITPIX_SLONG && lc->minvalue < INT32_MIN)
			out_bitpix = CMPACK_BITPIX_DOUBLE;
		if (out_bitpix==CMPACK_BITPIX_USHORT && lc->maxvalue > UINT16_MAX) 
			out_bitpix = CMPACK_BITPIX_SLONG;
		if (out_bitpix==CMPACK_BITPIX_SSHORT && lc->maxvalue > INT16_MAX) 
			out_bitpix = CMPACK_BITPIX_SLONG;
		if (out_bitpix==CMPACK_BITPIX_ULONG && lc->maxvalue > UINT32_MAX)
			out_bitpix = CMPACK_BITPIX_DOUBLE;
		if (out_bitpix==CMPACK_BITPIX_SLONG && lc->maxvalue > INT32_MAX)
			out_bitpix = CMPACK_BITPIX_DOUBLE;
	} else {
		out_bitpix = lc->bitpix;
	}
	
	/* Get source image data */
	cmpack_ccd_set_channel(infile, lc->channel);
	res = cmpack_ccd_to_image(infile, out_bitpix, &img);
	if (res!=0) 
		return res;

	/* Binning */
	if (lc->hbin>1 || lc->vbin>1) {
		CmpackImage *tmp = cmpack_image_binning(img, lc->hbin, lc->vbin);
		cmpack_image_destroy(img);
		img = tmp;
	}

	/* Flip image */
	if (lc->hflip || lc->vflip) 
		cmpack_image_transpose(img, lc->hflip, lc->vflip);

	/* Set border */
	height = cmpack_image_height(img);
	width = cmpack_image_width(img);
	if (lc->border.left>0) 
		cmpack_image_fillrect(img, 0, 0, lc->border.left, height, lc->minvalue);
	if (lc->border.top>0)
		cmpack_image_fillrect(img, 0, 0, width, lc->border.top, lc->minvalue);
	if (lc->border.right>0)
		cmpack_image_fillrect(img, width-lc->border.right, 0, lc->border.right, height, lc->minvalue);
	if (lc->border.bottom>0)
		cmpack_image_fillrect(img, 0, height-lc->border.bottom, width, lc->border.bottom, lc->minvalue);

	/* Create output file */
	res = ccd_prepare(outfile, cmpack_image_width(img), cmpack_image_height(img),
		cmpack_image_bitpix(img));
	if (res==0) 
		res = ccd_copy_header(outfile, infile, lc->con, (wcsfile ? 1 : 0));
	if (res==0 && wcsfile)
		res = ccd_copy_header(outfile, wcsfile, lc->con, 2);
	if (res==0)
		res = ccd_write_image(outfile, img);

	/* Set time offset */
	if (lc->toffset!=0) {
		CmpackCcdParams params;
		if (cmpack_ccd_get_params(outfile, CMPACK_CM_JD, &params)==0 && params.jd>0.0) {
			printpard(lc->con, "Old JD", 1, params.jd, 6);
			params.jd += lc->toffset/86400.0;
			printpard(lc->con, "New JD", 1, params.jd, 6);
			cmpack_ccd_set_params(outfile, CMPACK_CM_JD, &params);
		}
	}

	cmpack_image_destroy(img);
	return res;
}
