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

varfind_dlg.cpp (C-Munipack project)
The 'Find variables' dialog
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 "findvariables_dlg.h"
#include "configuration.h"
#include "main.h"
#include "ctxhelp.h"
#include "frameinfo_dlg.h"
#include "varfindfile_dlg.h"
#include "project_dlg.h"

enum tMenuId
{
	MENU_FILE = 1,
	MENU_VIEW,
	MENU_HELP
};

enum tCommandId
{
	CMD_CLOSE = 100,
	CMD_PRINT,
	CMD_REBUILD,
	CMD_SAVE_CHART,
	CMD_SAVE_MAGDEV,
	CMD_SAVE_LCURVE,
	CMD_SHOW_CHART,
	CMD_SHOW_IMAGE,
	CMD_SHOW_MIXED,
	CMD_EXPORT_DATA,
	CMD_EXPORT_MAGDEV,
	CMD_EXPORT_LCURVE,
	CMD_SHOW_TAGS,
	CMD_HELP
};

static const CMenuBar::tMenuItem FileMenu[] = {
	{ CMenuBar::MB_ITEM,	CMD_SAVE_MAGDEV,		"Save _mag-dev curve" },
	{ CMenuBar::MB_ITEM,	CMD_SAVE_CHART,			"Save _chart" },
	{ CMenuBar::MB_ITEM,	CMD_SAVE_LCURVE,		"Save _light curve" },
	{ CMenuBar::MB_SEPARATOR },
	{ CMenuBar::MB_ITEM,	CMD_EXPORT_DATA,		"Export varfind data" },
	{ CMenuBar::MB_ITEM,	CMD_EXPORT_MAGDEV,		"Export mag-dev curve as image" },
	{ CMenuBar::MB_ITEM,	CMD_EXPORT_LCURVE,		"Export light curve as image" },
	//{ CMenuBar::MB_ITEM,	CMD_PRINT,				"P_rint" },
	{ CMenuBar::MB_SEPARATOR },
	{ CMenuBar::MB_ITEM,	CMD_REBUILD,			"Rebuild" },
	{ CMenuBar::MB_SEPARATOR },
	{ CMenuBar::MB_ITEM,	CMD_CLOSE,				"_Close" },
	{ CMenuBar::MB_END }
};

static const CMenuBar::tMenuItem ViewMenu[] = {
	{ CMenuBar::MB_RADIOBTN, CMD_SHOW_IMAGE,		"_Image only" },
	{ CMenuBar::MB_RADIOBTN, CMD_SHOW_CHART,		"_Chart only" },
	{ CMenuBar::MB_RADIOBTN, CMD_SHOW_MIXED,		"Image and chart" },
	{ CMenuBar::MB_SEPARATOR },
	{ CMenuBar::MB_CHECKBTN, CMD_SHOW_TAGS,			"Tags for known variables" },
	{ CMenuBar::MB_END }
};

static const CMenuBar::tMenuItem HelpMenu[] = {
	{ CMenuBar::MB_ITEM,	CMD_HELP,				"_Show help", "help" },
	{ CMenuBar::MB_END }
};

static const CMenuBar::tMenu PreviewMenu2[] = {
	{ "_File",	MENU_FILE,	FileMenu },
	{ "_View",	MENU_VIEW,	ViewMenu },
	{ "_Help",	MENU_HELP,	HelpMenu },
	{ NULL }
};

//-------------------------   HELPER FUNCTIONS   --------------------------------

static gboolean foreach_all_files(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, GSList **list)
{
	if (g_Project->GetState(path) & CFILE_MATCHING)
		*list = g_slist_append(*list, gtk_tree_row_reference_new(model, path));
	return FALSE;
}

static void foreach_sel_files(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, GSList **list)
{
	if (g_Project->GetState(path) & CFILE_MATCHING)
		*list = g_slist_append(*list, gtk_tree_row_reference_new(model, path));
}

//-------------------------   VARFIND DIALOG   --------------------------------

//
// Constructor
//
CVarFindDlg::CVarFindDlg(void):m_FileList(NULL), m_Image(NULL), m_ValidDataSet(false)
{
	gchar buf[512];

	// Dialog caption
	sprintf(buf, "%s - %s", "Find variables", g_AppTitle);
	gtk_window_set_title(GTK_WINDOW(m_pDlg), buf);

	// Menu bar
	m_Menu.Create(PreviewMenu2, false);
	m_Menu.RegisterCallback(MenuCallback, this);
	gtk_box_pack_start(GTK_BOX(m_MainBox), m_Menu.Handle(), FALSE, FALSE, 0);

	// VarFind box
	m_VarFind.RegisterCallback(VarFindCallback, this);
	gtk_box_pack_start(GTK_BOX(m_MainBox), m_VarFind.Handle(), TRUE, TRUE, 0);
	
	// Show the dialog
	gtk_widget_show_all(m_MainBox);
}

//
// Destructor
//
CVarFindDlg::~CVarFindDlg(void)
{
	g_slist_foreach(m_FileList, (GFunc)gtk_tree_row_reference_free, NULL);
	g_slist_free(m_FileList);
	delete m_Image;
}

//
// Open a new dialog
//
bool CVarFindDlg::Open(GtkWindow *pParent, bool selected_files, CSelectionList *list)
{
	int res = 0;
	GtkTreePath *refpath;
	const gchar *tmp_file;

	bool positiveWest = g_Project->Profile()->GetBool(CProfile::POSITIVE_WEST);

	m_FrameSet.Clear();
	m_Phot.Clear();
	m_Frame.Close();
	delete m_Image;
	m_Image = NULL;
	m_Catalog.Clear();
	g_slist_foreach(m_FileList, (GFunc)gtk_tree_row_reference_free, NULL);
	g_slist_free(m_FileList);
	m_FileList = NULL;

	// Reference file
	switch (g_Project->GetReferenceType())
	{
	case REF_FRAME:
		refpath = g_Project->GetReferencePath();
		if (refpath) {
			gchar *pht_file = g_Project->GetPhotFile(refpath);
			if (pht_file) {
				GError *error = NULL;
				if (m_Phot.Load(pht_file, &error)) {
					m_VarFind.SetPhotometryFile(pParent, &m_Phot);
					gchar *fts_file = g_Project->GetImageFile(refpath);
					if (fts_file) {
						CCCDFile frame;
						if (frame.Open(fts_file, CMPACK_OPEN_READONLY, (!positiveWest ? 0 : CMPACK_OPENF_POSITIVE_WEST), &error)) {
							m_Image = frame.GetImageData();
							if (m_Image)
								m_VarFind.SetImage(m_Image);
							m_Frame.MakeCopy(frame);
						}
						g_free(fts_file);
					}
				} else {
					if (error) {
						ShowError(pParent, error->message);
						g_error_free(error);
					}
					res = -1;
				}
				g_free(pht_file);
			}
			gtk_tree_path_free(refpath);
		}
		break;

	case REF_CATALOG_FILE:
		tmp_file = g_Project->GetTempCatFile()->FullPath();
		if (tmp_file) {
			GError *error = NULL;
			if (m_Catalog.Load(tmp_file, &error)) {
				m_VarFind.SetCatalogFile(pParent, &m_Catalog);
				gchar *fts_file = SetFileExtension(tmp_file, FILE_EXTENSION_FITS);
				CCCDFile frame;
				if (frame.Open(fts_file, CMPACK_OPEN_READONLY, (!positiveWest ? 0 : CMPACK_OPENF_POSITIVE_WEST), &error)) {
					m_Image = frame.GetImageData();
					if (m_Image) 
						m_VarFind.SetImage(m_Image);
					m_Frame.MakeCopy(frame);
				}
				g_free(fts_file);
			} else {
				if (error) {
					ShowError(pParent, error->message);
					g_error_free(error);
				}
				res = -1;
			}
		}
		break;

	default:
		ShowError(pParent, "No reference file");
		res = -1;
	}
	if (res!=0)
		return false;

	if (!selected_files) {
		// All files
		gtk_tree_model_foreach(g_Project->FileList(), (GtkTreeModelForeachFunc)foreach_all_files, &m_FileList);
		if (!m_FileList) {
			ShowError(pParent, "There are no files in the project.");
			return false;
		}
	} else {
		// Selected files
		GtkTreeSelection *pSel = g_MainWnd->GetSelection();
		if (gtk_tree_selection_count_selected_rows(pSel)>0) 
			gtk_tree_selection_selected_foreach(pSel, (GtkTreeSelectionForeachFunc)foreach_sel_files, &m_FileList);
		if (!m_FileList) {
			ShowError(pParent, "There are no selected files.");
			return false;
		}
	}

	m_VarFind.SetApertures(*g_Project->Apertures(), g_Project->GetInt("MagDevCurve", "Aperture", 0));
	m_VarFind.SetSelectionList(list);
	return true;
}

//
// Dialog initialization
//
void CVarFindDlg::OnInitDialog(void)
{
	COutputDlg::OnInitDialog();

	RebuildFrameSet();
}

//
// Menu callback
//
void CVarFindDlg::MenuCallback(CCBObject *sender, int message, int wparam, void *lparam, void *cb_data)
{
	CVarFindDlg *pMe = (CVarFindDlg*)cb_data;

	switch (message)
	{
	case CMenuBar::CB_ACTIVATE:
		pMe->OnCommand(wparam);
		break;
	}
}

//
// Command handler
//
void CVarFindDlg::OnCommand(int cmd_id)
{
	switch (cmd_id)
	{
	// File menu
	case CMD_SAVE_CHART:
		m_VarFind.ExportChart();
		break;
	case CMD_SAVE_LCURVE:
		m_VarFind.SaveLightCurve();
		break;
	case CMD_SAVE_MAGDEV:
		m_VarFind.SaveMagDevCurve();
		break;
	case CMD_EXPORT_LCURVE:
		m_VarFind.ExportLightCurve();
		break;
	case CMD_EXPORT_MAGDEV:
		m_VarFind.ExportMagDevCurve();
		break;
	case CMD_EXPORT_DATA:
		ExportData();
		break;
	case CMD_CLOSE:
		Close();
		break;
	case CMD_REBUILD:
		UpdateFileList();
		RebuildFrameSet();
		break;

	// View menu
	case CMD_SHOW_CHART:
		m_VarFind.SetDisplayMode(CVarFind::DISPLAY_CHART);
		UpdateControls();
		break;
	case CMD_SHOW_IMAGE:
		m_VarFind.SetDisplayMode(CVarFind::DISPLAY_IMAGE);
		UpdateControls();
		break;
	case CMD_SHOW_MIXED:
		m_VarFind.SetDisplayMode(CVarFind::DISPLAY_MIXED);
		UpdateControls();
		break;
	case CMD_SHOW_TAGS:
		m_VarFind.SetTagsVisible(m_Menu.IsChecked(CMD_SHOW_TAGS));
		UpdateControls();
		break;

	// Help menu
	case CMD_HELP:
		g_MainWnd->ShowHelp(GTK_WINDOW(m_pDlg), IDH_FIND_VARIABLES);
		break;
	}
}

//
// Update frame list
//
void CVarFindDlg::UpdateFileList(void)
{
	g_slist_foreach(m_FileList, (GFunc)gtk_tree_row_reference_free, NULL);
	g_slist_free(m_FileList);
	m_FileList = NULL;

	g_Project->Lock();
	GtkTreeModel *frames = g_Project->FileList();
	gtk_tree_model_foreach(frames, (GtkTreeModelForeachFunc)foreach_all_files, &m_FileList);
	g_Project->Unlock();
}

//
// VarFind callback
//
void CVarFindDlg::VarFindCallback(CCBObject *sender, int message, int wparam, void *lparam, void *cb_data)
{
	CVarFindDlg *pMe = (CVarFindDlg*)cb_data;

	switch (message)
	{
	case CVarFind::CB_APERTURE_CHANGED:
		if (pMe->m_ValidDataSet)
			pMe->RebuildFrameSet();
		break;

	case CVarFind::CB_COMPARISON_CHANGED:
		if (pMe->m_ValidDataSet) {
			pMe->SaveSelection();
			pMe->UpdateMagDev();
			pMe->UpdateLightCurve();
			pMe->UpdateControls();
		}
		break;

	case CVarFind::CB_VARIABLE_CHANGED:
		if (pMe->m_ValidDataSet) {
			pMe->SaveSelection();
			pMe->UpdateLightCurve();
			pMe->UpdateControls();
		}
		break;

	case CVarFind::CB_ENABLE_CTRL_QUERY:
		*((bool*)lparam) = pMe->OnEnableCtrlQuery((CVarFind::tControlId)wparam);
		break;

	case CVarFind::CB_OBJECT_VALID_QUERY:
		*((bool*)lparam) = pMe->m_FrameSet.FindObject(wparam)>=0;
		break;

	case CVarFind::CB_REMOVE_FRAMES_FROM_DATASET:
		pMe->RemoveFramesFromDataSet();
		break;
	case CVarFind::CB_DELETE_FRAMES_FROM_PROJECT:
		pMe->DeleteFramesFromProject();
		break;
	case CVarFind::CB_SHOW_FRAME_INFO:
		pMe->ShowFrameInfo();
		break;
	case CVarFind::CB_SHOW_FRAME_PREVIEW:
		pMe->ShowFramePreview();
		break;
	case CVarFind::CB_REMOVE_OBJECTS_FROM_DATASET:
		pMe->RemoveObjectsFromDataSet();
		break;

	case CVarFind::CB_UPDATE_STATUS:
		pMe->SetStatus((char*)lparam);
		break;
	}
}

//
// Aperture change, rebuild data
//
void CVarFindDlg::RebuildFrameSet(void)
{
	int apertureId = m_VarFind.ApertureId();

	m_FrameSet.Clear();
	m_ValidDataSet = false;
	if (apertureId>=0) {
		if (m_Phot.Valid()) {
			CApertures aper;
			aper.Add(*m_VarFind.Aperture());
			m_FrameSet.Init(aper, m_Phot);
		} else 
		if (m_Catalog.Valid()) {
			CApertures aper;
			aper.Add(*m_VarFind.Aperture());
			m_FrameSet.Init(aper, m_Catalog);
		}
		g_Project->SetInt("LightCurve", "Aperture", apertureId);

		CProgressDlg pDlg(GTK_WINDOW(m_pDlg), "Processing files");
		pDlg.SetMinMax(0, g_slist_length(m_FileList));
		GError *error = NULL;
		m_ValidDataSet = pDlg.Execute(ExecuteProc, this, &error)!=0;
		g_Project->applyPendingUpdates();
		if (error) {
			ShowError(GTK_WINDOW(m_pDlg), error->message);
			g_error_free(error);
		}
	}

	m_VarFind.UpdateChart();
	UpdateMagDev();
	UpdateLightCurve();
	UpdateControls();
}

//
// Comparison changed
//
void CVarFindDlg::UpdateMagDev(void)
{
	int apertureId = m_VarFind.ApertureId();
	CSelection objs = m_VarFind.GetSelection();

	m_VarFind.SetMagDev(CTable());

	if (apertureId>=0) {
		double jdmin, jdmax, magrange;
		int comp_star;
		CTable *magdev = CmpackMagDevCurve(NULL, m_FrameSet, apertureId, objs, &jdmin, &jdmax, &magrange, &comp_star, NULL);
		if (magdev) {
			if (comp_star >= 0 && objs.CountStars(CMPACK_SELECT_COMP) == 0) {
				objs.Select(comp_star, CMPACK_SELECT_COMP);
				m_VarFind.SetSelection(objs);
			}
			m_VarFind.SetFixedJDRange(jdmin, jdmax);
			m_VarFind.SetFixedMagRange(magrange);
			m_VarFind.SetMagDev(*magdev);
			delete magdev;
		}
	}	
}

//
// Variable changed
//
void CVarFindDlg::UpdateLightCurve()
{
	int apertureId = m_VarFind.ApertureId();
	CSelection objs = m_VarFind.GetSelection();

	m_VarFind.SetLightCurve(CTable());
	if (objs.CountStars(CMPACK_SELECT_VAR)>0 && objs.CountStars(CMPACK_SELECT_COMP)>0 && apertureId>=0) {
		GError *error = NULL;
		CTable *lcurve = CmpackLightCurve(NULL, m_FrameSet, *g_Project->Profile(), apertureId, objs, CObjectCoords(), CLocation(), CMPACK_LCURVE_FRAME_IDS, &error);
		if (lcurve) {
			m_VarFind.SetLightCurve(*lcurve);
			delete lcurve;
		}
		else {
			if (error) {
				ShowError(GTK_WINDOW(m_pDlg), error->message, true);
				g_error_free(error);
			}
		}
	}
}

//
// Enable/disable controls
//
void CVarFindDlg::UpdateControls(void)
{
	int apertureId = m_VarFind.ApertureId();
	CSelection objs = m_VarFind.GetSelection();

	m_Menu.Enable(CMD_SHOW_IMAGE, m_Frame.Valid());
	m_Menu.Enable(CMD_SHOW_MIXED, m_Frame.Valid());
	m_Menu.Check(CMD_SHOW_IMAGE, m_VarFind.DisplayMode()==CVarFind::DISPLAY_IMAGE);
	m_Menu.Check(CMD_SHOW_CHART, m_VarFind.DisplayMode()==CVarFind::DISPLAY_CHART);
	m_Menu.Check(CMD_SHOW_MIXED, m_VarFind.DisplayMode()==CVarFind::DISPLAY_MIXED);
	m_Menu.Check(CMD_SHOW_TAGS, m_VarFind.TagsVisible());
	m_Menu.Enable(CMD_EXPORT_DATA, apertureId >=0);
	m_Menu.Enable(CMD_SAVE_MAGDEV, objs.CountStars(CMPACK_SELECT_COMP)>0 && apertureId >=0);
	m_Menu.Enable(CMD_SAVE_LCURVE, objs.CountStars(CMPACK_SELECT_VAR)>0 && objs.CountStars(CMPACK_SELECT_COMP)>0 && apertureId >=0);
	m_Menu.Enable(CMD_EXPORT_MAGDEV, objs.CountStars(CMPACK_SELECT_COMP)>0 && apertureId >=0);
	m_Menu.Enable(CMD_EXPORT_LCURVE, objs.CountStars(CMPACK_SELECT_VAR)>0 && objs.CountStars(CMPACK_SELECT_COMP)>0 && apertureId >=0);
}

//
// Is this control enabled?
//
bool CVarFindDlg::OnEnableCtrlQuery(CVarFind::tControlId ctrl)
{
	switch (ctrl)
	{
	case CVarFind::ID_SAVE_CHART:
		return m_Phot.Valid() || m_Catalog.Valid();
	case CVarFind::ID_SHOW_FRAME_PREVIEW:
	case CVarFind::ID_SHOW_FRAME_INFO:
	case CVarFind::ID_REMOVE_FROM_DATASET:
	case CVarFind::ID_DELETE_FROM_PROJECT:
		return true;
	default:
		return false;
	}
}

int CVarFindDlg::ExecuteProc(CProgressDlg *sender, void *userdata)
{
	return ((CVarFindDlg*)userdata)->OnProcessFiles(sender);
}

int CVarFindDlg::OnProcessFiles(CProgressDlg *sender)
{
	GtkTreePath	*path;
	char		msg[256];

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

	// Process files
	int infiles = 0, outfiles = 0;
	for (GSList *node = m_FileList; node != NULL && !sender->Cancelled(); node = node->next) {
		path = gtk_tree_row_reference_get_path((GtkTreeRowReference*)node->data);
		if (path) {
			int frame_id = g_Project->GetFrameID(path);
			char *tpath = g_Project->GetPhotFile(path);
			gchar *fname = g_path_get_basename(tpath);
			sender->SetFileName(fname);
			g_free(fname);
			sender->SetProgress(infiles++);
			sprintf(msg, "Frame #%d:", frame_id);
			sender->Print(msg);
			GError *error = NULL;
			if (m_FrameSet.AppendFrame(tpath, frame_id, &error)) {
				outfiles++;
			} else {
				sender->Print(error->message);
				g_error_free(error);
			}
			g_free(tpath);
			gtk_tree_path_free(path);
		}
	}
	if (sender->Cancelled()) {
		sender->Print("Cancelled at the user's request");
		return false;
	}

	sprintf(msg, "====== %d succeeded, %d failed ======", outfiles, infiles-outfiles);
	sender->Print(msg);
	return (outfiles>0);
}

void CVarFindDlg::ExportData(void)
{
	GtkFileFilter *filters[2];

	GtkWidget *pSaveDlg = gtk_file_chooser_dialog_new("Export varfind data",
		GTK_WINDOW(m_pDlg), GTK_FILE_CHOOSER_ACTION_SAVE, GTK_STOCK_CANCEL, 
		GTK_RESPONSE_CANCEL, GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT, NULL);
	gtk_file_chooser_standard_tooltips(GTK_FILE_CHOOSER(pSaveDlg));
	gtk_window_set_position(GTK_WINDOW(m_pDlg), GTK_WIN_POS_CENTER);
	gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(pSaveDlg), true);

	// File filters
	filters[0] = gtk_file_filter_new();
	gtk_file_filter_add_pattern(filters[0], "*.dat");
	gtk_file_filter_set_name(filters[0], "C-Munipack varfind files");
	gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(pSaveDlg), filters[0]);
	filters[1] = gtk_file_filter_new();
	gtk_file_filter_add_pattern(filters[1], "*");
	gtk_file_filter_set_name(filters[1], "All files");
	gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(pSaveDlg), filters[1]);
	gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(pSaveDlg), filters[0]);

	// Restore last folder and file name
	gchar *folder = g_Project->GetStr("Output", "Folder", NULL);
	if (!folder)
		folder = g_path_get_dirname(g_Project->Path());
	if (folder && g_file_test(folder, G_FILE_TEST_IS_DIR)) 
		gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(pSaveDlg), folder);
	g_free(folder);

	gchar *filename = g_Project->GetStr("VarFind", "FileName", "varfind.dat");
	if (filename) 
		gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(pSaveDlg), filename);
	g_free(filename);

	if (gtk_dialog_run(GTK_DIALOG(pSaveDlg))!=GTK_RESPONSE_ACCEPT) {
		gtk_widget_destroy(pSaveDlg);
		return;
	}
	gtk_widget_hide(pSaveDlg);

	gchar *fpath = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(pSaveDlg));

	// Save last folder and file name
	gchar *dirpath = g_path_get_dirname(fpath);
	g_Project->SetStr("Output", "Folder", dirpath);
	g_free(dirpath);

	gchar *basename = g_path_get_basename(fpath);
	g_Project->SetStr("VarFind", "FileName", basename);
	g_free(basename);
	
	// Export data in separate thread
	GError *error = NULL;
	if (m_FrameSet.Save(fpath, 0, &error)) {
		int res = 0;
		if (m_Phot.Valid()) {
			// Save reference CCD image
			gchar *dst_path = SetFileExtension(fpath, FILE_EXTENSION_CATALOG);
			CCatalog file;
			if (!file.Create(m_Phot, &error) || !file.SaveAs(dst_path, &error))
				res = -1;
			g_free(dst_path);
		} else 
		if (m_Catalog.Valid()) {
			// Copy catalog file
			gchar *dst_path = SetFileExtension(fpath, FILE_EXTENSION_CATALOG);
			if (!m_Catalog.SaveAs(dst_path, &error))
				res = -1;
			g_free(dst_path);
		}
		if (res==0 && m_Frame.Valid() && (m_Phot.Valid() || m_Catalog.Valid())) {
			// Save CCD frame 
			gchar *dst_path = SetFileExtension(fpath, FILE_EXTENSION_FITS);
			m_Frame.SaveAs(dst_path, &error);
			g_free(dst_path);
		}
	}
	if (error) {
		ShowError(GTK_WINDOW(m_pDlg), error->message, true);
		g_error_free(error);
	}
	g_free(fpath);
	gtk_widget_destroy(pSaveDlg);
}

void CVarFindDlg::RemoveFramesFromDataSet(void)
{
	bool changed = false;

	GList *rows = m_VarFind.GetSelectedFrames();
	if (rows) {
		for (GList *ptr=rows; ptr!=NULL; ptr=ptr->next) {
			int frame = (int)cmpack_graph_data_get_param(m_VarFind.LightCurveData(), 0, GPOINTER_TO_INT(ptr->data));
			m_FrameSet.DeleteFrame(frame);
			changed = true;
		}
		g_list_free(rows);
	}
	if (changed) {
		UpdateMagDev();
		UpdateLightCurve();
		UpdateControls();
	}
}

void CVarFindDlg::RemoveObjectsFromDataSet(void)
{
	bool changed = false;

	GList *rows = m_VarFind.GetSelectedObjects();
	if (rows) {
		for (GList *ptr=rows; ptr!=NULL; ptr=ptr->next) {
			int object = (int)cmpack_graph_data_get_param(m_VarFind.MagDevCurveData(), 0, GPOINTER_TO_INT(ptr->data));
			m_FrameSet.DeleteObject(object);
			changed = true;
		}
		g_list_free(rows);
	}
	if (changed) {
		m_VarFind.UpdateChart();
		UpdateMagDev();
		UpdateLightCurve();
		UpdateControls();
	}
}

void CVarFindDlg::DeleteFramesFromProject(void)
{
	bool ok, selected_ref;

	GList *rows = m_VarFind.GetSelectedFrames();
	if (rows) {
		int count = g_list_length(rows);
		selected_ref = false;
		for (GList *ptr=rows; ptr!=NULL; ptr=ptr->next) {
			int frame_id = (int)cmpack_graph_data_get_param(m_VarFind.LightCurveData(), 0, GPOINTER_TO_INT(ptr->data));
			if (frame_id == g_Project->GetReferenceFrame()) {
				selected_ref = true;
				break;
			}
		}
		if (selected_ref) {
			if (count==1) 
				ShowError(GTK_WINDOW(m_pDlg), "The selected frame is a reference frame. It it not allowed to remove it.");
			else
				ShowError(GTK_WINDOW(m_pDlg), "The selection includes a reference frame. It is not allowed to remove it.");
			ok = false;
		} else {
			if (count==1) 
				ok = ShowConfirmation(GTK_WINDOW(m_pDlg), "Do you want to remove the selected frame from the project?");
			else 
				ok = ShowConfirmation(GTK_WINDOW(m_pDlg), "Do you want to remove the selected frames from the project?");
		}
		if (ok) {
			for (GList *ptr=rows; ptr!=NULL; ptr=ptr->next) {
				int frame_id = (int)cmpack_graph_data_get_param(m_VarFind.LightCurveData(), 0, GPOINTER_TO_INT(ptr->data));
				m_FrameSet.DeleteFrame(frame_id);
				g_Project->RemoveFrame(frame_id);
			}
			g_Project->applyPendingUpdates();
			UpdateMagDev();
			UpdateLightCurve();
			UpdateControls();
		}
		g_list_free(rows);
	}
}

void CVarFindDlg::ShowFramePreview(void)
{
	int frame = m_VarFind.SelectedFrameID();
	if (frame>=0) {
		GtkTreePath *path = g_Project->GetPath(frame);
		if (path) {
			g_MainWnd->ShowFramePreview(path);
			gtk_tree_path_free(path);
		}
	}
}

void CVarFindDlg::ShowFrameInfo(void)
{
	int frame = m_VarFind.SelectedFrameID();
	if (frame>=0) {
		GtkTreePath *path = g_Project->GetPath(frame);
		if (path) {
			CFrameInfoDlg dlg(GTK_WINDOW(m_pDlg));
			dlg.Show(path);
			gtk_tree_path_free(path);
		}
	}
}

//
// Set selection to the project
//
void CVarFindDlg::SaveSelection(void)
{
	CSelection objs = m_VarFind.GetSelection();
	if (objs.CountStars(CMPACK_SELECT_VAR)>0 && objs.CountStars(CMPACK_SELECT_COMP)>0) 
		g_Project->SetLastSelection(objs);
}

//--------------------   MAKE VAR FIND   ----------------------------

CMakeVarFindDlg::CMakeVarFindDlg(GtkWindow *pParent):m_pParent(pParent), m_Reference(false), 
	m_Import(false)
{
	GtkWidget *vbox, *bbox;
	GSList *group;

	// Dialog with buttons
	m_pDlg = gtk_dialog_new_with_buttons("Find variables", pParent, 
		(GtkDialogFlags)(GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT),
		GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_OK, GTK_RESPONSE_ACCEPT, 
		GTK_STOCK_HELP, GTK_RESPONSE_HELP, NULL);
	gtk_dialog_widget_standard_tooltips(GTK_DIALOG(m_pDlg));
	gtk_window_set_position(GTK_WINDOW(m_pDlg), GTK_WIN_POS_CENTER);
	gtk_dialog_set_tooltip_by_response(GTK_DIALOG(m_pDlg), GTK_RESPONSE_ACCEPT, 
		"Use the entered values and continue");
	g_signal_connect(G_OBJECT(m_pDlg), "response", G_CALLBACK(response_dialog), this);

	// Dialog icon
	gchar *icon = get_icon_file("varfind");
	gtk_window_set_icon(GTK_WINDOW(m_pDlg), gdk_pixbuf_new_from_file(icon, NULL));
	g_free(icon);

	// Dialog layout
	vbox = gtk_vbox_new(FALSE, 4);
	gtk_widget_set_size_request(vbox, 600, -1);
	gtk_box_pack_start(GTK_BOX(GTK_DIALOG(m_pDlg)->vbox), vbox, TRUE, TRUE, 0);
	gtk_container_set_border_width(GTK_CONTAINER(vbox), 8);

	// Process
	GtkWidget *label = gtk_label_new(NULL);
	gtk_label_set_markup(GTK_LABEL(label), "<b>Process</b>");
	gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
	gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, TRUE, 0);
	m_AllBtn = gtk_radio_button_new_with_label(NULL, "all files in current project");
	gtk_widget_set_tooltip_text(m_AllBtn, "Include all frames in the current project");
	g_signal_connect(G_OBJECT(m_AllBtn), "toggled", G_CALLBACK(toggled), this);
	gtk_box_pack_start(GTK_BOX(vbox), m_AllBtn, TRUE, TRUE, 0);
	group = gtk_radio_button_get_group(GTK_RADIO_BUTTON(m_AllBtn));
	m_SelBtn = gtk_radio_button_new_with_label(group, "selected files only");
	gtk_widget_set_tooltip_text(m_SelBtn, "Include frames that are selected in the main window");
	g_signal_connect(G_OBJECT(m_SelBtn), "toggled", G_CALLBACK(toggled), this);
	gtk_box_pack_start(GTK_BOX(vbox), m_SelBtn, TRUE, TRUE, 0);

	// Separator
	gtk_box_pack_start(GTK_BOX(vbox), gtk_label_new(NULL), FALSE, TRUE, 0);

	label = gtk_label_new(NULL);
	gtk_label_set_markup(GTK_LABEL(label), "<b>External file</b>");
	gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
	gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, TRUE, 0);
	m_ImportBtn = gtk_check_button_new_with_label("Import data from an external file");
	gtk_widget_set_tooltip_text(m_ImportBtn, "Import data from an external file saved before by the Find variables tool");
	g_signal_connect(G_OBJECT(m_ImportBtn), "toggled", G_CALLBACK(toggled), this);
	gtk_box_pack_start(GTK_BOX(vbox), m_ImportBtn, TRUE, TRUE, 0);
	
	m_PathBox = gtk_hbox_new(FALSE, 8);
	gtk_box_pack_start(GTK_BOX(vbox), m_PathBox, TRUE, TRUE, 0);
	m_PathLabel = gtk_label_new("Path:");
	gtk_box_pack_start(GTK_BOX(m_PathBox), m_PathLabel, FALSE, TRUE, 0);
	m_Path = gtk_entry_new();
	gtk_widget_set_tooltip_text(m_Path, "Path to the source file");
	gtk_widget_set_size_request(m_Path, 360, -1);
	gtk_box_pack_start(GTK_BOX(m_PathBox), m_Path, TRUE, TRUE, 0);
	m_PathBtn = gtk_button_new_with_label("Browse");
	gtk_widget_set_tooltip_text(m_PathBtn, "Browse for file in a separate dialog");
	g_signal_connect(G_OBJECT(m_PathBtn), "clicked", G_CALLBACK(button_clicked), this);
	gtk_box_pack_start(GTK_BOX(m_PathBox), m_PathBtn, FALSE, TRUE, 0);
		
	// Separator
	gtk_box_pack_start(GTK_BOX(vbox), gtk_label_new(NULL), FALSE, TRUE, 0);

	// Options
	bbox = gtk_hbutton_box_new();
	gtk_button_box_set_layout(GTK_BUTTON_BOX(bbox), GTK_BUTTONBOX_START);
	gtk_container_add(GTK_CONTAINER(vbox), bbox);
	m_OptionsBtn = gtk_button_new_with_label("Options");
	gtk_widget_set_tooltip_text(m_OptionsBtn, "Edit project settings");
	gtk_box_pack_start(GTK_BOX(bbox), m_OptionsBtn, FALSE, TRUE, 0);
	g_signal_connect(G_OBJECT(m_OptionsBtn), "clicked", G_CALLBACK(button_clicked), this);

	gtk_widget_show_all(GTK_DIALOG(m_pDlg)->vbox);
}

CMakeVarFindDlg::~CMakeVarFindDlg()
{
	gtk_widget_destroy(m_pDlg);
}

void CMakeVarFindDlg::Execute(void)
{
	char *fpath, *fname, *path;
	const char *cpath;

	// Restore last settings
	m_Import = g_Project->GetBool("VarFind", "Import");
	fpath = g_Project->GetStr("Output", "Folder", NULL);
	fname = g_Project->GetStr("VarFind", "FileName", NULL);
	if (fpath && fname) {
		path = g_build_filename(fpath, fname, NULL);
		gtk_entry_set_text(GTK_ENTRY(m_Path), path);
		g_free(path);
	}
	g_free(fpath);
	g_free(fname);

	m_Reference = g_Project->GetReferenceType()!=REF_UNDEFINED;
	if (!m_Reference) {
		// Allow import only
		gtk_widget_set_sensitive(m_SelBtn, FALSE);
		gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(m_SelBtn), FALSE);
		gtk_widget_set_sensitive(m_AllBtn, FALSE);
		gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(m_AllBtn), FALSE);
		gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(m_ImportBtn), TRUE);
	} else {
		// Process frames or import data
		GtkTreeSelection *pSel = g_MainWnd->GetSelection();
		gtk_widget_set_sensitive(m_AllBtn, TRUE);
		gtk_widget_set_sensitive(m_SelBtn, gtk_tree_selection_count_selected_rows(pSel)>0);
		gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(m_ImportBtn), m_Import);
		if (gtk_tree_selection_count_selected_rows(pSel)>1)
			gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(m_SelBtn), TRUE);
		else
			gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(m_AllBtn), TRUE);
	}
	UpdateControls();

	// Show the dialog
	if (gtk_dialog_run(GTK_DIALOG(m_pDlg))!=GTK_RESPONSE_ACCEPT) {
		gtk_widget_hide(m_pDlg);
		return;
	}
	gtk_widget_hide(m_pDlg);

	g_Project->SetBool("VarFind", "Import", m_Import);
	if (m_Import) {
		// Save settings
		cpath = gtk_entry_get_text(GTK_ENTRY(m_Path));
		path = g_path_get_dirname(cpath);
		fname = g_path_get_basename(cpath);
		g_Project->SetStr("Output", "Folder", path);
		g_Project->SetStr("VarFind", "FileName", fname);
		g_free(path);
		g_free(fname);

		GError *error = NULL;
		CVarFindFileDlg *dlg = new CVarFindFileDlg();
		if (!dlg->Load(m_pParent, cpath, &error)) {
			if (error) {
				ShowError(m_pParent, error->message, true);
				g_error_free(error);
			}
			delete dlg;
			return;
		}
		dlg->Show();
	} else {
		CVarFindDlg *dlg = new CVarFindDlg();
		bool selected_frames = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(m_SelBtn))!=0;
		if (!dlg->Open(m_pParent, selected_frames, g_Project->SelectionList())) {
			delete dlg;
			return;
		}
		dlg->Show();
	}
}

void CMakeVarFindDlg::response_dialog(GtkDialog *pDlg, gint response_id, CMakeVarFindDlg *pMe)
{
	if (!pMe->OnResponseDialog(response_id))
		g_signal_stop_emission_by_name(pDlg, "response");
}

bool CMakeVarFindDlg::OnResponseDialog(gint response_id)
{
	switch (response_id)
	{
	case GTK_RESPONSE_ACCEPT:
		if (m_Import) {
			const char *path = gtk_entry_get_text(GTK_ENTRY(m_Path));
			if (!g_file_test(path, G_FILE_TEST_IS_REGULAR)) {
				ShowError(GTK_WINDOW(m_pDlg), "Please, click the Browse button and select a file.");
				return false;
			} 
			if (FileType(path)!=TYPE_VARFIND) {
				ShowError(GTK_WINDOW(m_pDlg), "Selected file is not a frame-set file.");
				return false;
			}
		}
		break;

	case GTK_RESPONSE_HELP:
		// Show context help
		g_MainWnd->ShowHelp(GTK_WINDOW(m_pDlg), IDH_MAKE_FIND_VARIABLES);
		return false;
	}
	return true;
}

void CMakeVarFindDlg::UpdateControls(void)
{
	gtk_widget_set_sensitive(m_AllBtn, !m_Import);
	gtk_widget_set_sensitive(m_SelBtn, !m_Import);
	gtk_widget_set_sensitive(m_PathLabel, m_Import);
	gtk_widget_set_sensitive(m_Path, m_Import);
	gtk_widget_set_sensitive(m_PathBtn, m_Import);
}

void CMakeVarFindDlg::toggled(GtkWidget *widget, CMakeVarFindDlg *pMe)
{
	pMe->OnToggled(widget);
}

void CMakeVarFindDlg::OnToggled(GtkWidget *widget)
{
	if (widget==m_ImportBtn) {
		m_Import = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget))!=0;
		if (!m_Reference && !m_Import)
			gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(widget), TRUE);
		UpdateControls();
	} else
	if (widget==m_SelBtn || widget==m_AllBtn) {
		m_Import = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget))==0;
		UpdateControls();
	}
}

void CMakeVarFindDlg::button_clicked(GtkWidget *widget, CMakeVarFindDlg *pMe)
{
	pMe->OnButtonClicked(widget);
}

void CMakeVarFindDlg::OnButtonClicked(GtkWidget *widget)
{
	if (widget==m_OptionsBtn)
		EditPreferences();
	else if (widget==m_PathBtn) 
		ChangeFilePath();
}

void CMakeVarFindDlg::ChangeFilePath(void)
{
	GtkWidget *pPathDlg = gtk_file_chooser_dialog_new("Select file",
		GTK_WINDOW(m_pDlg), GTK_FILE_CHOOSER_ACTION_OPEN, GTK_STOCK_CLOSE, 
		GTK_RESPONSE_REJECT, GTK_STOCK_OK, GTK_RESPONSE_ACCEPT, NULL);
	gtk_file_chooser_standard_tooltips(GTK_FILE_CHOOSER(pPathDlg));
	gtk_window_set_position(GTK_WINDOW(pPathDlg), GTK_WIN_POS_CENTER);
	const gchar *fpath = gtk_entry_get_text(GTK_ENTRY(m_Path));
	if (!gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(pPathDlg), fpath)) {
		gchar *dir = g_path_get_dirname(fpath);
		gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(pPathDlg), dir);
		g_free(dir);
	}
	if (gtk_dialog_run(GTK_DIALOG(pPathDlg)) == GTK_RESPONSE_ACCEPT) {
		gchar *path = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(pPathDlg));
		gtk_entry_set_text(GTK_ENTRY(m_Path), path);
		g_free(path);
		UpdateControls();
	}
	gtk_widget_destroy(pPathDlg);
}

void CMakeVarFindDlg::EditPreferences(void)
{
	CEditProjectDlg pDlg(GTK_WINDOW(m_pDlg));
	pDlg.Execute(PAGE_FIND_VARIABLES);
}
