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

selection.cpp (C-Munipack project)
Table of selected stars
Copyright (C) 2008 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 <string.h>
#include <assert.h>
#include <glib/gstdio.h>

#include "proc_classes.h"
#include "catfile_class.h"
#include "configuration.h"
#include "configuration.h"
#include "profile.h"
#include "progress_dlg.h"
#include "utils.h"
#include "main.h"

//--------------------------   CONVERSION CONTEXT   --------------------------

//
// Constructor
//
CConvertProc::CConvertProc(void):m_Progress(NULL), m_Bitpix(CMPACK_BITPIX_AUTO),
	m_Channel(CMPACK_CHANNEL_DEFAULT), m_FlipV(0), m_FlipH(0), m_Binning(1), m_TimeOffset(0)
{
	m_Konv = cmpack_konv_init();
}


//
// Destructor
//
CConvertProc::~CConvertProc()
{
	cmpack_konv_destroy(m_Konv);
}


//
// Initialization
//
void CConvertProc::Init(CConsole *pProgress)
{
	// Check project
	assert (g_Project->isOpen() && !g_Project->isReadOnly());

	// Output handling
	m_Progress = pProgress;
	if (pProgress) {
		CmpackConsole *con = cmpack_con_init_cb(CConsole::OutputProc, pProgress);
		if (CConfig::GetBool(CConfig::DEBUG_OUTPUTS))
			cmpack_con_set_level(con, CMPACK_LEVEL_DEBUG);
		cmpack_konv_set_console(m_Konv, con);
		cmpack_con_destroy(con);
	}

	// Check directory
	force_directory(g_Project->DataDir());

	// Data format
	m_Bitpix = (CmpackBitpix)g_Project->Profile()->GetInt(CProfile::WORK_FORMAT);
	cmpack_konv_set_bitpix(m_Konv, m_Bitpix);

	// Frame border
	m_Border = g_Project->Profile()->GetBorder();
	cmpack_konv_set_border(m_Konv, &m_Border);

	// Threshold values
	double minvalue, maxvalue;
	minvalue = g_Project->Profile()->GetDbl(CProfile::BAD_PIXEL_VALUE);
	maxvalue = g_Project->Profile()->GetDbl(CProfile::OVEREXPOSED_VALUE);
	cmpack_konv_set_thresholds(m_Konv, minvalue, maxvalue);

	// Binning
	m_Binning = g_Project->Profile()->GetInt(CProfile::BINNING);
	cmpack_konv_set_binning(m_Konv, m_Binning, m_Binning);

	// Transposition
	m_FlipH = g_Project->Profile()->GetBool(CProfile::FLIP_H);
	m_FlipV = g_Project->Profile()->GetBool(CProfile::FLIP_V);
	cmpack_konv_set_transposition(m_Konv, m_FlipH, m_FlipV);

	// Color to grayscale conversion
	m_Channel = (CmpackChannel)g_Project->GetInt("Convert", "ColorChannel", CMPACK_CHANNEL_DEFAULT);
	cmpack_konv_set_channel(m_Konv, m_Channel);

	// Time offset
	m_TimeOffset = g_Project->Profile()->GetInt(CProfile::TIME_OFFSET);
	cmpack_konv_set_toffset(m_Konv, m_TimeOffset);
}


//
// Convert file
//
bool CConvertProc::Execute(GtkTreePath *pPath, GError **error)
{
	int res = 0, avg_frames, sum_frames;
	char tmp[128], *filter, *tpath;
	double jd, exptime, ccdtemp, toffset;
	bool positiveWest = g_Project->Profile()->GetBool(CProfile::POSITIVE_WEST);
	CCCDFile infile;

	jd = exptime = 0.0;
	ccdtemp = -999.9;
	filter = NULL;
	avg_frames = sum_frames = 1;
	toffset = m_TimeOffset;

	// Clear internal frame data
	g_Project->resetFields(pPath, FRAME_STATE, FRAME_FLAGS,
		FRAME_JULDAT, FRAME_TEMPFILE, FRAME_PHOTFILE, FRAME_REPORT, FRAME_BIASFILE, FRAME_DARKFILE, 
		FRAME_FLATFILE, FRAME_FILTER,  FRAME_TIMECORR,  FRAME_EXPTIME, FRAME_AVGFRAMES, FRAME_SUMFRAMES,  FRAME_CCDTEMP, 
		FRAME_STARS, FRAME_MSTARS, FRAME_OFFSET_X, FRAME_OFFSET_Y, FRAME_THUMBNAIL, FRAME_MB_OBJ, FRAME_MB_KEY, FRAME_TR_XX,  
		FRAME_TR_YX, FRAME_TR_XY, FRAME_TR_YY, FRAME_TR_X0, FRAME_TR_Y0, -1);

	// Set time offset
	if (toffset != 0)
		g_Project->setField(pPath, FRAME_TIMECORR, toffset, -1);
	
	// Get frame data
	int frameId = g_Project->GetFrameID(pPath);
	gchar *fpath = g_Project->GetSourceFile(pPath);

	// Make temporary file name
	sprintf(tmp, "tmp%05d.%s", frameId, FILE_EXTENSION_FITS);
	tpath = g_build_filename(g_Project->DataDir(), tmp, NULL);

	// Open source file
	if (infile.Open(fpath, CMPACK_OPEN_READONLY, (!positiveWest ? 0 : CMPACK_OPENF_POSITIVE_WEST), error)) {
		bool ext_wcs = infile.hasExternalWcs();
		if (infile.isWorkingFormat() && (m_Binning==1) && (m_TimeOffset==0) && 
			(m_Bitpix==CMPACK_BITPIX_AUTO || infile.Depth()==m_Bitpix) &&
			(m_Channel==CMPACK_CHANNEL_DEFAULT) && (m_FlipV==0) && (m_FlipH==0) && 
			(m_Border.left==0 && m_Border.top==0 && m_Border.bottom==0 && m_Border.right==0) &&
			!ext_wcs) {
				// We can make a simple copy, no transformation is required
				infile.Close();
				if (!copy_file(fpath, tpath, false, error)) 
					res = -1;
		} else {
			// Conversion
			CmpackCcdFile *outfile;
			char *f = g_locale_from_utf8(tpath, -1, NULL, NULL, NULL);
			res = cmpack_ccd_open(&outfile, f, CMPACK_OPEN_CREATE, 0);
			g_free(f);
			if (res==0) {
				if (!ext_wcs) {
					res = cmpack_konv(m_Konv, infile.Handle(), outfile);
				} else {
					CCCDFile *xfile = infile.openExternalWcs(error);
					if (xfile) {
						if (xfile->Wcs()) {
							res = cmpack_konv_with_wcs(m_Konv, infile.Handle(), xfile->Handle(), outfile);
						} else {
							set_error(error, CMPACK_ERR_INVALID_WCS);
							res = -1;
						}
						delete xfile;
					} else
						res = -1;
				}
				cmpack_ccd_destroy(outfile);
			}
		}
		if (res==0) {
			// Read frame parameters
			CCCDFile tmpfile;
			if (tmpfile.Open(tpath, CMPACK_OPEN_READONLY, (!positiveWest ? 0 : CMPACK_OPENF_POSITIVE_WEST), error)) {
				jd = tmpfile.JulianDate();
				exptime = tmpfile.ExposureDuration();
				ccdtemp = tmpfile.CCDTemperature();
				avg_frames = tmpfile.AvgFrames();
				sum_frames = tmpfile.SumFrames();
				filter = g_strdup(tmpfile.Filter());
			} else
				res = -1;
		}
	} else
		res = -1;

	if (res<0) {
		// Failed with Gtk error
		if (*error)
			g_Project->SetError(pPath, (*error)->message, CFILE_CONVERSION | CFILE_PHOTOMETRY | CFILE_MATCHING, -1);
	} else
	if (res>0) {
		// Failed with C-Munipack error code
		set_error(error, res);
		if (*error)
			g_Project->SetError(pPath, (*error)->message, CFILE_CONVERSION | CFILE_PHOTOMETRY | CFILE_MATCHING, -1);
	} else {
		// OK 
		if (jd>0 && exptime>0)
			jd += 0.5*exptime/86400.0;
		m_Progress->Print("Conversion OK");
		g_Project->SetResult(pPath, "Conversion OK", CFILE_CONVERSION | CFILE_PHOTOMETRY | CFILE_MATCHING, CFILE_CONVERSION,  
			FRAME_TEMPFILE, tmp, FRAME_JULDAT, jd, FRAME_FILTER, filter, FRAME_EXPTIME, exptime, FRAME_CCDTEMP, ccdtemp, 
			FRAME_AVGFRAMES, avg_frames, FRAME_SUMFRAMES, sum_frames, -1);
	}
	
	g_free(filter);
	g_free(fpath);
	g_free(tpath);
	return (res==0);
}

//-----------------------   BIAS CORRECTION CONTEXT   ----------------------

//
// Constructor
//
CBiasCorrProc::CBiasCorrProc():m_Progress(NULL), m_BiasFile(NULL)
{
	m_Bias = cmpack_bias_init();
}


//
// Destructor
//
CBiasCorrProc::~CBiasCorrProc()
{
	cmpack_bias_destroy(m_Bias);
	g_free(m_BiasFile);
}

//
// Check temporary bias file
//
bool CBiasCorrProc::CheckBiasFile(const gchar *orig_file)
{
	const CFileInfo *orig = g_Project->GetOrigBiasFile(),
		*temp = g_Project->GetTempBiasFile();

	if (!orig->Valid() || !temp->Valid()) 
		return false;
	if (orig->Compare(CFileInfo(orig_file))!=0) 
		return false;
	if (temp->Compare(CFileInfo(temp->FullPath()))!=0) 
		return false;
	return true;
}

//
// Initialization
//
bool CBiasCorrProc::Init(CConsole *pProgress, const gchar *fbias, GError **error)
{
	// Check project
	assert (g_Project->isOpen() && !g_Project->isReadOnly());

	// Output handling
	m_Progress = pProgress;
	if (pProgress) {
		CmpackConsole *con = cmpack_con_init_cb(CConsole::OutputProc, pProgress);
		if (CConfig::GetBool(CConfig::DEBUG_OUTPUTS))
			cmpack_con_set_level(con, CMPACK_LEVEL_DEBUG);
		cmpack_bias_set_console(m_Bias, con);
		cmpack_con_destroy(con);
	}

	g_free(m_BiasFile);
	m_BiasFile = g_strdup(fbias);

	// Check directory
	force_directory(g_Project->DataDir());

	char tmp[512];
	sprintf(tmp, "Bias frame: %s", fbias);
	pProgress->Print(tmp);

	// Convert bias frame
	if (!CheckBiasFile(fbias)) {
		gchar *temp = g_build_filename(g_Project->DataDir(), "bias." FILE_EXTENSION_FITS, NULL);
		bool ok = ConvertBiasFrame(pProgress, fbias, temp, error);
		g_free(temp);
		if (!ok)
			return false;
	}

	// Load bias frame
	const gchar *tpath = g_Project->GetTempBiasFile()->FullPath();
	CmpackCcdFile *bias;
	char *f = g_locale_from_utf8(tpath, -1, NULL, NULL, NULL);
	int res = cmpack_ccd_open(&bias, f, CMPACK_OPEN_READONLY, 0);
	g_free(f);
	if (res==0) {
		res = cmpack_bias_rbias(m_Bias, bias);
		cmpack_ccd_destroy(bias);
	}
	if (res!=0) {
		// Failed
		set_error(error, "Error when reading the file", fbias, res);
		return false;
	}

	// OK
	CmpackBorder border(g_Project->Profile()->GetBorder());
	cmpack_bias_set_border(m_Bias, &border);
	double minvalue = g_Project->Profile()->GetDbl(CProfile::BAD_PIXEL_VALUE);
	double maxvalue = g_Project->Profile()->GetDbl(CProfile::OVEREXPOSED_VALUE);
	cmpack_bias_set_thresholds(m_Bias, minvalue, maxvalue);
	return true;
}


//
// Convert bias frame
//
bool CBiasCorrProc::ConvertBiasFrame(CConsole *pProgress, const gchar *orig_file, const gchar *temp_file, GError **error)
{
	CmpackKonv *konv = cmpack_konv_init();

	// Delete temporary file
	g_unlink(temp_file);

	// Output handling
	if (pProgress) {
		CmpackConsole *con = cmpack_con_init_cb(CConsole::OutputProc, pProgress);
		if (CConfig::GetBool(CConfig::DEBUG_OUTPUTS))
			cmpack_con_set_level(con, CMPACK_LEVEL_DEBUG);
		cmpack_konv_set_console(konv, con);
		cmpack_con_destroy(con);
	}

	// Data format
	CmpackBitpix bitpix = (CmpackBitpix)g_Project->Profile()->GetInt(CProfile::WORK_FORMAT);
	cmpack_konv_set_bitpix(konv, bitpix);

	// Threshold values
	double minvalue, maxvalue;
	minvalue = g_Project->Profile()->GetDbl(CProfile::BAD_PIXEL_VALUE);
	maxvalue = g_Project->Profile()->GetDbl(CProfile::OVEREXPOSED_VALUE);
	cmpack_konv_set_thresholds(konv, minvalue, maxvalue);

	// Color to grayscale conversion
	CmpackChannel channel = (CmpackChannel)g_Project->GetInt("Convert", "ColorChannel", CMPACK_CHANNEL_DEFAULT);
	cmpack_konv_set_channel(konv, channel);

	// Open source file
	CCCDFile infile;
	bool positiveWest = g_Project->Profile()->GetBool(CProfile::POSITIVE_WEST);
	if (!infile.Open(orig_file, CMPACK_OPEN_READONLY, (!positiveWest ? 0 : CMPACK_OPENF_POSITIVE_WEST), error)) {
		cmpack_konv_destroy(konv);
		return false;
	}

	if (infile.isWorkingFormat() && (bitpix==CMPACK_BITPIX_AUTO || infile.Depth()==bitpix)) {
		// We can make a simple copy, no transformation is required
		infile.Close();
		if (!copy_file(orig_file, temp_file, false, error)) {
			cmpack_konv_destroy(konv);
			return false;
		}
	} else {
		CmpackCcdFile *outfile;
		char *f = g_locale_from_utf8(temp_file, -1, NULL, NULL, NULL);
		int res = cmpack_ccd_open(&outfile, f, CMPACK_OPEN_CREATE, 0);
		g_free(f);
		if (res==0) {
			res = cmpack_konv(konv, infile.Handle(), outfile);
			cmpack_ccd_destroy(outfile);
		}
		if (res!=0) {
			set_error(error, "Error when converting the file", infile.Path(), res);
			cmpack_konv_destroy(konv);
			return false;
		}
	}

	// OK
	g_Project->SetTempBiasFile(temp_file);
	cmpack_konv_destroy(konv);
	return true;
}


//
// Bias correction
//
bool CBiasCorrProc::Execute(GtkTreePath *pPath, GError **error)
{
	// Check frame status
	unsigned state = g_Project->GetState(pPath);
	if ((state & CFILE_CONVERSION)==0) {
		const char *msg = "A working copy of the source files must be made before the bias correction. Use the function \"Fetch/convert files\" first.";
		set_error(error, msg);
		g_Project->SetErrorMessage(pPath, msg);
		return false;
	}
	if ((state & CFILE_BIASCORR)!=0) {
		const char *msg = "The bias correction has been applied to the frame already.";
		set_error(error, msg);
		g_Project->SetErrorMessage(pPath, msg);
		return false;
	}
	if ((state & CFILE_DARKCORR)!=0) {
		const char *msg = "You cannot do the bias correction after the dark correction.";
		set_error(error, msg);
		g_Project->SetErrorMessage(pPath, msg);
		return false;
	}
	if ((state & CFILE_FLATCORR)!=0) {
		const char *msg = "You cannot do the bias correction after the flat correction.";
		set_error(error, msg);
		g_Project->SetErrorMessage(pPath, msg);
		return false;
	}
	if ((state & CFILE_PHOTOMETRY)!=0) {
		const char *msg = "You cannot do the bias correction after the photometry.";
		set_error(error, msg);
		g_Project->SetErrorMessage(pPath, msg);
		return false;
	}

	// Bias correction
	CmpackCcdFile *infile;
	char *tpath = g_Project->GetImageFile(pPath);
	char *f = g_locale_from_utf8(tpath, -1, NULL, NULL, NULL);
	int res = cmpack_ccd_open(&infile, f, CMPACK_OPEN_READWRITE, 0);
	g_free(f);
	g_free(tpath);
	if (res==0) {
		res = cmpack_bias(m_Bias, infile);
		cmpack_ccd_destroy(infile);
	}
	if (res>0) {
		// Failed
		set_error(error, res);
		g_Project->SetErrorCode(pPath, res);
		return false;
	}

	// Success
	m_Progress->Print("Bias correction OK");
	g_Project->SetResult(pPath, "Bias correction OK", CFILE_BIASCORR, CFILE_BIASCORR, FRAME_BIASFILE, m_BiasFile, -1);
	return true;
}

//-----------------------   TIME CORRECTION CONTEXT   ----------------------

//
// Initialization
//
void CTimeCorrProc::Init(CConsole *pProgress, double seconds, bool reset)
{
	// Check project
	assert (g_Project->isOpen() && !g_Project->isReadOnly());

	m_Progress = pProgress;
	m_Seconds = seconds;
	m_Reset = reset;

	// Check directory
	force_directory(g_Project->DataDir());

	// Print correction
	char msg[512];
	sprintf(msg, "Time correction: %.3f seconds", m_Seconds);
	pProgress->Print(msg);
}


//
// Time correction
//
bool CTimeCorrProc::Execute(GtkTreePath *pPath, GError **error)
{
	double jd = 0, exptime = 0;
	const char *msg;
	char buf[512];
	GtkTreeModel *pList = g_Project->FileList();

	// Check frame status
	int state = g_Project->GetState(pPath);
	if ((state & CFILE_CONVERSION)==0) {
		msg = "A working copy of the source files must be made before time correction. Use the function \"Fetch/convert files\" first.";
		set_error(error, msg);
		g_Project->SetErrorMessage(pPath, msg);
		return false;
	}

	// Open source file 
	if (!m_Reset) {
		gchar *tpath = g_Project->GetImageFile(pPath);
		if (tpath) {
			CmpackCcdFile *ccdfile;
			char *f = g_locale_from_utf8(tpath, -1, NULL, NULL, NULL);
			int res = cmpack_ccd_open(&ccdfile, f, CMPACK_OPEN_READWRITE, 0);
			g_free(f);
			if (res==0) {
				CmpackCcdParams params;
				if (cmpack_ccd_get_params(ccdfile, CMPACK_CM_JD | CMPACK_CM_EXPOSURE, &params)==0 && params.jd>0) {
					params.jd += m_Seconds/86400.0;
					jd = params.jd;
					exptime = params.exposure;
					cmpack_ccd_set_params(ccdfile, CMPACK_CM_JD, &params);
				}
				cmpack_ccd_destroy(ccdfile);
			}
			if (res!=0) {
				set_error(error, res);
				g_Project->SetErrorCode(pPath, res);
				g_free(tpath);
				return false;
			}
			if (jd<=0) {
				set_error(error, CMPACK_ERR_INVALID_JULDAT);
				g_Project->SetErrorCode(pPath, CMPACK_ERR_INVALID_JULDAT);
				g_free(tpath);
				return false;
			}
			g_free(tpath);
		}
	} else {
		gchar *spath = g_Project->GetSourceFile(pPath);
		if (spath) {
			CmpackCcdFile *srcfile;
			char *sf = g_locale_from_utf8(spath, -1, NULL, NULL, NULL);
			int res = cmpack_ccd_open(&srcfile, sf, CMPACK_OPEN_READONLY, 0);
			g_free(sf);
			if (res==0) {
				gchar *tpath = g_Project->GetImageFile(pPath);
				CmpackCcdFile *ccdfile;
				char *tf = g_locale_from_utf8(tpath, -1, NULL, NULL, NULL);
				res = cmpack_ccd_open(&ccdfile, tf, CMPACK_OPEN_READWRITE, 0);
				g_free(tf);
				if (res==0) {
					CmpackCcdParams params;
					if (cmpack_ccd_get_params(srcfile, CMPACK_CM_JD, &params)==0 && params.jd>0) {
						params.jd += m_Seconds/86400.0;
						jd = params.jd;
						cmpack_ccd_set_params(srcfile, CMPACK_CM_JD, &params);
					}
					cmpack_ccd_destroy(ccdfile);
				}
				CmpackCcdParams params;
				if (cmpack_ccd_get_params(srcfile, CMPACK_CM_EXPOSURE, &params)==0)
					exptime = params.exposure;
				g_free(tpath);
				cmpack_ccd_destroy(srcfile);
			}
			if (res!=0) {
				set_error(error, res);
				g_Project->SetErrorCode(pPath, res);
				g_free(spath);
				return false;
			}
			if (jd<=0) {
				set_error(error, CMPACK_ERR_INVALID_JULDAT);
				g_Project->SetErrorCode(pPath, CMPACK_ERR_INVALID_JULDAT);
				g_free(spath);
				return false;
			}
			g_free(spath);
		}
	}

	if (jd>0) {
		// Update time in the photometry file
		jd += 0.5*exptime/86400.0;
		if ((state & CFILE_PHOTOMETRY)!=0) {
			gchar *fpath = g_Project->GetPhotFile(pPath);
			if (fpath) {
				CmpackPhtFile *phtfile;
				char *ff = g_locale_from_utf8(fpath, -1, NULL, NULL, NULL);
				int res = cmpack_pht_open(&phtfile, ff, CMPACK_OPEN_READWRITE, 0);
				g_free(ff);
				if (res==0) {
					CmpackPhtInfo params;
					params.jd = jd;
					cmpack_pht_set_info(phtfile, CMPACK_PI_JD, &params);
					cmpack_pht_destroy(phtfile);
				}
				g_free(fpath);
			}
		}

		// Success
		double tc = 0;
		if (!m_Reset)
			tc = g_Project->GetTimeCorrection(pPath);
		tc += m_Seconds;
		sprintf(buf, "New JD: %.7f", jd);
		m_Progress->Print(buf);
		m_Progress->Print("Time correction OK");
		g_Project->SetResult(pPath, "Time correction OK", CFILE_TIMECORR, CFILE_TIMECORR, FRAME_JULDAT, jd, FRAME_TIMECORR, tc, -1);
	}

	return true;
}

//-----------------------   DARK CORRECTION CONTEXT   ----------------------

//
// Constructor
//
CDarkCorrProc::CDarkCorrProc():m_Progress(NULL), m_DarkFile(NULL)
{
	m_Dark = cmpack_dark_init();
}


//
// Destructor
//
CDarkCorrProc::~CDarkCorrProc()
{
	cmpack_dark_destroy(m_Dark);
	g_free(m_DarkFile);
}

//
// Check temporary bias file
//
bool CDarkCorrProc::CheckDarkFile(const gchar *orig_file)
{
	const CFileInfo *orig = g_Project->GetOrigDarkFile(),
		*temp = g_Project->GetTempDarkFile();

	if (!orig->Valid() || !temp->Valid()) 
		return false;
	if (orig->Compare(CFileInfo(orig_file))!=0) 
		return false;
	if (temp->Compare(CFileInfo(temp->FullPath()))!=0) 
		return false;
	return true;
}

//
// Initialization
//
bool CDarkCorrProc::Init(CConsole *pProgress, const gchar *fdark, GError **error)
{
	// Check project
	assert (g_Project->isOpen() && !g_Project->isReadOnly());

	// Output handling
	m_Progress = pProgress;
	if (pProgress) {
		CmpackConsole *con = cmpack_con_init_cb(CConsole::OutputProc, pProgress);
		if (CConfig::GetBool(CConfig::DEBUG_OUTPUTS))
			cmpack_con_set_level(con, CMPACK_LEVEL_DEBUG);
		cmpack_dark_set_console(m_Dark, con);
		cmpack_con_destroy(con);
	}

	g_free(m_DarkFile);
	m_DarkFile = g_strdup(fdark);

	// Check directory
	force_directory(g_Project->DataDir());

	char msg[512];
	sprintf(msg, "Dark frame: %s", fdark);
	pProgress->Print(msg);

	// Convert dark frame
	if (!CheckDarkFile(fdark)) {
		char *temp = g_build_filename(g_Project->DataDir(), "dark." FILE_EXTENSION_FITS, NULL);
		bool ok = ConvertDarkFrame(pProgress, fdark, temp, error);
		g_free(temp);
		if (!ok)
			return false;
	}
	
	// Load dark frame
	const gchar *tpath = g_Project->GetTempDarkFile()->FullPath();
	CmpackCcdFile *dark;
	char *f = g_locale_from_utf8(tpath, -1, NULL, NULL, NULL);
	int res = cmpack_ccd_open(&dark, f, CMPACK_OPEN_READONLY, 0);
	g_free(f);
	if (res==0) {
		res = cmpack_dark_rdark(m_Dark, dark);
		cmpack_ccd_destroy(dark);
	}
	if (res!=0) {
		// Failed
		set_error(error, "Error when reading the file", fdark, res);
		return false;
	}

	// OK
	CmpackBorder border(g_Project->Profile()->GetBorder());
	cmpack_dark_set_border(m_Dark, &border);
	double minvalue = g_Project->Profile()->GetDbl(CProfile::BAD_PIXEL_VALUE);
	double maxvalue = g_Project->Profile()->GetDbl(CProfile::OVEREXPOSED_VALUE);
	cmpack_dark_set_thresholds(m_Dark, minvalue, maxvalue);
	bool scaling = g_Project->Profile()->GetBool(CProfile::ADVANCED_CALIBRATION);
	cmpack_dark_set_scaling(m_Dark, scaling);
	return true;
}


//
// Convert dark frame
//
bool CDarkCorrProc::ConvertDarkFrame(CConsole *pProgress, const gchar *orig_file, const gchar *temp_file, GError **error)
{
	CmpackKonv *konv = cmpack_konv_init();

	// Delete temporary file
	g_unlink(temp_file);

	// Output handling
	if (pProgress) {
		CmpackConsole *con = cmpack_con_init_cb(CConsole::OutputProc, pProgress);
		if (CConfig::GetBool(CConfig::DEBUG_OUTPUTS))
			cmpack_con_set_level(con, CMPACK_LEVEL_DEBUG);
		cmpack_konv_set_console(konv, con);
		cmpack_con_destroy(con);
	}

	// Data format
	CmpackBitpix bitpix = (CmpackBitpix)g_Project->Profile()->GetInt(CProfile::WORK_FORMAT);
	cmpack_konv_set_bitpix(konv, bitpix);

	// Threshold values
	double minvalue, maxvalue;
	minvalue = g_Project->Profile()->GetDbl(CProfile::BAD_PIXEL_VALUE);
	maxvalue = g_Project->Profile()->GetDbl(CProfile::OVEREXPOSED_VALUE);
	cmpack_konv_set_thresholds(konv, minvalue, maxvalue);

	// Color to grayscale conversion
	CmpackChannel channel = (CmpackChannel)g_Project->GetInt("Convert", "ColorChannel", CMPACK_CHANNEL_DEFAULT);
	cmpack_konv_set_channel(konv, channel);

	// Open source file
	CCCDFile infile;
	bool positiveWest = g_Project->Profile()->GetBool(CProfile::POSITIVE_WEST);
	if (!infile.Open(orig_file, CMPACK_OPEN_READONLY, (!positiveWest ? 0 : CMPACK_OPENF_POSITIVE_WEST), error)) {
		cmpack_konv_destroy(konv);
		return false;
	}

	if (infile.isWorkingFormat() && (bitpix==CMPACK_BITPIX_AUTO || infile.Depth()==bitpix)) {
		// We can make a simple copy, no transformation is required
		infile.Close();
		if (!copy_file(orig_file, temp_file, false, error)) {
			cmpack_konv_destroy(konv);
			return false;
		}
	} else {
		CmpackCcdFile *outfile;
		char *f = g_locale_from_utf8(temp_file, -1, NULL, NULL, NULL);
		int res = cmpack_ccd_open(&outfile, f, CMPACK_OPEN_CREATE, 0);
		g_free(f);
		if (res==0) {
			res = cmpack_konv(konv, infile.Handle(), outfile);
			cmpack_ccd_destroy(outfile);
		}
		if (res!=0) {
			set_error(error, "Error when converting the file", infile.Path(), res);
			cmpack_konv_destroy(konv);
			return false;
		}
	}

	// OK
	g_Project->SetTempDarkFile(temp_file);
	cmpack_konv_destroy(konv);
	return true;
}


//
// Dark correction
//
bool CDarkCorrProc::Execute(GtkTreePath *pPath, GError **error)
{
	bool advcalib = g_Project->Profile()->GetBool(CProfile::ADVANCED_CALIBRATION);

	// Check frame status
	unsigned state = g_Project->GetState(pPath);
	if ((state & CFILE_CONVERSION)==0) {
		const char *msg = "A working copy of the source files must be made before the dark correction. Use the function \"Fetch/convert files\" first.";
		set_error(error, msg);
		g_Project->SetErrorMessage(pPath, msg);
		return false;
	}
	if (advcalib && (state & CFILE_BIASCORR)==0) {
		const char *msg = "The bias correction must be applied to the frame before the dark correction.";
		set_error(error, msg);
		g_Project->SetErrorMessage(pPath, msg);
		return false;
	}
	if ((state & CFILE_DARKCORR)!=0) {
		const char *msg = "The dark correction has been applied to the frame already.";
		set_error(error, msg);
		g_Project->SetErrorMessage(pPath, msg);
		return false;
	}
	if ((state & CFILE_FLATCORR)!=0) {
		const char *msg = "You cannot do the dark correction after flat correction.";
		set_error(error, msg);
		g_Project->SetErrorMessage(pPath, msg);
		return false;
	}
	if ((state & CFILE_PHOTOMETRY)!=0) {
		const char *msg = "You cannot do the dark correction after the photometry.";
		set_error(error, msg);
		g_Project->SetErrorMessage(pPath, msg);
		return false;
	}

	// Dark correction
	gchar *tpath = g_Project->GetImageFile(pPath);
	CmpackCcdFile *ccdfile;
	char *f = g_locale_from_utf8(tpath, -1, NULL, NULL, NULL);
	int res = cmpack_ccd_open(&ccdfile, f, CMPACK_OPEN_READWRITE, 0);
	g_free(f);
	if (res==0) {
		res = cmpack_dark(m_Dark, ccdfile);
		cmpack_ccd_destroy(ccdfile);
	}

	if (res>0) {
		// Failed
		set_error(error, res);
		g_Project->SetErrorCode(pPath, res);
		g_free(tpath);
		return false;
	}

	// Success
	m_Progress->Print("Dark-frame correction OK");
	g_Project->SetResult(pPath, "Dark-frame correction OK", CFILE_DARKCORR, CFILE_DARKCORR, FRAME_DARKFILE, m_DarkFile, -1);
	g_free(tpath);
	return true;
}

//-----------------------   FLAT CORRECTION CONTEXT   ----------------------

//
// Constructor
//
CFlatCorrProc::CFlatCorrProc():m_Progress(NULL), m_FlatFile(NULL)
{
	m_Flat = cmpack_flat_init();
}

//
// Destructor
//
CFlatCorrProc::~CFlatCorrProc()
{
	cmpack_flat_destroy(m_Flat);
	g_free(m_FlatFile);
}

//
// Check temporary bias file
//
bool CFlatCorrProc::CheckFlatFile(const gchar *orig_file)
{
	const CFileInfo *orig = g_Project->GetOrigFlatFile(),
		*temp = g_Project->GetTempFlatFile();

	if (!orig->Valid() || !temp->Valid()) 
		return false;
	if (orig->Compare(CFileInfo(orig_file))!=0) 
		return false;
	if (temp->Compare(CFileInfo(temp->FullPath()))!=0) 
		return false;
	return true;
}

//
// Initialization
//
bool CFlatCorrProc::Init(CConsole *pProgress, const gchar *fflat, GError **error)
{
	// Check project
	assert (g_Project->isOpen() && !g_Project->isReadOnly());

	// Output handling
	m_Progress = pProgress;
	if (pProgress) {
		CmpackConsole *con = cmpack_con_init_cb(CConsole::OutputProc, pProgress);
		if (CConfig::GetBool(CConfig::DEBUG_OUTPUTS))
			cmpack_con_set_level(con, CMPACK_LEVEL_DEBUG);
		cmpack_flat_set_console(m_Flat, con);
		cmpack_con_destroy(con);
	}

	g_free(m_FlatFile);
	m_FlatFile = g_strdup(fflat);

	// Check directory
	force_directory(g_Project->DataDir());

	char msg[512];
	sprintf(msg, "Flat frame: %s", fflat);
	pProgress->Print(msg);

	// Flat frame
	if (!CheckFlatFile(fflat)) {
		char *temp = g_build_filename(g_Project->DataDir(), "flat." FILE_EXTENSION_FITS, NULL);
		bool ok = ConvertFlatFrame(pProgress, fflat, temp, error);
		g_free(temp);
		if (!ok)
			return false;
	}

	const gchar *tpath = g_Project->GetTempFlatFile()->FullPath();
	CmpackCcdFile *flat;
	char *f = g_locale_from_utf8(tpath, -1, NULL, NULL, NULL);
	int res = cmpack_ccd_open(&flat, f, CMPACK_OPEN_READONLY, 0);
	g_free(f);
	if (res==0) {
		res = cmpack_flat_rflat(m_Flat, flat);
		cmpack_ccd_destroy(flat);
	}
	if (res>0) {
		// Failed
		set_error(error, "Error when reading the file", fflat, res);
		return false;
	}

	// OK
	CmpackBorder border(g_Project->Profile()->GetBorder());
	cmpack_flat_set_border(m_Flat, &border);
	double minvalue = g_Project->Profile()->GetDbl(CProfile::BAD_PIXEL_VALUE);
	double maxvalue = g_Project->Profile()->GetDbl(CProfile::OVEREXPOSED_VALUE);
	cmpack_flat_set_thresholds(m_Flat, minvalue, maxvalue);
	return true;
}

//
// Convert dark frame
//
bool CFlatCorrProc::ConvertFlatFrame(CConsole *pProgress, const gchar *orig_file, const gchar *temp_file, GError **error)
{
	CmpackKonv *konv = cmpack_konv_init();

	// Delete temporary file
	g_unlink(temp_file);

	// Output handling
	if (pProgress) {
		CmpackConsole *con = cmpack_con_init_cb(CConsole::OutputProc, pProgress);
		if (CConfig::GetBool(CConfig::DEBUG_OUTPUTS))
			cmpack_con_set_level(con, CMPACK_LEVEL_DEBUG);
		cmpack_konv_set_console(konv, con);
		cmpack_con_destroy(con);
	}

	// Data format
	CmpackBitpix bitpix = (CmpackBitpix)g_Project->Profile()->GetInt(CProfile::WORK_FORMAT);
	cmpack_konv_set_bitpix(konv, bitpix);

	// Threshold values
	double minvalue, maxvalue;
	minvalue = g_Project->Profile()->GetDbl(CProfile::BAD_PIXEL_VALUE);
	maxvalue = g_Project->Profile()->GetDbl(CProfile::OVEREXPOSED_VALUE);
	cmpack_konv_set_thresholds(konv, minvalue, maxvalue);

	// Color to grayscale conversion
	CmpackChannel channel = (CmpackChannel)g_Project->GetInt("Convert", "ColorChannel", CMPACK_CHANNEL_DEFAULT);
	cmpack_konv_set_channel(konv, channel);

	// Open source file
	CCCDFile infile;
	bool positiveWest = g_Project->Profile()->GetBool(CProfile::POSITIVE_WEST);
	if (!infile.Open(orig_file, CMPACK_OPEN_READONLY, (!positiveWest ? 0 : CMPACK_OPENF_POSITIVE_WEST), error)) {
		cmpack_konv_destroy(konv);
		return false;
	}

	if (infile.isWorkingFormat() && (bitpix==CMPACK_BITPIX_AUTO || infile.Depth()==bitpix)) {
		// We can make a simple copy, no transformation is required
		infile.Close();
		if (!copy_file(orig_file, temp_file, false, error)) {
			cmpack_konv_destroy(konv);
			return false;
		}
	} else {
		CmpackCcdFile *outfile;
		char *f = g_locale_from_utf8(temp_file, -1, NULL, NULL, NULL);
		int res = cmpack_ccd_open(&outfile, f, CMPACK_OPEN_CREATE, 0);
		g_free(f);
		if (res==0) {
			res = cmpack_konv(konv, infile.Handle(), outfile);
			cmpack_ccd_destroy(outfile);
		}
		if (res!=0) {
			set_error(error, "Error when converting the file", infile.Path(), res);
			cmpack_konv_destroy(konv);
			return false;
		}
	}

	// OK
	g_Project->SetTempFlatFile(temp_file);
	cmpack_konv_destroy(konv);
	return true;
}


//
// Flat correction
//
bool CFlatCorrProc::Execute(GtkTreePath *pPath, GError **error)
{
	// Check frame status
	unsigned state = g_Project->GetState(pPath);
	if ((state & CFILE_CONVERSION)==0) {
		const gchar *msg = "A working copy of the source files must be made before the flat correction. Use the function \"Fetch/convert files\" first.";
		set_error(error, msg);
		g_Project->SetErrorMessage(pPath, msg);
		return false;
	}
	if ((state & CFILE_FLATCORR)!=0) {
		const gchar *msg = "The flat correction has been applied to the frame already.";
		set_error(error, msg);
		g_Project->SetErrorMessage(pPath, msg);
		return false;
	}
	if ((state & CFILE_PHOTOMETRY)!=0) {
		const gchar *msg = "You cannot do time correction after the photometry.";
		set_error(error, msg);
		g_Project->SetErrorMessage(pPath, msg);
		return false;
	}

	// Open source file 
	gchar *tpath = g_Project->GetImageFile(pPath);
	CmpackCcdFile *infile;
	char *f = g_locale_from_utf8(tpath, -1, NULL, NULL, NULL);
	int res = cmpack_ccd_open(&infile, f, CMPACK_OPEN_READWRITE, 0);
	g_free(f);
	if (res==0) {
		res = cmpack_flat(m_Flat, infile);
		cmpack_ccd_destroy(infile);
	}
	if (res>0) {
		// Failed
		set_error(error, res);
		g_Project->SetErrorCode(pPath, res);
		g_free(tpath);
		return false;
	}

	// Success
	m_Progress->Print("Flat-frame correction OK");
	g_Project->SetResult(pPath, "Flat-frame correction OK", CFILE_FLATCORR, CFILE_FLATCORR, FRAME_FLATFILE, m_FlatFile, -1);
	g_free(tpath);
	return true;
}

//-----------------------   PHOTOMETRY CONTEXT   ----------------------

//
// Constructor
//
CPhotometryProc::CPhotometryProc():m_Progress(NULL)
{
	m_Phot = cmpack_phot_init();
}


//
// Destructor
//
CPhotometryProc::~CPhotometryProc()
{
	cmpack_phot_destroy(m_Phot);
}


//
// Initialization
//
void CPhotometryProc::Init(CConsole *pProgress)
{
	// Check project
	assert (g_Project->isOpen() && !g_Project->isReadOnly());

	// Output handling
	m_Progress = pProgress;
	if (pProgress) {
		CmpackConsole *con = cmpack_con_init_cb(CConsole::OutputProc, pProgress);
		if (CConfig::GetBool(CConfig::DEBUG_OUTPUTS))
			cmpack_con_set_level(con, CMPACK_LEVEL_DEBUG);
		cmpack_phot_set_console(m_Phot, con);
		cmpack_con_destroy(con);
	}

	// Check directory
	force_directory(g_Project->DataDir());

	// Photometry parameters
	cmpack_phot_set_rnoise(m_Phot, g_Project->Profile()->GetDbl(CProfile::READ_NOISE));
	cmpack_phot_set_adcgain(m_Phot, g_Project->Profile()->GetDbl(CProfile::ADC_GAIN));
	cmpack_phot_set_minval(m_Phot, g_Project->Profile()->GetDbl(CProfile::BAD_PIXEL_VALUE));
	cmpack_phot_set_maxval(m_Phot, g_Project->Profile()->GetDbl(CProfile::OVEREXPOSED_VALUE));
	cmpack_phot_set_fwhm(m_Phot, g_Project->Profile()->GetDbl(CProfile::DETECTION_FWHM));
	cmpack_phot_set_thresh(m_Phot, g_Project->Profile()->GetDbl(CProfile::DETECTION_THRESHOLD));
	cmpack_phot_set_minrnd(m_Phot, g_Project->Profile()->GetDbl(CProfile::MIN_ROUNDNESS));
	cmpack_phot_set_maxrnd(m_Phot, g_Project->Profile()->GetDbl(CProfile::MAX_ROUNDNESS));
	cmpack_phot_set_minshrp(m_Phot, g_Project->Profile()->GetDbl(CProfile::MIN_SHARPNESS));
	cmpack_phot_set_maxshrp(m_Phot, g_Project->Profile()->GetDbl(CProfile::MAX_SHARPNESS));
	cmpack_phot_set_skyin(m_Phot, g_Project->Profile()->GetDbl(CProfile::SKY_INNER_RADIUS));
	cmpack_phot_set_skyout(m_Phot, g_Project->Profile()->GetDbl(CProfile::SKY_OUTER_RADIUS));
	cmpack_phot_set_limit(m_Phot, g_Project->Profile()->GetInt(CProfile::MAX_STARS));

	// Frame Border
	CmpackBorder border(g_Project->Profile()->GetBorder());
	cmpack_phot_set_border(m_Phot, &border);

	// Apertures
	double aper[MAX_APERTURES];
	CApertures ap = g_Project->Profile()->Apertures();
	for (int i=0; i<ap.Count() && i<MAX_APERTURES; i++) 
		aper[i] = ap.Get(i)->Radius();
	cmpack_phot_set_aper(m_Phot, aper, ap.Count());
	g_Project->SetApertures(ap);
}


//
// Photometry
//
bool CPhotometryProc::Execute(GtkTreePath *pPath, GError **error)
{
	int state, fileid, nstars;
	const char *msg;
	char pht[128], txt[128];

	// Check frame status
	state = g_Project->GetState(pPath);
	if ((state & CFILE_CONVERSION) == 0) {
		msg = "A working copy of the source files must be made before the photometry. Use the function \"Fetch/convert files\" first.";
		set_error(error, msg);
		g_Project->SetErrorMessage(pPath, msg);
		return false;
	}

	// Open source file 
	gchar *srcpath = g_Project->GetImageFile(pPath);
	CmpackCcdFile *ccdfile;
	gchar *sf = g_locale_from_utf8(srcpath, -1, NULL, NULL, NULL);
	int res = cmpack_ccd_open(&ccdfile, sf, CMPACK_OPEN_READONLY, 0);
	g_free(sf);
	if (res!=0) {
		set_error(error, res);
		g_Project->SetErrorCode(pPath, res);
		g_free(sf);
		g_free(srcpath);
		return false;
	}
	g_free(srcpath);

	// Open target file
	fileid = g_Project->GetFrameID(pPath);
	sprintf(pht, "tmp%05d.%s", fileid, FILE_EXTENSION_PHOTOMETRY);
	char *outpath = g_build_filename(g_Project->DataDir(), pht, NULL);
	g_unlink(outpath);
	CmpackPhtFile *outfile;
	gchar *of = g_locale_from_utf8(outpath, -1, NULL, NULL, NULL);
	res = cmpack_pht_open(&outfile, of, CMPACK_OPEN_CREATE, 0);
	g_free(of);
	if (res!=0) {
		set_error(error, res);
		g_Project->SetErrorCode(pPath, res);
		g_free(of);
		g_free(outpath);
		cmpack_ccd_destroy(ccdfile);
		return false;
	}
	g_free(outpath);

	// Execute photometry
	res = cmpack_phot(m_Phot, ccdfile, outfile, &nstars);
	if (res==0) {
		// Success
		sprintf(txt, "%d stars found", nstars);
		m_Progress->Print(txt);
		sprintf(txt, "Photometry OK (%d stars found)", nstars);
		g_Project->SetResult(pPath, txt, CFILE_PHOTOMETRY | CFILE_MATCHING, CFILE_PHOTOMETRY, FRAME_PHOTFILE, pht, FRAME_STARS, nstars, 
			FRAME_MSTARS, 0, FRAME_OFFSET_X, 0.0, FRAME_OFFSET_Y, 0.0, FRAME_MB_OBJ, 0, FRAME_MB_KEY, 0, FRAME_TR_XX, 0.0, FRAME_TR_YX, 0.0, 
			FRAME_TR_XY, 0.0, FRAME_TR_YY, 0.0, FRAME_TR_X0, 0.0, FRAME_TR_Y0, 0.0, -1);
	} else {
		// Failed
		set_error(error, res);
		g_Project->SetError(pPath, res, CFILE_PHOTOMETRY | CFILE_MATCHING, FRAME_PHOTFILE, FRAME_STARS, FRAME_MSTARS, 
			FRAME_OFFSET_X, FRAME_OFFSET_Y, FRAME_MB_OBJ, FRAME_MB_KEY, FRAME_TR_XX, FRAME_TR_YX, FRAME_TR_XY, FRAME_TR_YY, 
			FRAME_TR_X0, FRAME_TR_Y0, -1);
	}

	cmpack_pht_close(outfile);
	cmpack_ccd_destroy(ccdfile);
	return res==0;
}

//-----------------------   PHOTOMETRY CONTEXT   ----------------------

//
// Constructor
//
CPhotometry2Proc::CPhotometry2Proc() :m_Progress(NULL)
{
	m_Phot = cmpack_phot_init();
}


//
// Destructor
//
CPhotometry2Proc::~CPhotometry2Proc()
{
	cmpack_phot_destroy(m_Phot);
}


//
// Initialization for automatic star detection
//
void CPhotometry2Proc::Init(CConsole *pProgress)
{
	// Check project
	assert(g_Project->isOpen() && !g_Project->isReadOnly());

	// Output handling
	m_Progress = pProgress;
	if (pProgress) {
		CmpackConsole *con = cmpack_con_init_cb(CConsole::OutputProc, pProgress);
		if (CConfig::GetBool(CConfig::DEBUG_OUTPUTS))
			cmpack_con_set_level(con, CMPACK_LEVEL_DEBUG);
		cmpack_phot_set_console(m_Phot, con);
		cmpack_con_destroy(con);
	}

	// Check directory
	force_directory(g_Project->DataDir());

	// Photometry parameters
	cmpack_phot_set_rnoise(m_Phot, g_Project->Profile()->GetDbl(CProfile::READ_NOISE));
	cmpack_phot_set_adcgain(m_Phot, g_Project->Profile()->GetDbl(CProfile::ADC_GAIN));
	cmpack_phot_set_minval(m_Phot, g_Project->Profile()->GetDbl(CProfile::BAD_PIXEL_VALUE));
	cmpack_phot_set_maxval(m_Phot, g_Project->Profile()->GetDbl(CProfile::OVEREXPOSED_VALUE));
	cmpack_phot_set_fwhm(m_Phot, g_Project->Profile()->GetDbl(CProfile::DETECTION_FWHM));
	cmpack_phot_set_thresh(m_Phot, g_Project->Profile()->GetDbl(CProfile::DETECTION_THRESHOLD));
	cmpack_phot_set_minrnd(m_Phot, g_Project->Profile()->GetDbl(CProfile::MIN_ROUNDNESS));
	cmpack_phot_set_maxrnd(m_Phot, g_Project->Profile()->GetDbl(CProfile::MAX_ROUNDNESS));
	cmpack_phot_set_minshrp(m_Phot, g_Project->Profile()->GetDbl(CProfile::MIN_SHARPNESS));
	cmpack_phot_set_maxshrp(m_Phot, g_Project->Profile()->GetDbl(CProfile::MAX_SHARPNESS));
	cmpack_phot_set_skyin(m_Phot, g_Project->Profile()->GetDbl(CProfile::SKY_INNER_RADIUS));
	cmpack_phot_set_skyout(m_Phot, g_Project->Profile()->GetDbl(CProfile::SKY_OUTER_RADIUS));
	cmpack_phot_set_limit(m_Phot, g_Project->Profile()->GetInt(CProfile::MAX_STARS));

	// Frame Border
	CmpackBorder border(g_Project->Profile()->GetBorder());
	cmpack_phot_set_border(m_Phot, &border);

	// Apertures
	double aper[MAX_APERTURES];
	CApertures ap = g_Project->Profile()->Apertures();
	for (int i = 0; i < ap.Count() && i < MAX_APERTURES; i++)
		aper[i] = ap.Get(i)->Radius();
	cmpack_phot_set_aper(m_Phot, aper, ap.Count());
	g_Project->SetApertures(ap);
}


//
// Initialize with user-defined object list
//
void CPhotometry2Proc::InitWithObjects(CConsole *pCon, int length, const CmpackPhotObject *list)
{
	Init(pCon);

	// User-defined objects
	cmpack_phot_set_use_object_list(m_Phot, TRUE);
	cmpack_phot_set_object_list(m_Phot, length, list);
}


//
// Photometry
//
bool CPhotometry2Proc::Execute(GtkTreePath *pPath, GError **error)
{
	int state, fileid, nstars;
	const char *msg;
	char pht[128], txt[128];

	// Check frame status
	state = g_Project->GetState(pPath);
	if ((state & CFILE_CONVERSION) == 0) {
		msg = "A working copy of the source files must be made before the photometry. Use the function \"Fetch/convert files\" first.";
		set_error(error, msg);
		g_Project->SetErrorMessage(pPath, msg);
		return false;
	}

	// Open source file 
	gchar *srcpath = g_Project->GetImageFile(pPath);
	CmpackCcdFile *ccdfile;
	gchar *sf = g_locale_from_utf8(srcpath, -1, NULL, NULL, NULL);
	int res = cmpack_ccd_open(&ccdfile, sf, CMPACK_OPEN_READONLY, 0);
	g_free(sf);
	if (res != 0) {
		set_error(error, res);
		g_Project->SetErrorCode(pPath, res);
		g_free(sf);
		g_free(srcpath);
		return false;
	}
	g_free(srcpath);

	// Open target file
	fileid = g_Project->GetFrameID(pPath);
	sprintf(pht, "tmp%05d.%s", fileid, FILE_EXTENSION_PHOTOMETRY);
	char *outpath = g_build_filename(g_Project->DataDir(), pht, NULL);
	g_unlink(outpath);
	CmpackPhtFile *outfile;
	gchar *of = g_locale_from_utf8(outpath, -1, NULL, NULL, NULL);
	res = cmpack_pht_open(&outfile, of, CMPACK_OPEN_CREATE, 0);
	g_free(of);
	if (res != 0) {
		set_error(error, res);
		g_Project->SetErrorCode(pPath, res);
		g_free(of);
		g_free(outpath);
		cmpack_ccd_destroy(ccdfile);
		return false;
	}
	g_free(outpath);

	// Execute photometry
	res = cmpack_phot(m_Phot, ccdfile, outfile, &nstars);
	if (res == 0) {
		// Success
		sprintf(txt, "%d stars found", nstars);
		m_Progress->Print(txt);
		strcpy(txt, "Photometry & matching OK");
		g_Project->SetResult(pPath, txt, CFILE_PHOTOMETRY | CFILE_MATCHING, CFILE_PHOTOMETRY | CFILE_MATCHING, FRAME_PHOTFILE, pht, 
			FRAME_STARS, nstars, FRAME_MSTARS, nstars, FRAME_OFFSET_X, 0.0, FRAME_OFFSET_Y, 0.0, FRAME_MB_OBJ, 0, FRAME_MB_KEY, 0, FRAME_TR_XX, 
			1.0, FRAME_TR_YX, 0.0, FRAME_TR_XY, 0.0, FRAME_TR_YY, 1.0, FRAME_TR_X0, 0.0, FRAME_TR_Y0, 0.0, -1);
	}
	else {
		// Failed
		set_error(error, res);
		g_Project->SetError(pPath, res, CFILE_PHOTOMETRY | CFILE_MATCHING, FRAME_PHOTFILE, FRAME_STARS, FRAME_MSTARS, 
			FRAME_OFFSET_X, FRAME_OFFSET_Y, FRAME_MB_OBJ, FRAME_MB_KEY, FRAME_TR_XX, FRAME_TR_YX, FRAME_TR_XY, FRAME_TR_YY, 
			FRAME_TR_X0, FRAME_TR_Y0, -1);
	}

	cmpack_pht_close(outfile);
	cmpack_ccd_destroy(ccdfile);
	return res == 0;
}

// Read image file and prepare it for processing
bool CPhotometry2Proc::Read(CCCDFile &file, GError **error)
{
	int res = cmpack_phot_read(m_Phot, file.Handle());
	if (res != 0) 
		set_error(error, res);
	return res == 0;
}

// Find position of an object
bool CPhotometry2Proc::Pos(double *x, double *y)
{
	return cmpack_phot_pos(m_Phot, x, y) == CMPACK_ERR_OK;
}

// Star detection and enumeration
bool CPhotometry2Proc::FindFirst()
{
	return cmpack_phot_find_first(m_Phot) == CMPACK_ERR_OK;
}

bool CPhotometry2Proc::FindNext()
{
	return cmpack_phot_find_next(m_Phot) == CMPACK_ERR_OK;
}

bool CPhotometry2Proc::GetData(unsigned mask, CmpackPhotObject &data)
{
	return cmpack_phot_get_data(m_Phot, mask, &data) == CMPACK_ERR_OK;
}

void CPhotometry2Proc::FindClose()
{
	cmpack_phot_find_close(m_Phot);
}

//-----------------------   MATCHING CONTEXT   ----------------------

//
// Constructor
//
CMatchingProc::CMatchingProc():m_Progress(NULL)
{
	m_Match = cmpack_match_init();
}


//
// Destructor
//
CMatchingProc::~CMatchingProc()
{
	cmpack_match_destroy(m_Match);
}


//
// Initialization
//
bool CMatchingProc::InitWithReferenceFrame(CConsole *pProgress, GtkTreePath *path, GError **error)
{
	int nstars;

	// Check project
	assert (g_Project->isOpen() && !g_Project->isReadOnly());

	// Output handling
	m_Progress = pProgress;
	if (pProgress) {
		CmpackConsole *con = cmpack_con_init_cb(CConsole::OutputProc, pProgress);
		if (CConfig::GetBool(CConfig::DEBUG_OUTPUTS))
			cmpack_con_set_level(con, CMPACK_LEVEL_DEBUG);
		cmpack_match_set_console(m_Match, con);
		cmpack_con_destroy(con);
	}

	// Check directory
	force_directory(g_Project->DataDir());
	g_Project->SetTargetType(STATIONARY_TARGET);

	// Set configuration
	if (g_Project->Profile()->GetBool(CProfile::SPARSE_FIELDS)) {
		cmpack_match_set_method(m_Match, CMPACK_MATCH_SPARSE_FIELDS);
		cmpack_match_set_maxoffset(m_Match, g_Project->Profile()->GetDbl(CProfile::MAX_OFFSET));
	} else if (g_Project->Profile()->GetBool(CProfile::DENSE_FIELDS)) {
		cmpack_match_set_method(m_Match, CMPACK_MATCH_PHILNR);
		cmpack_match_set_threshold(m_Match, g_Project->Profile()->GetDbl(CProfile::MATCH_CLIP2));
	} else {
		cmpack_match_set_method(m_Match, CMPACK_MATCH_STANDARD);
		cmpack_match_set_threshold(m_Match, g_Project->Profile()->GetDbl(CProfile::MATCH_CLIP));
		cmpack_match_set_maxstars(m_Match, g_Project->Profile()->GetInt(CProfile::MATCH_READ_STARS));
		cmpack_match_set_vertices(m_Match, g_Project->Profile()->GetInt(CProfile::MATCH_IDENT_STARS));
	}

	char txt[512];
	sprintf(txt, "Reference frame: #%d", g_Project->GetFrameID(path));
	pProgress->Print(txt);

	// Read reference frame
	gchar *tpath = g_Project->GetPhotFile(path);
	CmpackPhtFile *phtfile;
	char *f = g_locale_from_utf8(tpath, -1, NULL, NULL, NULL);
	int res = cmpack_pht_open(&phtfile, f, CMPACK_OPEN_READONLY, 0);
	g_free(f);
	if (res==0) {
		res = cmpack_match_readref_pht(m_Match, phtfile);
		cmpack_pht_destroy(phtfile);
	}
	if (res!=0) {
		// Failed
		set_error(error, "Error when reading the file", tpath, res);
		g_free(tpath);
		return false;
	}
	g_free(tpath);

	nstars = cmpack_match_refcount(m_Match);
	sprintf(txt, "%d stars found", nstars);
	m_Progress->Print(txt);

	// Set reference frame
	g_Project->SetReferenceFrame(path);

	// Object coordinates, location and observer's name
	CCCDFile ccd;
	gchar *fpath = g_Project->GetImageFile(path);
	bool positiveWest = g_Project->Profile()->GetBool(CProfile::POSITIVE_WEST);
	if (!ccd.Open(fpath, CMPACK_OPEN_READONLY, (!positiveWest ? 0 : CMPACK_OPENF_POSITIVE_WEST), error)) {
		g_free(fpath);
		return false;
	}

	if (ccd.Object()->Valid()) 
		g_Project->SetObjectCoords(*ccd.Object());
	else
		g_Project->SetObjectCoords(CObjectCoords());
	if (ccd.Location()->Valid()) 
		g_Project->SetLocation(*ccd.Location());
	else if (g_Project->Profile()->DefaultLocation().Valid()) 
		g_Project->SetLocation(g_Project->Profile()->DefaultLocation());
	else
		g_Project->SetLocation(CLocation());
	g_Project->SetObserver(ccd.Observer());
	g_Project->SetTelescope(ccd.Telescope());
	g_Project->SetInstrument(ccd.Instrument());
	g_free(fpath);
	return true;
}

//
// Initialization
//
bool CMatchingProc::InitWithCatalogFile(CConsole *pProgress, const char *cat_file, GError **error)
{
	int nstars;

	// Check project
	assert (g_Project->isOpen() && !g_Project->isReadOnly());

	// Output handling
	m_Progress = pProgress;
	if (pProgress) {
		CmpackConsole *con = cmpack_con_init_cb(CConsole::OutputProc, pProgress);
		if (CConfig::GetBool(CConfig::DEBUG_OUTPUTS))
			cmpack_con_set_level(con, CMPACK_LEVEL_DEBUG);
		cmpack_match_set_console(m_Match, con);
		cmpack_con_destroy(con);
	}

	// Check directory
	force_directory(g_Project->DataDir());
	g_Project->SetTargetType(STATIONARY_TARGET);

	// Set configuration
	if (g_Project->Profile()->GetBool(CProfile::SPARSE_FIELDS)) {
		cmpack_match_set_method(m_Match, CMPACK_MATCH_SPARSE_FIELDS);
		cmpack_match_set_maxoffset(m_Match, g_Project->Profile()->GetDbl(CProfile::MAX_OFFSET));
	} else if (g_Project->Profile()->GetBool(CProfile::DENSE_FIELDS)) {
		cmpack_match_set_method(m_Match, CMPACK_MATCH_PHILNR);
		cmpack_match_set_threshold(m_Match, g_Project->Profile()->GetDbl(CProfile::MATCH_CLIP2));
	} else {
		cmpack_match_set_method(m_Match, CMPACK_MATCH_STANDARD);
		cmpack_match_set_threshold(m_Match, g_Project->Profile()->GetDbl(CProfile::MATCH_CLIP));
		cmpack_match_set_maxstars(m_Match, g_Project->Profile()->GetInt(CProfile::MATCH_READ_STARS));
		cmpack_match_set_vertices(m_Match, g_Project->Profile()->GetInt(CProfile::MATCH_IDENT_STARS));
	}

	char txt[512];
	sprintf(txt, "Catalog file: %s", cat_file);
	pProgress->Print(txt);

	if (!g_file_test(cat_file, G_FILE_TEST_IS_REGULAR)) {
		set_error(error, "Error when reading the file", cat_file, CMPACK_ERR_FILE_NOT_FOUND);
		return false;
	}

	// Make copy of catalog file
	char *tmp_file = g_build_filename(g_Project->DataDir(), "reference." FILE_EXTENSION_CATALOG, NULL);
	if (!CheckCatalogFile(cat_file)) 
		ConvertCatFile(cat_file, tmp_file);

	// Read catalog file
	CmpackCatFile *catfile;
	char *f = g_locale_from_utf8(tmp_file, -1, NULL, NULL, NULL);
	int res = cmpack_cat_open(&catfile, f, CMPACK_OPEN_READONLY, 0);
	g_free(f);
	if (res!=0) {
		set_error(error, "Error when reading the file", tmp_file, res);
		g_free(tmp_file);
		return false;
	}
	res = cmpack_match_readref_cat(m_Match, catfile);
	if (res!=0) {
		set_error(error, "Error when reading the file", tmp_file, res);
		g_free(tmp_file);
		return false;
	}
	g_free(tmp_file);

	nstars = cmpack_match_refcount(m_Match);
	sprintf(txt, "%d stars found", nstars);
	m_Progress->Print(txt);

	// Set reference frame
	g_Project->Lock();
	g_Project->SetReferenceFile(cat_file);

	// Selected stars, tags, object coordinates, location, observer's name
	CCatalog cat(catfile);
	const CSelectionList *list = cat.Selections();
	gchar *base_name = g_path_get_basename(cat_file);
	gchar *file_name = StripFileExtension(base_name);
	if (list && list->Count()>0) {
		g_Project->SetLastSelection(list->At(0));
		g_Project->SelectionList()->Clear();
		int index = 1;
		for (int i=0; i<list->Count(); i++) {
			gchar *news_name = NULL;
			if (list->Name(i) && g_Project->SelectionList()->IndexOf(list->Name(i))<0) 
				news_name = g_strdup(list->Name(i));
			else if (file_name && g_Project->SelectionList()->IndexOf(file_name)<0) 
				news_name = g_strdup(file_name);
			while (!news_name) {
				gchar *name = (gchar*)g_malloc(256*sizeof(gchar));
				sprintf(name, "Selection %d", index);
				if (g_Project->SelectionList()->IndexOf(name)<0) 
					news_name = name;
				else
					g_free(name);
				index++;
			}
			g_Project->SelectionList()->Set(news_name, list->At(i));
			g_free(news_name);
		}
	} else {
		g_Project->SetLastSelection(CSelection());
		g_Project->SelectionList()->Clear();
	}
	g_Project->SetTags(*cat.Tags());
	if (cat.Object()->Valid()) {
		g_Project->SetObjectCoords(*cat.Object());
	} else
		g_Project->SetObjectCoords(CObjectCoords());
	if (cat.Location()->Valid()) {
		g_Project->SetLocation(*cat.Location());
	} else if (g_Project->Profile()->DefaultLocation().Valid()) {
		g_Project->SetLocation(g_Project->Profile()->DefaultLocation());
	} else
		g_Project->SetLocation(CLocation());
	g_Project->SetObserver(cat.Observer());
	g_Project->SetTelescope(cat.Telescope());
	g_Project->SetInstrument(cat.Instrument());
	g_Project->Unlock();
	g_free(base_name);
	g_free(file_name);
	cmpack_cat_destroy(catfile);

	return true;
}

//
// Initialization
//
bool CMatchingProc::Reinitialize(CConsole *pProgress, GError **error)
{
	int nstars;
	char txt[512];
	GtkTreePath *refpath;
	const gchar *tmp_path;

	// Check project
	assert (g_Project->isOpen() && !g_Project->isReadOnly());
	assert (g_Project->GetTargetType() == STATIONARY_TARGET);

	m_Progress = pProgress;
	if (pProgress) {
		CmpackConsole *con = cmpack_con_init_cb(CConsole::OutputProc, pProgress);
		if (CConfig::GetBool(CConfig::DEBUG_OUTPUTS))
			cmpack_con_set_level(con, CMPACK_LEVEL_DEBUG);
		cmpack_match_set_console(m_Match, con);
		cmpack_con_destroy(con);
	}

	force_directory(g_Project->DataDir());

	// Set configuration
	if (g_Project->Profile()->GetBool(CProfile::SPARSE_FIELDS)) {
		cmpack_match_set_method(m_Match, CMPACK_MATCH_SPARSE_FIELDS);
		cmpack_match_set_maxoffset(m_Match, g_Project->Profile()->GetDbl(CProfile::MAX_OFFSET));
	} else if (g_Project->Profile()->GetBool(CProfile::DENSE_FIELDS)) {
		cmpack_match_set_method(m_Match, CMPACK_MATCH_PHILNR);
		cmpack_match_set_threshold(m_Match, g_Project->Profile()->GetDbl(CProfile::MATCH_CLIP2));
	} else {
		cmpack_match_set_method(m_Match, CMPACK_MATCH_STANDARD);
		cmpack_match_set_threshold(m_Match, g_Project->Profile()->GetDbl(CProfile::MATCH_CLIP));
		cmpack_match_set_maxstars(m_Match, g_Project->Profile()->GetInt(CProfile::MATCH_READ_STARS));
		cmpack_match_set_vertices(m_Match, g_Project->Profile()->GetInt(CProfile::MATCH_IDENT_STARS));
	}

	switch (g_Project->GetReferenceType())
	{
	case REF_FRAME:
		refpath = g_Project->GetReferencePath();
		if (refpath) {
			// Read reference frame
			gchar *pht_file = g_Project->GetPhotFile(refpath);
			if (pht_file) {
				CmpackPhtFile *phtfile;
				char *f = g_locale_from_utf8(pht_file, -1, NULL, NULL, NULL);
				int res = cmpack_pht_open(&phtfile, f, CMPACK_OPEN_READONLY, 0);
				g_free(f);
				if (res==0) {
					res = cmpack_match_readref_pht(m_Match, phtfile);
					cmpack_pht_destroy(phtfile);
				}
				if (res!=0) {
					set_error(error, "Error when reading the file", pht_file, res);
					g_free(pht_file);
					gtk_tree_path_free(refpath);
					return false;
				}
				g_free(pht_file);
				// Print reference frame ID to the message log
				int frame_id = g_Project->GetFrameID(refpath);
				sprintf(txt, "Reference frame: #%d", frame_id);
				pProgress->Print(txt);
				nstars = cmpack_match_refcount(m_Match);
				sprintf(txt, "%d stars found", nstars);
				m_Progress->Print(txt);
			}
			gtk_tree_path_free(refpath);
		}
		break;

	case REF_CATALOG_FILE:
		tmp_path = g_Project->GetTempCatFile()->FullPath();
		if (tmp_path) {
			// Read catalogue file
			CmpackCatFile *catfile;
			char *f = g_locale_from_utf8(tmp_path, -1, NULL, NULL, NULL);
			int res = cmpack_cat_open(&catfile, f, CMPACK_OPEN_READONLY, 0);
			g_free(f);
			if (res==0) {
				res = cmpack_match_readref_cat(m_Match, catfile);
				cmpack_cat_destroy(catfile);
			}
			if (res!=0) {
				set_error(error, "Error when reading the file", tmp_path, res);
				return false;
			}
			// Print catalog file name to the message log
			const gchar *cat_path = g_Project->GetReferenceCatalog()->FullPath();
			if (cat_path) {
				gchar *basename = g_path_get_basename(cat_path);
				sprintf(txt, "Catalog file: %s", basename);
				pProgress->Print(txt);
				g_free(basename);
			}
			nstars = cmpack_match_refcount(m_Match);
			sprintf(txt, "%d stars found", nstars);
			m_Progress->Print(txt);
		}
		break;

	default:
		break;
	}

	return true;
}

//
// Check temporary catalogue file
//
bool CMatchingProc::CheckCatalogFile(const gchar *orig_file)
{
	const CFileInfo *orig = g_Project->GetReferenceCatalog(),
		*temp = g_Project->GetTempCatFile();

	if (!orig->Valid() || !temp->Valid()) 
		return false;
	if (orig->Compare(CFileInfo(orig_file))!=0) 
		return false;
	if (temp->Compare(CFileInfo(temp->FullPath()))!=0) 
		return false;
	return true;
}

void CMatchingProc::ConvertCatFile(const gchar *orig_file, const gchar *temp_file)
{
	char *fts_path, *src_path, *dst_path;

	// Delete existing files
	g_unlink(temp_file);
	fts_path = SetFileExtension(temp_file, FILE_EXTENSION_FITS);
	g_unlink(fts_path);
	g_free(fts_path);

	// Copy catalog photometry file
	if (copy_file(orig_file, temp_file, false, NULL)) {
		// Copy catalog image file
		src_path = SetFileExtension(orig_file, FILE_EXTENSION_FITS);
		dst_path = SetFileExtension(temp_file, FILE_EXTENSION_FITS);
		copy_file(src_path, dst_path, false, NULL);
		g_free(src_path);
		g_free(dst_path);
		g_Project->SetTempCatFile(CFileInfo(temp_file));
	}
}

bool CMatchingProc::Execute(GtkTreePath *pPath, GError **error)
{
	int nstars, mstars, state;
	const char *msg;
	double offset_x, offset_y;
	char *fpath, txt[128];
	CmpackMatrix matrix;

	assert (g_Project->isOpen() && !g_Project->isReadOnly());
	assert (g_Project->GetTargetType() == STATIONARY_TARGET);

	mstars = nstars = 0;
	offset_x = offset_y = 0;

	// Check frame status
	state = g_Project->GetState(pPath);
	if ((state & CFILE_CONVERSION) == 0) {
		msg = "A working copy of the source files must be made before the photometry. Use the function \"Fetch/convert files\" first.";
		set_error(error, msg);
		g_Project->SetErrorMessage(pPath, msg);
		return false;
	}
	if ((state & CFILE_PHOTOMETRY) == 0) {
		msg = "The photometry must be applied to the frame before the matching.";
		set_error(error, msg);
		g_Project->SetErrorMessage(pPath, msg);
		return false;
	}

	// Open the photometry file
	CmpackPhtFile *phtfile;
	fpath = g_Project->GetPhotFile(pPath);
	char *f = g_locale_from_utf8(fpath, -1, NULL, NULL, NULL);
	int res = cmpack_pht_open(&phtfile, f, CMPACK_OPEN_READWRITE, 0);
	g_free(f);
	if (res!=0) {
		set_error(error, res);
		g_free(fpath);
		return false;
	}

	res = cmpack_match(m_Match, phtfile, &mstars);
	if (res!=0) {
		// Failed
		set_error(error, res);
		g_Project->SetError(pPath, res, CFILE_MATCHING, FRAME_MSTARS, FRAME_OFFSET_X, FRAME_OFFSET_Y, 
			FRAME_TR_XX, FRAME_TR_YX, FRAME_TR_XY, FRAME_TR_YY, FRAME_TR_X0, FRAME_TR_Y0, -1);
		cmpack_pht_destroy(phtfile);
		g_free(fpath);
		return false;
	} 

	// Success
	nstars = g_Project->GetStars(pPath);
	cmpack_match_get_offset(m_Match, &offset_x, &offset_y);
	cmpack_match_get_matrix(m_Match, &matrix);
	sprintf(txt, "Matching OK (%.0f %% stars matched)", 100.0*((double)mstars)/nstars);
	g_Project->SetResult(pPath, txt, CFILE_MATCHING, CFILE_MATCHING, FRAME_MSTARS, mstars, FRAME_OFFSET_X, offset_x, FRAME_OFFSET_Y, offset_y, 
		FRAME_TR_XX, matrix.xx, FRAME_TR_YX, matrix.yx, FRAME_TR_XY, matrix.xy, FRAME_TR_YY, matrix.yy, FRAME_TR_X0, matrix.x0, FRAME_TR_Y0, matrix.y0, -1);
	sprintf(txt, "%d stars matched (%.0f %%).", mstars, 100.0*((double)mstars)/nstars);
	m_Progress->Print(txt);
	cmpack_pht_destroy(phtfile);
	g_free(fpath);
	return true;
}

//-----------------------   MATCHING CONTEXT   ----------------------

//
// Constructor
//
CTrackingProc::CTrackingProc():m_Progress(NULL), m_refId(-1), m_keys(NULL)
{
	m_Match = cmpack_match_init();
}


//
// Destructor
//
CTrackingProc::~CTrackingProc()
{
	cmpack_match_destroy(m_Match);
	g_slist_foreach(m_keys, (GFunc)g_free, NULL);
	g_slist_free(m_keys);
}


//
// Initialization
//
bool CTrackingProc::InitWithReferenceFrame(CConsole *pProgress, GtkTreePath *path, GError **error)
{
	int nstars;

	ClearTracking();

	// Check project
	assert (g_Project->isOpen() && !g_Project->isReadOnly());

	// Output handling
	m_Progress = pProgress;
	if (pProgress) {
		CmpackConsole *con = cmpack_con_init_cb(CConsole::OutputProc, pProgress);
		if (CConfig::GetBool(CConfig::DEBUG_OUTPUTS))
			cmpack_con_set_level(con, CMPACK_LEVEL_DEBUG);
		cmpack_match_set_console(m_Match, con);
		cmpack_con_destroy(con);
	}

	// Check directory
	force_directory(g_Project->DataDir());
	g_Project->SetTargetType(MOVING_TARGET);

	// Set configuration
	if (g_Project->Profile()->GetBool(CProfile::SPARSE_FIELDS)) {
		cmpack_match_set_method(m_Match, CMPACK_MATCH_SPARSE_FIELDS);
		cmpack_match_set_maxoffset(m_Match, g_Project->Profile()->GetDbl(CProfile::MAX_OFFSET));
	} else if (g_Project->Profile()->GetBool(CProfile::DENSE_FIELDS)) {
		cmpack_match_set_method(m_Match, CMPACK_MATCH_PHILNR);
		cmpack_match_set_threshold(m_Match, g_Project->Profile()->GetDbl(CProfile::MATCH_CLIP2));
	} else {
		cmpack_match_set_method(m_Match, CMPACK_MATCH_STANDARD);
		cmpack_match_set_threshold(m_Match, g_Project->Profile()->GetDbl(CProfile::MATCH_CLIP));
		cmpack_match_set_maxstars(m_Match, g_Project->Profile()->GetInt(CProfile::MATCH_READ_STARS));
		cmpack_match_set_vertices(m_Match, g_Project->Profile()->GetInt(CProfile::MATCH_IDENT_STARS));
	}
	cmpack_match_set_ignore(m_Match, 0, 0);
	cmpack_match_set_objref(m_Match, 0, 0);
	cmpack_match_set_objpos(m_Match, 0, 0, 0);

	char txt[512];
	sprintf(txt, "Reference frame: #%d", g_Project->GetFrameID(path));
	pProgress->Print(txt);

	// Read reference frame
	gchar *tpath = g_Project->GetPhotFile(path);
	CmpackPhtFile *phtfile;
	char *f = g_locale_from_utf8(tpath, -1, NULL, NULL, NULL);
	int res = cmpack_pht_open(&phtfile, f, CMPACK_OPEN_READONLY, 0);
	g_free(f);
	if (res==0) {
		res = cmpack_match_readref_pht(m_Match, phtfile);
		cmpack_pht_destroy(phtfile);
	}
	if (res!=0) {
		// Failed
		set_error(error, "Error when reading the file", tpath, res);
		g_free(tpath);
		return false;
	}
	g_free(tpath);

	nstars = cmpack_match_refcount(m_Match);
	sprintf(txt, "%d stars found", nstars);
	m_Progress->Print(txt);

	// Set reference frame
	m_refId = g_Project->GetMBObject(path);
	g_Project->SetReferenceFrame(path, m_refId);

	sprintf(txt, "Moving target is object #%d", m_refId);
	pProgress->Print(txt);

	// Object coordinates, location and observer's name
	CCCDFile ccd;
	gchar *fpath = g_Project->GetImageFile(path);
	bool positiveWest = g_Project->Profile()->GetBool(CProfile::POSITIVE_WEST);
	if (!ccd.Open(fpath, CMPACK_OPEN_READONLY, (!positiveWest ? 0 : CMPACK_OPENF_POSITIVE_WEST), error)) {
		g_free(fpath);
		return false;
	}

	if (ccd.Object()->Valid()) 
		g_Project->SetObjectCoords(*ccd.Object());
	else
		g_Project->SetObjectCoords(CObjectCoords());
	if (ccd.Location()->Valid()) 
		g_Project->SetLocation(*ccd.Location());
	else if (g_Project->Profile()->DefaultLocation().Valid()) 
		g_Project->SetLocation(g_Project->Profile()->DefaultLocation());
	else
		g_Project->SetLocation(CLocation());
	g_Project->SetObserver(ccd.Observer());
	g_Project->SetTelescope(ccd.Telescope());
	g_Project->SetInstrument(ccd.Instrument());
	g_free(fpath);
	return true;
}

//
// Initialization
//
bool CTrackingProc::Reinitialize(CConsole *pProgress, GError **error)
{
	int nstars;
	char txt[512];
	GtkTreePath *refpath;

	// Check project
	assert (g_Project->isOpen() && !g_Project->isReadOnly());
	assert (g_Project->GetTargetType() == MOVING_TARGET);

	m_Progress = pProgress;
	if (pProgress) {
		CmpackConsole *con = cmpack_con_init_cb(CConsole::OutputProc, pProgress);
		if (CConfig::GetBool(CConfig::DEBUG_OUTPUTS))
			cmpack_con_set_level(con, CMPACK_LEVEL_DEBUG);
		cmpack_match_set_console(m_Match, con);
		cmpack_con_destroy(con);
	}

	force_directory(g_Project->DataDir());

	// Set configuration
	if (g_Project->Profile()->GetBool(CProfile::SPARSE_FIELDS)) {
		cmpack_match_set_method(m_Match, CMPACK_MATCH_SPARSE_FIELDS);
		cmpack_match_set_maxoffset(m_Match, g_Project->Profile()->GetDbl(CProfile::MAX_OFFSET));
	} else if (g_Project->Profile()->GetBool(CProfile::DENSE_FIELDS)) {
		cmpack_match_set_method(m_Match, CMPACK_MATCH_PHILNR);
		cmpack_match_set_threshold(m_Match, g_Project->Profile()->GetDbl(CProfile::MATCH_CLIP2));
	} else {
		cmpack_match_set_method(m_Match, CMPACK_MATCH_STANDARD);
		cmpack_match_set_threshold(m_Match, g_Project->Profile()->GetDbl(CProfile::MATCH_CLIP));
		cmpack_match_set_maxstars(m_Match, g_Project->Profile()->GetInt(CProfile::MATCH_READ_STARS));
		cmpack_match_set_vertices(m_Match, g_Project->Profile()->GetInt(CProfile::MATCH_IDENT_STARS));
	}
	cmpack_match_set_ignore(m_Match, 0, 0);
	cmpack_match_set_objref(m_Match, 0, 0);
	cmpack_match_set_objpos(m_Match, 0, 0, 0);
	ClearTracking();

	refpath = g_Project->GetReferencePath();
	if (refpath) {
		// Read reference frame
		gchar *pht_file = g_Project->GetPhotFile(refpath);
		if (pht_file) {
			CmpackPhtFile *phtfile;
			char *f = g_locale_from_utf8(pht_file, -1, NULL, NULL, NULL);
			int res = cmpack_pht_open(&phtfile, f, CMPACK_OPEN_READONLY, 0);
			g_free(f);
			if (res==0) {
				res = cmpack_match_readref_pht(m_Match, phtfile);
				cmpack_pht_destroy(phtfile);
			}
			if (res!=0) {
				set_error(error, "Error when reading the file", pht_file, res);
				g_free(pht_file);
				gtk_tree_path_free(refpath);
				return false;
			}
			g_free(pht_file);
			// Print reference frame ID to the message log
			int frame_id = g_Project->GetFrameID(refpath);
			sprintf(txt, "Reference frame: #%d", frame_id);
			pProgress->Print(txt);
		}
		gtk_tree_path_free(refpath);
	}

	nstars = cmpack_match_refcount(m_Match);
	sprintf(txt, "%d stars found", nstars);
	m_Progress->Print(txt);

	// Set reference frame
	m_refId = g_Project->GetReferenceTarget();
	sprintf(txt, "Moving target is object #%d", m_refId);
	pProgress->Print(txt);

	m_MBData = *g_Project->GetTrackingData();

	if (!m_MBData.valid()) {
		set_error(error, "The tracking data are missing.");
		return false;
	}

	return true;
}

// Clear tracking, reset parameters related to tracking
void CTrackingProc::ClearTracking(void)
{
	g_slist_foreach(m_keys, (GFunc)g_free, NULL);
	g_slist_free(m_keys);
	m_keys = NULL;
	m_refId = 0;
	m_MBData.clear();
}

bool CTrackingProc::Execute(GtkTreePath *pPath, GError **error)
{
	int nstars, mstars, state;
	const char *msg;
	double offset_x, offset_y;
	char *fpath, txt[128];
	CmpackMatrix matrix;

	mstars = nstars = 0;
	offset_x = offset_y = 0;

	// Check frame status
	state = g_Project->GetState(pPath);
	if ((state & CFILE_CONVERSION) == 0) {
		msg = "A working copy of the source files must be made before the photometry. Use the function \"Fetch/convert files\" first.";
		set_error(error, msg);
		g_Project->SetErrorMessage(pPath, msg);
		return false;
	}
	if ((state & CFILE_PHOTOMETRY) == 0) {
		msg = "The photometry must be applied to the frame before the matching.";
		set_error(error, msg);
		g_Project->SetErrorMessage(pPath, msg);
		return false;
	}

	// Open the photometry file
	fpath = g_Project->GetPhotFile(pPath);
	CmpackPhtFile *phtfile;
	char *f = g_locale_from_utf8(fpath, -1, NULL, NULL, NULL);
	int res = cmpack_pht_open(&phtfile, f, CMPACK_OPEN_READWRITE, 0);
	g_free(f);
	if (res!=0) {
		set_error(error, res);
		g_free(fpath);
		return false;
	}
	
	tTrackPoint *mb_data = NULL;

	// Minor body tracking
	if (!m_MBData.valid()) {
		// Key frames
		int obj_id = g_Project->GetMBObject(pPath);
		int obj_index = cmpack_pht_find_object(phtfile, obj_id, 0);
		if (obj_index<0) {
			set_error(error, "The target object is not selected for a key frame.");
			cmpack_pht_destroy(phtfile);
			g_free(fpath);
			return false;
		}
		mb_data = (tTrackPoint*)g_malloc0(sizeof(tTrackPoint));
		mb_data->frame_id = g_Project->GetFrameID(pPath);
		mb_data->obj_index = obj_index;
		CmpackPhtInfo info;
		cmpack_pht_get_info(phtfile, CMPACK_PI_JD, &info);
		mb_data->jd = info.jd;
		CmpackPhtObject object;
		if (cmpack_pht_get_object(phtfile, obj_index, CMPACK_OM_CENTER, &object)==0) {
			mb_data->src_x = object.x;
			mb_data->src_y = object.y;
		} 
		cmpack_match_set_ignore(m_Match, obj_id, m_refId);
		cmpack_match_set_objref(m_Match, obj_id, m_refId);
		cmpack_match_set_objpos(m_Match, 0, 0, 0);
	} else {
		// Other frames
		CmpackPhtInfo info;
		cmpack_pht_get_info(phtfile, CMPACK_PI_JD, &info);
		double x, y;
		m_MBData.track(info.jd, x, y);
		cmpack_match_set_ignore(m_Match, 0, 0);
		cmpack_match_set_objref(m_Match, 0, 0);
		cmpack_match_set_objpos(m_Match, m_refId, x, y);
		// Update tracking data range
		double t0 = m_MBData.t0(), t1 = m_MBData.t1();
		if (info.jd - m_MBData.jd0() < t0)
			t0 = info.jd - m_MBData.jd0();
		if (info.jd - m_MBData.jd0() > t1)
			t1 = info.jd - m_MBData.jd0();
		m_MBData.setRange(t0, t1);
		g_Project->SetTrackingData(m_MBData);
	}

	res = cmpack_match(m_Match, phtfile, &mstars);
	if (res!=0) {
		// Failed
		set_error(error, res);
		g_Project->SetError(pPath, res, CFILE_MATCHING, FRAME_MSTARS, FRAME_OFFSET_X, FRAME_OFFSET_Y, 
			FRAME_TR_XX, FRAME_TR_YX, FRAME_TR_XY, FRAME_TR_YY, FRAME_TR_X0, FRAME_TR_Y0, -1);
		cmpack_pht_destroy(phtfile);
		g_free(fpath);
		return false;
	}

	if (!m_MBData.valid()) {
		// Key frames
		CmpackMatrix ref2src;
		cmpack_match_get_matrix(m_Match, &ref2src);
		mb_data->ref_x = mb_data->src_x;
		mb_data->ref_y = mb_data->src_y;
		cmpack_matrix_reverse_transform(&ref2src, &mb_data->ref_x, &mb_data->ref_y);
		CmpackPhtObject object;
		object.ref_id = m_refId;
		cmpack_pht_set_object(phtfile, mb_data->obj_index, CMPACK_PO_REF_ID, &object);
		m_keys = g_slist_append(m_keys, mb_data);
	} else {
		// Other frames
		bool tracking_failed = cmpack_pht_find_object(phtfile, m_refId, 1) < 0;
		if (tracking_failed) {
			// Matching of moving target failed
			nstars = g_Project->GetStars(pPath);
			sprintf(txt, "Target object not matched - %d stars matched (%.0f %%)", mstars, 100.0*((double)mstars)/nstars);
			g_Project->SetError(pPath, txt, CFILE_MATCHING, FRAME_MSTARS, FRAME_OFFSET_X, FRAME_OFFSET_Y, FRAME_TR_XX, 
				FRAME_TR_YX, FRAME_TR_XY, FRAME_TR_YY, FRAME_TR_X0, FRAME_TR_Y0, -1);
			set_error(error, CMPACK_ERR_TARGET_NOT_FOUND);
			m_Progress->Print(txt);
			g_Project->setField(pPath, FRAME_MSTARS, mstars, -1);
			cmpack_match_get_offset(m_Match, &offset_x, &offset_y);
			g_Project->SetOffset(pPath, offset_x, offset_y);
			cmpack_match_get_matrix(m_Match, &matrix);
			g_Project->SetTrafo(pPath, matrix);
			cmpack_pht_destroy(phtfile);
			g_free(fpath);
			return false;
		}
	}

	// Success
	nstars = g_Project->GetStars(pPath);
	cmpack_match_get_offset(m_Match, &offset_x, &offset_y);
	cmpack_match_get_matrix(m_Match, &matrix);
	sprintf(txt, "Matching OK (%.0f %% stars matched)", 100.0*((double)mstars)/nstars);
	g_Project->SetResult(pPath, txt, CFILE_MATCHING, CFILE_MATCHING, FRAME_MSTARS, mstars, FRAME_OFFSET_X, offset_x, FRAME_OFFSET_Y, offset_y, 
		FRAME_TR_XX, matrix.xx, FRAME_TR_YX, matrix.yx, FRAME_TR_XY, matrix.xy, FRAME_TR_YY, matrix.yy, FRAME_TR_X0, matrix.x0, FRAME_TR_Y0, matrix.y0, -1);
	sprintf(txt, "%d stars matched (%.0f %%).", mstars, 100.0*((double)mstars)/nstars);
	m_Progress->Print(txt);
	cmpack_pht_destroy(phtfile);
	g_free(fpath);
	return true;
}

// Track moving object
bool CTrackingProc::MBTrack(GError **error) 
{ 
	int count = g_slist_length(m_keys);
	if (count < 3) {
		set_error(error, "Not enough key frames defined (requires at least 3).");
		return false;
	}
	
	double *t = (double*)g_malloc(count*sizeof(double)), *x = (double*)g_malloc(count*sizeof(double)), *y = (double*)g_malloc(count*sizeof(double));
	int i = 0;
	for (GSList *ptr=m_keys; ptr != NULL; ptr = ptr->next) {
		tTrackPoint *pt = (tTrackPoint*)ptr->data;
		t[i] = pt->jd;				
		x[i] = pt->ref_x;			
		y[i] = pt->ref_y;			
		i++;
	}
	m_MBData = CTrackingData(i, t, x, y);
	g_Project->SetTrackingData(m_MBData);
	g_free(x);
	g_free(y);
	g_free(t);
	return true;
}

//-----------------------   MASTER BIAS CONTEXT   ----------------------

//
// Constructor
//
CMasterBiasProc::CMasterBiasProc():m_Path(NULL), m_File(NULL)
{
	m_MBias = cmpack_mbias_init();
}


//
// Destructor
//
CMasterBiasProc::~CMasterBiasProc()
{
	cmpack_mbias_destroy(m_MBias);
	if (m_File)
		cmpack_ccd_destroy(m_File);
	g_free(m_Path);
}


//
// Initialization
//
bool CMasterBiasProc::Open(CConsole *pProgress, const gchar *fpath, GError **error)
{
	char txt[512];

	if (m_File) {
		cmpack_ccd_destroy(m_File);
		m_File = NULL;
	}
	g_free(m_Path);
	m_Path = NULL;

	// Output handling
	if (pProgress) {
		CmpackConsole *con = cmpack_con_init_cb(CConsole::OutputProc, pProgress);
		if (CConfig::GetBool(CConfig::DEBUG_OUTPUTS))
			cmpack_con_set_level(con, CMPACK_LEVEL_DEBUG);
		cmpack_mbias_set_console(m_MBias, con);
		cmpack_con_destroy(con);
	}

	sprintf(txt, "Output file: %s", fpath);
	pProgress->Print(txt);

	// Image data type
	CmpackBitpix bitpix = (CmpackBitpix)g_Project->Profile()->GetInt(CProfile::MBIAS_FORMAT);
	cmpack_mbias_set_bitpix(m_MBias, bitpix);

	// Frame border
	CmpackBorder border(g_Project->Profile()->GetBorder());
	cmpack_mbias_set_border(m_MBias, &border);

	// Threshold values
	double minvalue, maxvalue;
	minvalue = g_Project->Profile()->GetDbl(CProfile::BAD_PIXEL_VALUE);
	maxvalue = g_Project->Profile()->GetDbl(CProfile::OVEREXPOSED_VALUE);
	cmpack_mbias_set_thresholds(m_MBias, minvalue, maxvalue);

	// Output file
	CmpackCcdFile *outfile;
	gchar *f = g_locale_from_utf8(fpath, -1, NULL, NULL, NULL);
	int res = cmpack_ccd_open(&outfile, f, CMPACK_OPEN_CREATE, 0);
	g_free(f);
	if (res==0)
		res = cmpack_mbias_open(m_MBias, outfile);
	if (res!=0) {
		// Failed
		set_error(error, "Error when creating the file", fpath, res);
		cmpack_ccd_destroy(outfile);
		return false;
	}

	// OK
	m_File = outfile;
	m_Path = g_strdup(fpath);
	return true;
}


//
// Flat correction
//
bool CMasterBiasProc::Add(GtkTreePath *pPath, GError **error)
{
	// Check frame status
	unsigned state = g_Project->GetState(pPath);
	if ((state & CFILE_CONVERSION)==0) {
		const char *msg = "A working copy of the source files must be made, use the function \"Fetch/convert files\" first.";
		set_error(error, msg);
		g_Project->SetErrorMessage(pPath, msg);
		return false;
	}

	// Open source file 
	gchar *tpath = g_Project->GetImageFile(pPath);
	CmpackCcdFile *ccdfile;
	char *f = g_locale_from_utf8(tpath, -1, NULL, NULL, NULL);
	int res = cmpack_ccd_open(&ccdfile, f, CMPACK_OPEN_READONLY, 0);
	g_free(f);
	if (res==0) {
		res = cmpack_mbias_read(m_MBias, ccdfile);
		cmpack_ccd_destroy(ccdfile);
	}
	if (res!=0) {
		set_error(error, res);
		g_Project->SetErrorCode(pPath, res);
	}
	g_free(tpath);
	return (res==0);
}

//
// Finalization
//
bool CMasterBiasProc::Close(GError **error)
{
	int res = cmpack_mbias_close(m_MBias);
	if (res==0 && m_File) {
		res = cmpack_ccd_close(m_File);
		m_File = NULL;
	}
	if (res!=0) {
		// Failed
		set_error(error, "Error when creating the file", m_Path, res); 
	}
	if (m_File) {
		cmpack_ccd_destroy(m_File);
		m_File = NULL;
	}
	g_free(m_Path);
	m_Path = NULL;
	return (res==0);
}

//-----------------------   MASTER DARK CONTEXT   ----------------------

//
// Constructor
//
CMasterDarkProc::CMasterDarkProc():m_Path(NULL), m_File(NULL)
{
	m_MDark = cmpack_mdark_init();
}


//
// Destructor
//
CMasterDarkProc::~CMasterDarkProc()
{
	cmpack_mdark_destroy(m_MDark);
	if (m_File)
		cmpack_ccd_destroy(m_File);
	g_free(m_Path);
}


//
// Initialization
//
bool CMasterDarkProc::Open(CConsole *pProgress, const gchar *fpath, GError **error)
{
	char txt[512];

	if (m_File) {
		cmpack_ccd_destroy(m_File);
		m_File = NULL;
	}
	g_free(m_Path);
	m_Path = NULL;

	// Output handling
	if (pProgress) {
		CmpackConsole *con = cmpack_con_init_cb(CConsole::OutputProc, pProgress);
		if (CConfig::GetBool(CConfig::DEBUG_OUTPUTS))
			cmpack_con_set_level(con, CMPACK_LEVEL_DEBUG);
		cmpack_mdark_set_console(m_MDark, con);
		cmpack_con_destroy(con);
	}

	sprintf(txt, "Output file: %s", fpath);
	pProgress->Print(txt);

	// Scalable darks
	bool adv_calib = g_Project->Profile()->GetBool(CProfile::ADVANCED_CALIBRATION);
	cmpack_mdark_set_scalable(m_MDark, adv_calib);

	// Image data type
	CmpackBitpix bitpix = (CmpackBitpix)g_Project->Profile()->GetInt(CProfile::MDARK_FORMAT);
	cmpack_mdark_set_bitpix(m_MDark, bitpix);

	// Frame border
	CmpackBorder border(g_Project->Profile()->GetBorder());
	cmpack_mdark_set_border(m_MDark, &border);

	// Threshold values
	double minvalue, maxvalue;
	minvalue = g_Project->Profile()->GetDbl(CProfile::BAD_PIXEL_VALUE);
	maxvalue = g_Project->Profile()->GetDbl(CProfile::OVEREXPOSED_VALUE);
	cmpack_mdark_set_thresholds(m_MDark, minvalue, maxvalue);

	// Output file
	CmpackCcdFile *outfile;
	gchar *f = g_locale_from_utf8(fpath, -1, NULL, NULL, NULL);
	int res = cmpack_ccd_open(&outfile, f, CMPACK_OPEN_CREATE, 0);
	g_free(f);
	if (res==0)
		res = cmpack_mdark_open(m_MDark, outfile);
	if (res!=0) {
		// Failed
		set_error(error, "Error when creating the file", fpath, res);
		cmpack_ccd_destroy(outfile);
		return false;
	}

	// OK
	m_File = outfile;
	m_Path = g_strdup(fpath);
	return true;
}


//
// Flat correction
//
bool CMasterDarkProc::Add(GtkTreePath *pPath, GError **error)
{
	// Check frame status
	unsigned state = g_Project->GetState(pPath);
	if ((state & CFILE_CONVERSION)==0) {
		const char *msg = "A working copy of the source files must be made, use the function \"Fetch/convert files\" first.";
		set_error(error, msg);
		g_Project->SetErrorMessage(pPath, msg);
		return false;
	}

	// Open source file 
	gchar *tpath = g_Project->GetImageFile(pPath);
	CmpackCcdFile *ccdfile;
	char *f = g_locale_from_utf8(tpath, -1, NULL, NULL, NULL);
	int res = cmpack_ccd_open(&ccdfile, f, CMPACK_OPEN_READONLY, 0);
	g_free(f);
	if (res==0) {
		res = cmpack_mdark_read(m_MDark, ccdfile);
		cmpack_ccd_destroy(ccdfile);
	}
	if (res!=0) {
		set_error(error, res);
		g_Project->SetErrorCode(pPath, res);
	}
	g_free(tpath);
	return (res==0);
}

//
// Finalization
//
bool CMasterDarkProc::Close(GError **error)
{
	int res = cmpack_mdark_close(m_MDark);
	if (res==0 && m_File) {
		res = cmpack_ccd_close(m_File);
		m_File = NULL;
	}
	if (res!=0) {
		// Failed
		set_error(error, "Error when creating the file", m_Path, res); 
	}
	if (m_File) {
		cmpack_ccd_destroy(m_File);
		m_File = NULL;
	}
	g_free(m_Path);
	m_Path = NULL;
	return (res==0);
}

//-----------------------   MASTER FLAT CONTEXT   ----------------------

//
// Constructor
//
CMasterFlatProc::CMasterFlatProc():m_Path(NULL), m_File(NULL)
{
	m_MFlat = cmpack_mflat_init();
}


//
// Destructor
//
CMasterFlatProc::~CMasterFlatProc()
{
	cmpack_mflat_destroy(m_MFlat);
	if (m_File)
		cmpack_ccd_destroy(m_File);
	g_free(m_Path);
}


//
// Initialization
//
bool CMasterFlatProc::Open(CConsole *pProgress, const gchar *fpath, GError **error)
{
	char txt[512];

	if (m_File) {
		cmpack_ccd_destroy(m_File);
		m_File = NULL;
	}
	g_free(m_Path);
	m_Path = NULL;

	// Output handling
	if (pProgress) {
		CmpackConsole *con = cmpack_con_init_cb(CConsole::OutputProc, pProgress);
		if (CConfig::GetBool(CConfig::DEBUG_OUTPUTS))
			cmpack_con_set_level(con, CMPACK_LEVEL_DEBUG);
		cmpack_mflat_set_console(m_MFlat, con);
		cmpack_con_destroy(con);
	}

	sprintf(txt, "Output file: %s", fpath);
	pProgress->Print(txt);

	// Mean output level
	double level = g_Project->Profile()->GetDbl(CProfile::MFLAT_LEVEL);
	cmpack_mflat_set_level(m_MFlat, level);

	// Image data type
	CmpackBitpix bitpix = (CmpackBitpix)g_Project->Profile()->GetInt(CProfile::MFLAT_FORMAT);
	cmpack_mflat_set_bitpix(m_MFlat, bitpix);

	// Frame border
	CmpackBorder border(g_Project->Profile()->GetBorder());
	cmpack_mflat_set_border(m_MFlat, &border);

	// Threshold values
	double minvalue, maxvalue;
	minvalue = g_Project->Profile()->GetDbl(CProfile::BAD_PIXEL_VALUE);
	maxvalue = g_Project->Profile()->GetDbl(CProfile::OVEREXPOSED_VALUE);
	cmpack_mflat_set_thresholds(m_MFlat, minvalue, maxvalue);

	// Output file
	CmpackCcdFile *outfile;
	char *f = g_locale_from_utf8(fpath, -1, NULL, NULL, NULL);
	int res = cmpack_ccd_open(&outfile, f, CMPACK_OPEN_CREATE, 0);
	g_free(f);
	if (res==0)
		res = cmpack_mflat_open(m_MFlat, outfile);
	if (res!=0) {
		// Failed
		set_error(error, "Error when creating the file", fpath, res);
		cmpack_ccd_destroy(outfile);
		return false;
	}

	// OK
	m_File = outfile;
	m_Path = g_strdup(fpath);
	return true;
}


//
// Flat correction
//
bool CMasterFlatProc::Add(GtkTreePath *pPath, GError **error)
{
	// Check frame status
	unsigned state = g_Project->GetState(pPath);
	if ((state & CFILE_CONVERSION)==0) {
		const char *msg = "A working copy of the source files must be made, use the function \"Fetch/convert files\" first.";
		set_error(error, msg);
		g_Project->SetErrorMessage(pPath, msg);
		return false;
	}

	// Open source file 
	gchar *tpath = g_Project->GetImageFile(pPath);
	CmpackCcdFile *ccdfile;
	char *f = g_locale_from_utf8(tpath, -1, NULL, NULL, NULL);
	int res = cmpack_ccd_open(&ccdfile, f, CMPACK_OPEN_READONLY, 0);
	g_free(f);
	if (res==0) {
		res = cmpack_mflat_read(m_MFlat, ccdfile);
		cmpack_ccd_destroy(ccdfile);
	}
	if (res!=0) {
		set_error(error, res);
		g_Project->SetErrorCode(pPath, res);
	}
	g_free(tpath);
	return (res==0);
}

//
// Finalization
//
bool CMasterFlatProc::Close(GError **error)
{
	int res = cmpack_mflat_close(m_MFlat);
	if (res==0 && m_File) {
		res = cmpack_ccd_close(m_File);
		m_File = NULL;
	}
	if (res!=0) {
		// Failed
		set_error(error, "Error when creating the file", m_Path, res); 
	}
	if (m_File) {
		cmpack_ccd_destroy(m_File);
		m_File = NULL;
	}
	g_free(m_Path);
	m_Path = NULL;
	return (res==0);
}

//-----------------------   KOMBINE CONTEXT   ----------------------

//
// Constructor
//
CCombineProc::CCombineProc():m_File(NULL), m_OutIndex(0), m_Path(NULL), m_NoAlign(false)
{
	m_Kombine = cmpack_kombine_init();
}


//
// Destructor
//
CCombineProc::~CCombineProc()
{
	cmpack_kombine_destroy(m_Kombine);
	if (m_File)
		cmpack_ccd_destroy(m_File);
	g_free(m_Path);
}


//
// Initialization
//
bool CCombineProc::Open(CConsole *pProgress, const gchar *fpath, int outindex, bool noAlign, GError **error)
{
	char txt[512];

	if (m_File) {
		cmpack_ccd_destroy(m_File);
		m_File = NULL;
	}

	// Output handling
	if (pProgress) {
		CmpackConsole *con = cmpack_con_init_cb(CConsole::OutputProc, pProgress);
		if (CConfig::GetBool(CConfig::DEBUG_OUTPUTS))
			cmpack_con_set_level(con, CMPACK_LEVEL_DEBUG);
		cmpack_kombine_set_console(m_Kombine, con);
		cmpack_con_destroy(con);
	}

	m_OutIndex = outindex;
	m_NoAlign = noAlign;
	if (m_NoAlign)
		cmpack_kombine_set_flags(m_Kombine, CMPACK_KOMBINE_NOALIGN, TRUE);

	sprintf(txt, "Output file: %s", fpath);
	pProgress->Print(txt);

	// Image data type
	CmpackBitpix bitpix = (CmpackBitpix)g_Project->Profile()->GetInt(CProfile::KOMBINE_FORMAT);
	cmpack_kombine_set_bitpix(m_Kombine, bitpix);

	// Frame border
	CmpackBorder border(g_Project->Profile()->GetBorder());
	cmpack_kombine_set_border(m_Kombine, &border);

	// Threshold values
	double minvalue, maxvalue;
	minvalue = g_Project->Profile()->GetDbl(CProfile::BAD_PIXEL_VALUE);
	maxvalue = g_Project->Profile()->GetDbl(CProfile::OVEREXPOSED_VALUE);
	cmpack_kombine_set_thresholds(m_Kombine, minvalue, maxvalue);

	// Output file
	CmpackCcdFile *outfile;
	gchar *f = g_locale_from_utf8(fpath, -1, NULL, NULL, NULL);
	int res = cmpack_ccd_open(&outfile, f, CMPACK_OPEN_CREATE, 0);
	g_free(f);
	if (res==0)
		res = cmpack_kombine_open(m_Kombine, outfile);
	if (res!=0) {
		// Failed
		set_error(error, "Error when creating the file", fpath, res);
		cmpack_ccd_destroy(outfile);
		return false;
	}

	m_File = outfile;
	m_Path = g_strdup(fpath);
	return true;
}


//
// Flat correction
//
bool CCombineProc::Add(GtkTreePath *pPath, GError **error)
{
	int	res = 0;

	// Check frame status
	unsigned state = g_Project->GetState(pPath);
	if (!m_NoAlign) {
		if ((state & CFILE_MATCHING) == 0) {
			const char *msg = "The frame was not matched, use the function \"Match stars\" first.";
			set_error(error, msg);
			g_Project->SetErrorMessage(pPath, msg);
			return false;
		}

		// Open source image file 
		gchar *tpath = g_Project->GetImageFile(pPath);
		CmpackCcdFile *ccdfile;
		char *tf = g_locale_from_utf8(tpath, -1, NULL, NULL, NULL);
		res = cmpack_ccd_open(&ccdfile, tf, CMPACK_OPEN_READONLY, 0);
		g_free(tf);
		if (res == 0) {
			// Open source photometry file
			gchar *ppath = g_Project->GetPhotFile(pPath);
			if (ppath) {
				CmpackPhtFile *phtfile;
				char *pf = g_locale_from_utf8(ppath, -1, NULL, NULL, NULL);
				res = cmpack_pht_open(&phtfile, pf, CMPACK_OPEN_READONLY, 0);
				if (res == 0) {
					// Add frame to combined frame
					res = cmpack_kombine_read(m_Kombine, ccdfile, phtfile);
					cmpack_pht_destroy(phtfile);
				}
				g_free(pf);
				g_free(ppath);
			}
			cmpack_ccd_destroy(ccdfile);
		}
		g_free(tpath);
	}
	else {
		if ((state & CFILE_CONVERSION) == 0) {
			const char *msg = "A working copy of the source files must be made before the bias correction. Use the function \"Fetch/convert files\" first.";
			set_error(error, msg);
			g_Project->SetErrorMessage(pPath, msg);
			return false;
		}

		// Open source image file 
		gchar *tpath = g_Project->GetImageFile(pPath);
		CmpackCcdFile *ccdfile;
		char *tf = g_locale_from_utf8(tpath, -1, NULL, NULL, NULL);
		res = cmpack_ccd_open(&ccdfile, tf, CMPACK_OPEN_READONLY, 0);
		if (res == 0) {
			res = cmpack_kombine_read(m_Kombine, ccdfile, NULL);
			cmpack_ccd_destroy(ccdfile);
		}
		g_free(tf);
		g_free(tpath);
	}

	if (res!=0) {
		set_error(error, res);
		g_Project->SetErrorCode(pPath, res);
		return false;
	}

	// Success
	char txt[128];
	if (m_OutIndex>0)
		sprintf(txt, "Merging OK (output frame #%d)", m_OutIndex);
	else
		sprintf(txt, "Merging OK");
	g_Project->SetResult(pPath, txt, 0, 0, false, -1);
	return true;
}

//
// Finalization
//
bool CCombineProc::Close(GError **error)
{
	int res = cmpack_kombine_close(m_Kombine);
	if (res==0 && m_File) {
		res = cmpack_ccd_close(m_File);
		m_File = NULL;
	}
	if (res!=0) {
		// Failed
		set_error(error, "Error when creating the file", m_Path, res); 
	}
	if (m_File) {
		cmpack_ccd_destroy(m_File);
		m_File = NULL;
	}
	m_OutIndex = 0;
	g_free(m_Path);
	m_Path = NULL;
	return (res==0);
}

//-----------------------   MUNIFIND CLASS INTERFACE   ----------------------

CTable *CmpackMagDevCurve(CConsole *pProgress, const CFrameSet &fset, int aperture_id, 
	const CSelection &objs, double *jdmin, double *jdmax, double *magrange, int *auto_comp, GError **error)
{
	assert(fset.Valid());

	CmpackMuniFind *mfind = cmpack_mfind_init();

	// Output handling
	if (pProgress) {
		CmpackConsole *con = cmpack_con_init_cb(CConsole::OutputProc, pProgress);
		if (CConfig::GetBool(CConfig::DEBUG_OUTPUTS))
			cmpack_con_set_level(con, CMPACK_LEVEL_DEBUG);
		cmpack_mfind_set_console(mfind, con);
		cmpack_con_destroy(con);
	}

	// Set parameters
	cmpack_mfind_set_aperture(mfind, aperture_id);
	cmpack_mfind_set_threshold(mfind, g_Project->Profile()->GetDbl(CProfile::VARFIND_THRESHOLD));

	if (jdmin)
		*jdmin = 0;
	if (jdmax)
		*jdmax = 0;
	if (magrange)
		*magrange = 0;

	// Comparison star
	int ncomp = objs.CountStars(CMPACK_SELECT_COMP);
	if (ncomp==0) {
		// Comparison star autodetection
		int comp_star = -1;
		int res = cmpack_mfind_autocomp(mfind, fset.Handle(), &comp_star, CMPACK_MFIND_DEFAULT);
		if (res!=0) {
			set_error(error, res);
			cmpack_mfind_destroy(mfind);
			return NULL;
		}
		cmpack_mfind_set_comparison(mfind, comp_star);
		if (auto_comp)
			*auto_comp = comp_star;
	}
	else {
		int *list = (int*)g_malloc(ncomp * sizeof(int));
		int count = objs.GetStarList(CMPACK_SELECT_COMP, list, ncomp);
		for (int i = 0; i < count; i++)
			cmpack_mfind_add_comparison(mfind, list[i]);
		if (auto_comp)
			*auto_comp = -1;
		g_free(list);
	}

	// Create a table
	CmpackTable *tab;
	int res = cmpack_mfind(mfind, fset.Handle(), &tab, CMPACK_MFIND_DEFAULT);
	if (res!=0) {
		set_error(error, res);
		cmpack_mfind_destroy(mfind);
		return NULL;
	}

	CTable *table = new CTable(tab);
	cmpack_tab_destroy(tab);  // Release my reference
	if (jdmin && jdmax)
		cmpack_mfind_jdrange(mfind, fset.Handle(), jdmin, jdmax, CMPACK_MFIND_DEFAULT);
	if (magrange)
		cmpack_mfind_magrange(mfind, fset.Handle(), magrange, CMPACK_MFIND_DEFAULT);
	cmpack_mfind_destroy(mfind);
	return table;
}

//-----------------------   LIGHT CURVE CLASS INTERFACE   ----------------------

//
// Air mass coefficient computation
//
static bool CmpackAirMass(CConsole *pProgress, CFrameSet &fset, const CProfile& profile, const CObjectCoords &obj, 
	const CLocation &loc, GError **error)
{
	assert(fset.Valid());

	// Object coordinates
	double ra, dec;
	if (!obj.Valid()) {
		set_error(error, CMPACK_ERR_NO_OBJ_COORDS);
		return false;
	}
	if (cmpack_strtora(obj.RA(), &ra)!=0) {
		set_error(error, CMPACK_ERR_INVALID_RA);
		return false;
	}
	if (cmpack_strtodec(obj.Dec(), &dec)!=0) {
		set_error(error, CMPACK_ERR_INVALID_DEC);
		return false;
	}

	// Observer coordinates
	double lon, lat;
	int positiveWest = (profile.GetBool(CProfile::POSITIVE_WEST) ? 1 : 0);
	if (!loc.Valid()) {
		set_error(error, CMPACK_ERR_NO_OBS_COORDS);
		return false;
	}
	if (cmpack_strtolon(loc.Lon(), &lon)!=0) {
		set_error(error, CMPACK_ERR_INVALID_LON);
		return false;
	}
	if (cmpack_strtolat(loc.Lat(), &lat)!=0) {
		set_error(error, CMPACK_ERR_INVALID_LAT);
		return false;
	}

	CmpackConsole *con = NULL;
	if (pProgress) {
		con = cmpack_con_init_cb(CConsole::OutputProc, pProgress);
		if (CConfig::GetBool(CConfig::DEBUG_OUTPUTS))
			cmpack_con_set_level(con, CMPACK_LEVEL_DEBUG);
	}

	int res = cmpack_airmass_fset(fset.Handle(), obj.Name(), ra, dec,
		loc.Name(), lon, lat, con);
	if (con)
		cmpack_con_destroy(con);
	if (res!=0) 
		set_error(error, res);
	return (res==0);
}

//
// Heliocentric correction computation
//
static bool CmpackHelCorr(CConsole *pProgress, CFrameSet &fset, const CObjectCoords &obj, GError **error)
{
	assert(fset.Valid());

	// Object coordinates
	double ra, dec;
	if (!obj.Valid()) {
		set_error(error, CMPACK_ERR_NO_OBJ_COORDS);
		return false;
	}
	if (cmpack_strtora(obj.RA(), &ra)!=0) {
		set_error(error, CMPACK_ERR_INVALID_RA);
		return false;
	}
	if (cmpack_strtodec(obj.Dec(), &dec)!=0) {
		set_error(error, CMPACK_ERR_INVALID_DEC);
		return false;
	}

	// Output handling
	CmpackConsole *con = NULL;
	if (pProgress) {
		con = cmpack_con_init_cb(CConsole::OutputProc, pProgress);
		if (CConfig::GetBool(CConfig::DEBUG_OUTPUTS))
			cmpack_con_set_level(con, CMPACK_LEVEL_DEBUG);
	}

	int res = cmpack_helcorr_fset(fset.Handle(), obj.Name(), ra, dec, con);
	if (con)
		cmpack_con_destroy(con);
	if (res!=0) 
		set_error(error, res);
	return (res==0);
}

//
// Make light curve
//
CTable *CmpackLightCurve(CConsole *pProgress, CFrameSet &fset, const CProfile& profile, int aperture, const CSelection &sel,
	const CObjectCoords &obj, const CLocation &loc, CmpackLCurveFlags flags, GError **error)
{
	assert(fset.Valid());
	assert(sel.Count()>0 || (flags & CMPACK_LCURVE_ALLSTARS)!=0);

	// Update air mass coefficients and heliocentric correction
	if ((flags & (CMPACK_LCURVE_AIRMASS | CMPACK_LCURVE_ALTITUDE))!=0) {
		if (!CmpackAirMass(NULL, fset, profile, obj, loc, error))
			return NULL;
	}
	if ((flags & (CMPACK_LCURVE_HELCORR | CMPACK_LCURVE_HJD))!=0) {
		if (!CmpackHelCorr(NULL, fset, obj, error))
			return NULL;
	}

	// Set aperture
	CmpackLCurve *lc = cmpack_lcurve_init();
	cmpack_lcurve_set_aperture(lc, aperture);

	// Set selected stars
	int stars[MAX_SELECTION];
	int count = sel.GetStarList(CMPACK_SELECT_VAR, stars, MAX_SELECTION);
	cmpack_lcurve_set_var(lc, stars, count);
	count = sel.GetStarList(CMPACK_SELECT_COMP, stars, MAX_SELECTION);
	cmpack_lcurve_set_comp(lc, stars, count);
	count = sel.GetStarList(CMPACK_SELECT_CHECK, stars, MAX_SELECTION);
	cmpack_lcurve_set_check(lc, stars, count);

	// Output handling
	if (pProgress) {
		CmpackConsole *con = cmpack_con_init_cb(CConsole::OutputProc, pProgress);
		if (CConfig::GetBool(CConfig::DEBUG_OUTPUTS))
			cmpack_con_set_level(con, CMPACK_LEVEL_DEBUG);
		cmpack_lcurve_set_console(lc, con);
		cmpack_con_destroy(con);
	}

	// Make light curve
	CmpackTable *tab = NULL;
	int res = cmpack_lcurve(lc, fset.Handle(), &tab, flags);
	cmpack_lcurve_destroy(lc);
	if (res!=0) {
		set_error(error, res);
		return NULL;
	}

	// OK
	CTable *table = new CTable(tab);
	cmpack_tab_destroy(tab);  // Release my reference
	return table;
}

//-----------------------   LIGHT CURVE CLASS INTERFACE   ----------------------

//
// Make light curve
//
CTable *CmpackApDevCurve(CConsole *pProgress, const CFrameSet &fset, const CSelection &sel, GError **error)
{
	assert(fset.Valid());
	assert(sel.Count()>0);

	CmpackADCurve *lc = cmpack_adcurve_init();

	// Set selected stars
	int stars[MAX_SELECTION];
	int count = sel.GetStarList(CMPACK_SELECT_COMP, stars, MAX_SELECTION);
	cmpack_adcurve_set_comp(lc, stars, count);
	count = sel.GetStarList(CMPACK_SELECT_CHECK, stars, MAX_SELECTION);
	cmpack_adcurve_set_check(lc, stars, count);

	// Output handling
	if (pProgress) {
		CmpackConsole *con = cmpack_con_init_cb(CConsole::OutputProc, pProgress);
		if (CConfig::GetBool(CConfig::DEBUG_OUTPUTS))
			cmpack_con_set_level(con, CMPACK_LEVEL_DEBUG);
		cmpack_adcurve_set_console(lc, con);
		cmpack_con_destroy(con);
	}

	// Make table
	CmpackTable *tab = NULL;
	int res = cmpack_adcurve(lc, fset.Handle(), &tab, CMPACK_ADCURVE_DEFAULT);
	cmpack_adcurve_destroy(lc);
	if (res!=0) {
		set_error(error, res);
		return NULL;
	}

	// OK
	CTable *table = new CTable(tab);
	cmpack_tab_destroy(tab);  // Release my reference
	return table;
}

//-----------------------   TRACK CURVE CLASS INTERFACE   ----------------------

//
// Make track curve
//
CTable *CmpackTrackCurve(CConsole *pProgress, const CFrameSet &fset, GError **error)
{
	assert (fset.Valid());
	
	// Output handling
	CmpackConsole *con = NULL;
	if (pProgress) {
		con = cmpack_con_init_cb(CConsole::OutputProc, pProgress);
		if (CConfig::GetBool(CConfig::DEBUG_OUTPUTS))
			cmpack_con_set_level(con, CMPACK_LEVEL_DEBUG);
	}

	// Make table
	CmpackTable *tab = NULL;
	int res = cmpack_tcurve(fset.Handle(), &tab, CMPACK_TCURVE_FRAME_IDS, con);
	if (con)
		cmpack_con_destroy(con);
	if (res!=0) {
		set_error(error, res);
		return NULL;
	}

	// OK
	CTable *table = new CTable(tab);
	cmpack_tab_destroy(tab);
	return table;
}

//-----------------------   AIR MASS CLASS INTERFACE   ----------------------

//
// Make light curve
//
CTable *CmpackAirMassCurve(CConsole *pProgress, const CFrameSet &fset, const CProfile& profile,
	const CObjectCoords &obj, const CLocation &loc, GError **error)
{
	assert(fset.Valid());

	// Object coordinates
	double ra, dec;
	if (!obj.Valid()) {
		set_error(error, CMPACK_ERR_NO_OBJ_COORDS);
		return NULL;
	}
	if (cmpack_strtora(obj.RA(), &ra)!=0) {
		set_error(error, CMPACK_ERR_INVALID_RA);
		return NULL;
	}
	if (cmpack_strtodec(obj.Dec(), &dec)!=0) {
		set_error(error, CMPACK_ERR_INVALID_DEC);
		return NULL;
	}

	// Observer coordinates
	double lon, lat;
	int positiveWest = (profile.GetBool(CProfile::POSITIVE_WEST) ? 1 : 0);
	if (!loc.Valid()) {
		set_error(error, CMPACK_ERR_NO_OBS_COORDS);
		return NULL;
	}
	if (cmpack_strtolon2(loc.Lon(), positiveWest, &lon)!=0) {
		set_error(error, CMPACK_ERR_INVALID_LON);
		return NULL;
	}
	if (cmpack_strtolat(loc.Lat(), &lat)!=0) {
		set_error(error, CMPACK_ERR_INVALID_LAT);
		return NULL;
	}

	// Output handling
	CmpackConsole *con = NULL;
	if (pProgress) {
		con = cmpack_con_init_cb(CConsole::OutputProc, pProgress);
		if (CConfig::GetBool(CConfig::DEBUG_OUTPUTS))
			cmpack_con_set_level(con, CMPACK_LEVEL_DEBUG);
	}

	// Make curve
	CmpackTable *tab = NULL;
	int res = cmpack_airmass_curve(fset.Handle(), &tab, NULL, ra, dec, NULL, lon, lat,
		CMPACK_AMASS_FRAME_IDS, con);
	if (con)
		cmpack_con_destroy(con);
	if (res!=0) {
		set_error(error, res);
		return NULL;
	}

	// OK
	CTable *table = new CTable(tab);
	cmpack_tab_destroy(tab);
	return table;
}

//-----------------------   TEMPERATURE   ----------------------

//
// Temperatures
//
CTable *CmpackTemperatureCurve(CConsole *pProgress, const CFrameSet &fset, GError **error)
{
	static const int cols = CMPACK_FC_JULDAT | CMPACK_FC_CCDTEMP | CMPACK_FC_FRAME;

	assert(fset.Valid());
	
	// Output handling
	CmpackConsole *con = NULL;
	if (pProgress) {
		con = cmpack_con_init_cb(CConsole::OutputProc, pProgress);
		if (CConfig::GetBool(CConfig::DEBUG_OUTPUTS))
			cmpack_con_set_level(con, CMPACK_LEVEL_DEBUG);
	}

	CmpackTable *tab = NULL;
	int res = cmpack_fset_plot(fset.Handle(), &tab, CMPACK_TABLE_CCD_TEMP, 
		(CmpackFSetColumns)cols, 0, 0, 0, 0, 0, 0, 0, 0, con);
	if (con)
		cmpack_con_destroy(con);
	if (res!=0) {
		set_error(error, res);
		return NULL;
	}

	CTable *table = new CTable(tab);
	cmpack_tab_destroy(tab);
	return table;
}

//
// Object properties
//
CTable *CmpackObjectProperties(CConsole *pProgress, const CFrameSet &fset, int objectId, int apertureId, GError **error)
{
	static const int cols = CMPACK_FC_FRAME | CMPACK_FC_JULDAT | CMPACK_FC_CENTER | CMPACK_FC_SKY | CMPACK_FC_FWHM | CMPACK_FC_MAG;

	assert(fset.Valid());
	
	// Output handling
	CmpackConsole *con = NULL;
	if (pProgress) {
		con = cmpack_con_init_cb(CConsole::OutputProc, pProgress);
		if (CConfig::GetBool(CConfig::DEBUG_OUTPUTS))
			cmpack_con_set_level(con, CMPACK_LEVEL_DEBUG);
	}

	// Find aperture index, object index
	int object_index = cmpack_fset_find_object(fset.Handle(), objectId);
	int aperture_index = cmpack_fset_find_aperture(fset.Handle(), apertureId);

	// Create a table
	CmpackTable *tab = NULL;
	int res = cmpack_fset_plot(fset.Handle(), &tab, CMPACK_TABLE_OBJ_PROP, 
		(CmpackFSetColumns)cols, object_index, aperture_index, 0, 0, 0, 0, 0, 0, con);
	if (con)
		cmpack_con_destroy(con);
	if (res!=0) {
		set_error(error, res);
		return NULL;
	}

	// OK
	CTable *table = new CTable(tab);
	cmpack_tab_destroy(tab);
	return table;
}

//----------------------   NEW FILES   ----------------------------

// Constructor
CUpdateProc::CUpdateProc(void):m_Progress(NULL), m_Konv(NULL), m_TCor(NULL), m_Bias(NULL), 
	m_Dark(NULL), m_Flat(NULL), m_Phot(NULL), m_Match(NULL), m_Track(NULL)
{
}

// Destructor
CUpdateProc::~CUpdateProc()
{
	delete m_Konv;
	delete m_TCor;
	delete m_Bias;
	delete m_Dark;
	delete m_Flat;
	delete m_Phot;
	delete m_Match;
	delete m_Track;
}

bool CUpdateProc::Init(CConsole *pProgress, GError **error)
{
	int res = 0;
	bool do_konv, do_tcor, do_bias, do_dark, do_flat, do_phot, do_match;
	CProject::tStatus status;

	// Check project
	assert (g_Project->isOpen() && !g_Project->isReadOnly());

	m_Progress = pProgress;

	g_Project->GetStatus(&status);
	do_konv = status.converted>0;
	do_tcor = g_Project->GetDbl("TimeCorr", "Seconds", 0)!=0;
	do_bias = g_Project->GetOrigBiasFile()->Valid();
	do_dark = g_Project->GetOrigDarkFile()->Valid();
	do_flat = g_Project->GetTempFlatFile()->Valid();
	do_phot = status.photometred>0;
	do_match = g_Project->GetReferenceType()!=REF_UNDEFINED;
	if (do_konv) {
		m_Konv = new CConvertProc();
		m_Konv->Init(pProgress);
	}
	if (res==0 && do_tcor) {
		m_TCor = new CTimeCorrProc();
		m_TCor->Init(pProgress, g_Project->GetDbl("TimeCorr", "Seconds", 0), false);
	}
	if (res==0 && do_bias) {
		m_Bias = new CBiasCorrProc();
		if (!m_Bias->Init(pProgress, g_Project->GetOrigBiasFile()->FullPath(), error))
			return false;
	}
	if (res==0 && do_dark) {
		m_Dark = new CDarkCorrProc();
		if (!m_Dark->Init(pProgress, g_Project->GetOrigDarkFile()->FullPath(), error))
			return false;
	}
	if (res==0 && do_flat) {
		m_Flat = new CFlatCorrProc();
		if (!m_Flat->Init(pProgress, g_Project->GetOrigFlatFile()->FullPath(), error))
			return false;
	}
	if (res==0 && do_phot) {
		m_Phot = new CPhotometryProc();
		m_Phot->Init(pProgress);
	}
	if (res==0 && do_match) {
		if (g_Project->GetTargetType() != MOVING_TARGET) {
			m_Match = new CMatchingProc();
			if (!m_Match->Reinitialize(pProgress, error))
				return false;
		} else {
			m_Track = new CTrackingProc();
			if (!m_Track->Reinitialize(pProgress, error))
				return false;
		}
	}
	return true;
}

bool CUpdateProc::Execute(GtkTreePath *path, bool &try_again, GError **error)
{
	gchar msg[256];

	int state = g_Project->GetState(path);

	sprintf(msg, "Frame #%d:", g_Project->GetFrameID(path));
	m_Progress->Print(msg);

	bool ok = true;
	if (ok && m_Konv && (state & CFILE_CONVERSION)==0)
		ok = m_Konv->Execute(path, error);
	if (ok && m_TCor && (state & (CFILE_TIMECORR | CFILE_PHOTOMETRY))==0)
		ok = m_TCor->Execute(path, error);
	if (ok && m_Bias && (state & (CFILE_BIASCORR | CFILE_DARKCORR | CFILE_FLATCORR | CFILE_PHOTOMETRY))==0) 
		ok = m_Bias->Execute(path, error);
	if (ok && m_Dark && (state & (CFILE_DARKCORR | CFILE_FLATCORR | CFILE_PHOTOMETRY))==0) 
		ok = m_Dark->Execute(path, error);
	if (ok && m_Flat && (state & (CFILE_FLATCORR | CFILE_PHOTOMETRY))==0) 
		ok = m_Flat->Execute(path, error);
	if (ok && m_Phot && (state & CFILE_PHOTOMETRY)==0)
		ok = m_Phot->Execute(path, error);
	if (ok && m_Match && (state & CFILE_MATCHING)==0)
		ok = m_Match->Execute(path, error);
	if (ok && m_Track && (state & CFILE_MATCHING)==0)
		ok = m_Track->Execute(path, error);
	return ok;
}
