// UserDefinedPlotDialog.cs created with MonoDevelop
// User: klose at 15:27 10.01.2011
// CVS release: $Id: UserDefinedPlotDialog.cs,v 1.2 2011-04-19 12:49:21 klose Exp $
//
//    QuickStep: Acquire, view and identify spectra 
//    Copyright (C) 2010  Thomas Klose, Hiperscan GmbH Dresden, Germany
//
//    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 3 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, see <http://www.gnu.org/licenses/>.
//

using System;
using System.IO;
using System.Text;
using System.Collections.Generic;
using System.CodeDom.Compiler;
using Gtk;
using Gdk;
using Glade;

using Mono.TextEditor;
using Mono.TextEditor.Highlighting;

using Hiperscan.Unix;
using Hiperscan.Spectroscopy;


namespace Hiperscan.Extensions.UserDefinedPlotType
{
	public class PlotTypeTreeView : Gtk.TreeView
	{
		public delegate void F2PressedHandler(TreeView tv);
		public event         F2PressedHandler F2Pressed;
		
		public PlotTypeTreeView(ListStore store) : base(store) {}

		protected override bool OnKeyPressEvent(Gdk.EventKey e)
		{
			if ((e.Key == Gdk.Key.F2) &&  (this.F2Pressed != null))
				this.F2Pressed(this);
			
			return base.OnKeyPressEvent(e);
		}
	}
	
	public class UserDefinedPlotDialog
	{
		[Widget] Gtk.Dialog user_defined_plot_dialog = null;
		
		[Widget] Gtk.Alignment plot_type_tv_align = null;
		
		[Widget] Gtk.ComboBox spectrum_combo  = null;
		[Widget] Gtk.ComboBox spectrum0_combo = null;
		
		[Widget] Gtk.ToolButton remove_button = null;
		[Widget] Gtk.ToolButton save_button   = null;
		[Widget] Gtk.ToolButton up_button     = null;
		[Widget] Gtk.ToolButton down_button   = null;
		
		[Widget] Gtk.Table plot_type_table = null;
		[Widget] Gtk.HBox  definition_hbox = null;
		
		[Widget] Gtk.Entry xlabel_entry = null;
		[Widget] Gtk.Entry ylabel_entry = null;
		
		[Widget] Gtk.SpinButton lower_limit_spin  = null;
		[Widget] Gtk.SpinButton upper_limit_spin  = null;
		[Widget] Gtk.SpinButton standard_min_spin = null;
		[Widget] Gtk.SpinButton standard_max_spin = null;
		
		[Widget] Gtk.CheckButton absorbance_checkbutton      = null;
		[Widget] Gtk.CheckButton interdependence_checkbutton = null;
		
		[Widget] Gtk.Notebook definition_notebook = null;
		[Widget] Gtk.Entry function_entry = null;
		[Widget] Gtk.ScrolledWindow scrolledwindow = null;
		
		[Widget] Gtk.Image simple_ok_image   = null;
		[Widget] Gtk.Image advanced_ok_image = null;
		
		private TreeIter current_plot_type_iter = TreeIter.Zero;
		private TextEditor text_editor = null;
		private PlotTypeTreeView plot_type_tv = null;
		private ListStore list_store = null;
		private ScrolledWindow plot_type_sw = null;
		private TreeViewColumn column = null;
		private CellRendererText cellrenderer_text = null;

		private volatile bool need_discard_changes_warning = false;
		
		
		public UserDefinedPlotDialog()
		{
			Glade.XML gui = new Glade.XML(null, "UserDefinedPlotType.UserDefinedPlotType.glade", "user_defined_plot_dialog", "Hiperscan");
			gui.Autoconnect(this);
			
			this.user_defined_plot_dialog.TransientFor = Plugin.host.MainWindow;
			this.user_defined_plot_dialog.DefaultHeight = 600;
			
			this.function_entry.Changed += this.OnFunctionTextChanged;
			
			this.text_editor = new TextEditor();
			this.text_editor.Document.SyntaxMode = SyntaxModeService.GetSyntaxMode ("text/x-csharp");
			this.text_editor.Document.LineChanged += delegate(object sender, LineEventArgs e) {
				this.advanced_ok_image.Hide();
			};
			this.scrolledwindow.Add(this.text_editor);
			
			this.user_defined_plot_dialog.DeleteEvent += this.OnDeleteEvent;

			this.InitializePlotTypeCombo();
			this.Update();
			
			this.user_defined_plot_dialog.ShowAll();
		}
		
		private void InitializePlotTypeCombo()
		{
			TreeIter iter;

			this.list_store = new ListStore(typeof(bool), typeof(string), typeof(UserDefinedPlotTypeInfo));
			this.plot_type_tv = new PlotTypeTreeView(this.list_store);
			this.plot_type_tv.F2Pressed += this.OnF2Pressed;
			this.plot_type_tv.Selection.Mode = Gtk.SelectionMode.Multiple;
			this.plot_type_sw = new ScrolledWindow();
			this.plot_type_sw.Add(this.plot_type_tv);
			this.plot_type_tv_align.Add(this.plot_type_sw);
			
			this.plot_type_tv.Selection.Changed += this.OnPlotTypeTvSelectionChanged;

			CellRendererToggle toggle = new CellRendererToggle();
			toggle.Toggled += delegate(object sender, ToggledArgs args) {
				if (this.list_store.GetIterFromString(out iter, args.Path))
				{
					bool val = (bool)this.list_store.GetValue(iter, 0);
					UserDefinedPlotTypeInfo plot_type = (UserDefinedPlotTypeInfo)this.list_store.GetValue(iter, 2);
					
					this.list_store.SetValue(iter, 0, !val);
					plot_type.Active = !val;
				}
			};
				
			this.column = new TreeViewColumn(string.Empty, toggle, "active", 0);
			this.column.Widget = new Gtk.Image(Stock.Execute, IconSize.Menu);
			this.column.Widget.Show();

			this.plot_type_tv.RulesHint = false;
			this.plot_type_tv.AppendColumn(this.column);
			
			this.cellrenderer_text = new CellRendererText();
			this.cellrenderer_text.Editable = true;
			this.cellrenderer_text.Edited += this.OnNameEdited;
			this.cellrenderer_text.EditingStarted += delegate {
				this.plot_type_table.Sensitive = false;
				this.definition_hbox.Sensitive = false;
			};
			this.cellrenderer_text.EditingCanceled += delegate {
				this.plot_type_table.Sensitive = true;
				this.definition_hbox.Sensitive = true;
			};
			this.cellrenderer_text.Edited += delegate {
				this.plot_type_table.Sensitive = true;
				this.definition_hbox.Sensitive = true;
			};
			this.plot_type_tv.AppendColumn(Catalog.GetString("Name"), this.cellrenderer_text, "text", 1);
			
			foreach (UserDefinedPlotTypeInfo plot_type in Plugin.plot_type_infos)
			{
				this.list_store.AppendValues(plot_type.Active,
				                             plot_type.Name.Substring(1),
				                             plot_type);
			}
		}
		
		private void Update()
		{
			if (this.current_plot_type_iter.Equals(TreeIter.Zero))
			{
				bool val = this.plot_type_tv.Selection.CountSelectedRows() > 0;
				this.remove_button.Sensitive   = val;
				this.save_button.Sensitive     = val;
				this.up_button.Sensitive       = false;
				this.down_button.Sensitive     = false;
				this.plot_type_table.Sensitive = false;
				this.definition_hbox.Sensitive = false;
				this.text_editor.Document.Text = string.Empty;
				return;
			}

			TreeIter first_iter, last_iter;
			this.list_store.GetIterFirst(out first_iter);
			int n = this.list_store.IterNChildren();
			this.list_store.IterNthChild(out last_iter, n-1);
			
			this.up_button.Sensitive   = !this.current_plot_type_iter.Equals(first_iter);
			this.down_button.Sensitive = !this.current_plot_type_iter.Equals(last_iter);
			
			this.remove_button.Sensitive   = true;
			this.save_button.Sensitive     = true;
			this.plot_type_table.Sensitive = true;
			this.definition_hbox.Sensitive = true;
			
			this.need_discard_changes_warning = true;
			
			UserDefinedPlotTypeInfo info = (UserDefinedPlotTypeInfo)this.list_store.GetValue(this.current_plot_type_iter, 2);
			
			this.xlabel_entry.Text = info.XLabel;
			this.ylabel_entry.Text = info.YLabel;
			
			this.lower_limit_spin.Value  = info.LowerLimit;
			this.upper_limit_spin.Value  = info.UpperLimit;
			this.standard_min_spin.Value = info.StandardMin;
			this.standard_max_spin.Value = info.StandardMax;
			
			this.spectrum_combo.Active  = (int)info.SpectrumType;
			this.spectrum0_combo.Active = (int)info.Spectrum0Type;

			this.definition_notebook.CurrentPage = (int)info.DefinitionType;
			this.absorbance_checkbutton.Active      = info.NeedAbsorbance;
			this.interdependence_checkbutton.Active = info.Interdependent;
			this.UpdateSimpleControls();
			
			this.function_entry.Text = info.Function;
			this.text_editor.Document.Text = info.Code;
			this.text_editor.Document.UpdateHighlighting();
		}
		
		private void UpdateSimpleControls()
		{
			if (this.definition_notebook.CurrentPage == (int)DefinitionType.Simple)
			{
				this.absorbance_checkbutton.Sensitive = false;
				this.interdependence_checkbutton.Sensitive = false;
				this.xlabel_entry.Sensitive = false;
				this.absorbance_checkbutton.Active = (this.spectrum_combo.Active == (int)SpectrumType.Absorbance);
				this.interdependence_checkbutton.Active = this.function_entry.Text.Contains("spectrum0");
			}
			else
			{
				this.absorbance_checkbutton.Sensitive = true;
				this.interdependence_checkbutton.Sensitive = true;
				this.xlabel_entry.Sensitive = true;
			}
		}
		
		private void UpdateCurrentPlotType()
		{
			if (this.current_plot_type_iter.Equals(TreeIter.Zero))
				return;
			
			UserDefinedPlotTypeInfo plot_type = (UserDefinedPlotTypeInfo)this.list_store.GetValue(this.current_plot_type_iter, 2);
			
			plot_type.XLabel = this.xlabel_entry.Text;
			plot_type.YLabel = this.ylabel_entry.Text;
			plot_type.LowerLimit = this.lower_limit_spin.Value;
			plot_type.UpperLimit = this.upper_limit_spin.Value;
			plot_type.StandardMin = this.standard_min_spin.Value;
			plot_type.StandardMax = this.standard_max_spin.Value;
			plot_type.NeedAbsorbance = this.absorbance_checkbutton.Active;
			plot_type.Interdependent = this.interdependence_checkbutton.Active;
			plot_type.DefinitionType = (DefinitionType)this.definition_notebook.CurrentPage;
			plot_type.SpectrumType = (SpectrumType)this.spectrum_combo.Active;
			plot_type.Spectrum0Type = (Spectrum0Type)this.spectrum0_combo.Active;
			plot_type.Function = this.function_entry.Text;
			plot_type.Code = this.text_editor.Document.Text;
		}
		
		internal void OnPlotTypeTvSelectionChanged(object o, EventArgs e)
		{
			if (this.plot_type_tv.Selection.CountSelectedRows() != 1)
			{
				this.current_plot_type_iter = TreeIter.Zero;
				this.Update();
				return;
			}

			if (!this.current_plot_type_iter.Equals(TreeIter.Zero) && 
			    this.plot_type_tv.Selection.IterIsSelected(this.current_plot_type_iter))
			    return;
			
			if (!this.ApplyChanges())
			{
				this.plot_type_tv.Selection.SelectIter(this.current_plot_type_iter);
				return;
			}
			
			TreePath[] paths = this.plot_type_tv.Selection.GetSelectedRows();
			TreeIter iter;
			this.list_store.GetIterFromString(out iter, paths[0].ToString());
			this.current_plot_type_iter = iter;
			this.Update();
		}
		
		private void OnF2Pressed(TreeView tv)
		{
			if (tv.Selection.CountSelectedRows() != 1)
				return;
			
			TreeIter iter;
			TreeModel model;
			TreePath[] paths = tv.Selection.GetSelectedRows(out model);
			if (this.list_store.GetIterFromString(out iter, paths[0].ToString()))
			{
				tv.SetCursorOnCell(paths[0], tv.Columns[1], this.cellrenderer_text, true);
			}
		}

		internal void OnAddButtonClicked(object o, EventArgs e)
		{
			
			string name = Catalog.GetString("New plot type");
			List<string> names = new List<string>();
			foreach (UserDefinedPlotTypeInfo pt in Plugin.plot_type_infos)
			{
				names.Add(pt.Name.Substring(1));
			}
			int ix = 0;
			while (names.Contains(name))
			{
				if (ix == 0)
					name += " " + (ix += 2).ToString();
				else
					name = name.Replace(ix.ToString(), (++ix).ToString());
			}

			UserDefinedPlotTypeInfo plot_type = new UserDefinedPlotTypeInfo(name);
			Plugin.plot_type_infos.Add(plot_type);
			TreeIter iter = this.list_store.AppendValues(plot_type.Active,
			                                             plot_type.Name.Substring(1),
			                                             plot_type);
			this.plot_type_tv.Selection.UnselectAll();
			this.plot_type_tv.Selection.SelectIter(iter);
			this.current_plot_type_iter = iter;
			
			TreePath path = this.list_store.GetPath(iter);
			this.plot_type_tv.ScrollToCell(path, this.column, false, 0f, 0f);
			this.plot_type_tv.SetCursorOnCell(path, this.plot_type_tv.Columns[1], this.cellrenderer_text, true);
		}
		
		internal void OnRemoveButtonClicked(object o, EventArgs e)
		{
			bool changed = false;
			
			List<TreeIter> iters = new List<TreeIter>();
			foreach (TreePath path in this.plot_type_tv.Selection.GetSelectedRows())
			{
				TreeIter iter;
				this.list_store.GetIterFromString(out iter, path.ToString());
				iters.Add(iter);
			}
			
			foreach (TreeIter iter in iters)
			{
				TreeIter rm_iter = iter;
				
				UserDefinedPlotTypeInfo plot_type = (UserDefinedPlotTypeInfo)this.list_store.GetValue(rm_iter, 2);
				Plugin.plot_type_infos.Remove(plot_type);
				
				changed = true;
				
				this.list_store.Remove(ref rm_iter);

				if (this.list_store.IterNChildren() == 1)
					this.current_plot_type_iter = TreeIter.Zero;
			}
			
			if (!changed)
				return;
		
			Plugin.host.UpdatePluginInfos();
			this.Update();
		}
		
		internal void OnLoadButtonClicked(object o, EventArgs e)
		{
			try
			{
				using (FileChooserDialog chooser = new FileChooserDialog(Catalog.GetString("Choose file to open"),
				                                                         this.user_defined_plot_dialog,
				                                                         FileChooserAction.Open,
				                                                         Stock.Open,
				                                                         ResponseType.Ok,
				                                                         Stock.Cancel,
				                                                         ResponseType.Cancel))
				                                                         
				{
					FileFilter filter = new FileFilter();
					filter.Name = Catalog.GetString("User-defined plot type definition (xml)");
					filter.AddPattern("*.xml");
					chooser.AddFilter(filter);
					
					chooser.SetCurrentFolder(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments));
					
					ResponseType response = (ResponseType)chooser.Run();
					chooser.Hide();
					
					if (response != ResponseType.Ok)
						return;
					
					UserDefinedPlotTypeInfo[] plot_types = ReadDefinitionFile(chooser.Filename);
					
					foreach (UserDefinedPlotTypeInfo plot_type in plot_types)
					{
						plot_type.Active = true;
						Plugin.plot_type_infos.Add(plot_type);
						this.list_store.AppendValues(plot_type.Active,
						                             plot_type.Name.Substring(1),
						                             plot_type);
					}
				}
			}
			catch (Exception ex)
			{
				Plugin.host.ShowInfoMessage(this.user_defined_plot_dialog,
				                            Catalog.GetString("Cannot load user-defined plot type definitions: {0}"),
				                            ex.Message);
			}
			
			Plugin.host.UpdatePluginInfos();
		}
		
		internal void OnSaveButtonClicked(object o, EventArgs e)
		{
			List<string> plot_type_names = new List<string>();
			
			foreach (TreePath path in this.plot_type_tv.Selection.GetSelectedRows())
			{
				TreeIter iter;
				this.list_store.GetIterFromString(out iter, path.ToString());
				UserDefinedPlotTypeInfo plot_type = (UserDefinedPlotTypeInfo)this.list_store.GetValue(iter, 2);
				plot_type_names.Add(plot_type.Name);
			}
			
			try
			{
				using (FileChooserDialog chooser = new FileChooserDialog(Catalog.GetString("Choose file name"),
				                                                         this.user_defined_plot_dialog,
				                                                         FileChooserAction.Save,
				                                                         Stock.Save,
				                                                         ResponseType.Ok,
				                                                         Stock.Cancel,
				                                                         ResponseType.Cancel))
				                                                         
				{
					FileFilter filter = new FileFilter();
					filter.Name = Catalog.GetString("User-defined plot type definition (xml)");
					filter.AddPattern("*.xml");
					chooser.AddFilter(filter);
					
					chooser.SetCurrentFolder(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments));
					chooser.DoOverwriteConfirmation = true;
					
					if (plot_type_names.Count == 1)
						chooser.CurrentName = plot_type_names[0].Substring(1) + ".xml";
					else
						chooser.CurrentName = Catalog.GetString("user_defined_plot_definitions.xml");
					
					ResponseType response = (ResponseType)chooser.Run();
					chooser.Hide();
					
					if (response != ResponseType.Ok)
						return;
					
					WriteDefinitionFile(chooser.Filename, plot_type_names);
				}
			}
			catch (Exception ex)
			{
				Plugin.host.ShowInfoMessage(this.user_defined_plot_dialog,
				                            Catalog.GetString("Cannot save user-defined plot type definitions: {0}"),
				                            ex.Message);
			}
		}
		
		internal void OnDefinitionNotebookSwitchPage(object o, SwitchPageArgs e)
		{
			this.UpdateSimpleControls();
		}
		
		internal void OnSpectrumComboChanged(object o, EventArgs e)
		{
			if (this.definition_notebook.CurrentPage == (int)DefinitionType.Simple)
				this.absorbance_checkbutton.Active = (this.spectrum_combo.Active == (int)SpectrumType.Absorbance);
		}
		
		internal void OnFunctionTextChanged(object o, EventArgs e)
		{
			this.simple_ok_image.Hide();
			
			if (this.definition_notebook.CurrentPage == (int)DefinitionType.Simple)
				this.interdependence_checkbutton.Active = this.function_entry.Text.Contains("spectrum0");
		}
		
		internal void OnCompileClicked(object o, EventArgs e)
		{
			try
			{
				this.Compile(true);
			}
			catch (Exception ex)
			{
				Plugin.host.ShowInfoMessage(this.user_defined_plot_dialog, 
				                            Catalog.GetString("Cannot compile user-defined plot definition: {0}"),
				                            ex.Message);
			}
		}
		
		internal void OnUpButtonClicked(object o, EventArgs e)
		{
			TreeIter iter = this.current_plot_type_iter;
			
			TreeIter before = TreeIter.Zero;
			this.list_store.Foreach(delegate(TreeModel _model, TreePath _path, TreeIter _iter) {
				TreeIter next = _iter;
				if (!this.list_store.IterNext(ref next))
					return false;
				if (next.Equals(iter))
				{
					before = _iter;
					return true;
				}
				return false;
			});
			
			this.list_store.Swap(iter, before);
		
			UserDefinedPlotTypeInfo plot_type = (UserDefinedPlotTypeInfo)this.list_store.GetValue(iter, 2);
			int ix = Plugin.plot_type_infos.IndexOf(plot_type);
			Plugin.plot_type_infos.RemoveAt(ix);
			Plugin.plot_type_infos.Insert(ix-1, plot_type);

			this.Update();
		}
		
		internal void OnDownButtonClicked(object o, EventArgs e)
		{
			TreeIter iter = this.current_plot_type_iter;
			
			TreeIter next = iter;
			if (!this.list_store.IterNext(ref next))
				return;
				
			this.list_store.Swap(iter, next);

			UserDefinedPlotTypeInfo plot_type = (UserDefinedPlotTypeInfo)this.list_store.GetValue(iter, 2);
			int ix = Plugin.plot_type_infos.IndexOf(plot_type);
			Plugin.plot_type_infos.RemoveAt(ix);
			Plugin.plot_type_infos.Insert(ix+1, plot_type);

			this.Update();
		}

		private void OnNameEdited(object o, EditedArgs args)
		{
			TreeIter iter;
			this.list_store.GetIter(out iter, new Gtk.TreePath(args.Path));
			
			bool exists = false;
			foreach (UserDefinedPlotTypeInfo pt in Plugin.plot_type_infos)
			{
				if (args.NewText == pt.Name.Substring(1))
					exists = true;
			}
			
			if (exists)
				return;
			
			UserDefinedPlotTypeInfo plot_type = (UserDefinedPlotTypeInfo)this.list_store.GetValue(iter, 2);
			plot_type.Name = args.NewText;
			this.list_store.SetValue(iter, 1, args.NewText);
		}		
		
		private void OnDeleteEvent(System.Object sender, DeleteEventArgs e)
		{
			e.RetVal = true;
			this.OnCancelButtonClicked(null, EventArgs.Empty);
		}
		
		internal void OnCancelButtonClicked(object o, EventArgs e)
		{
			ResponseType response = ResponseType.Ok;
			
			if (this.need_discard_changes_warning)
			{
				MessageDialog dialog = new MessageDialog(this.user_defined_plot_dialog, DialogFlags.Modal, MessageType.Question, ButtonsType.OkCancel, 
				                                         Catalog.GetString("Changes in user defined plot type definitions will be discarded."));
				response = (ResponseType)dialog.Run();
				dialog.Destroy();
			}
			
			if (response == ResponseType.Ok)
			{
				this.user_defined_plot_dialog.Destroy();
				InitPlotTypeInfos();
				Plugin.host.UpdatePluginInfos();
			}
		}
		
		internal void OnCloseButtonClicked(object o, EventArgs e)
		{
			this.Close();
		}
		
		private void Compile(bool show_result)
		{
			this.user_defined_plot_dialog.GdkWindow.Cursor = new Cursor(CursorType.Watch);
			Plugin.host.ProcessEvents();
			
			try
			{
				UserDefinedPlotData data = new UserDefinedPlotData();
				TreeIter iter = this.current_plot_type_iter;
				data.Name = (string)this.list_store.GetValue(iter, 1);
				data.XLabel = this.xlabel_entry.Text;
				data.YLabel = this.ylabel_entry.Text;
				data.LowerLimit = this.lower_limit_spin.Value;
				data.UpperLimit = this.upper_limit_spin.Value;
				data.StandardMin = this.standard_min_spin.Value;
				data.StandardMax = this.standard_max_spin.Value;
				data.Absorbance = this.absorbance_checkbutton.Active;
				data.DefinitionType = (DefinitionType)this.definition_notebook.CurrentPage;
				data.Function = this.function_entry.Text;
				data.Code = this.text_editor.Document.Text;
				
				UserDefinedPlotTypeInfo plot_type = new UserDefinedPlotTypeInfo(data);

				Console.WriteLine("Compiling plot type...");
				CompilerResults results = plot_type.Compile();
				Console.WriteLine("Done.");
				
				string error_text = string.Empty;
				foreach (CompilerError error in results.Errors)
				{
					if (!error.IsWarning)
						error_text += error.ErrorText + "\n";
				}

				if (string.IsNullOrEmpty(error_text))
				{
					switch (data.DefinitionType)
					{
						
					case DefinitionType.Simple:
						this.simple_ok_image.Visible = show_result;
						break;
						
					case DefinitionType.Advanced:
						this.advanced_ok_image.Visible = show_result;
						break;
						
					}
					return;
				}
				
				if (data.DefinitionType == DefinitionType.Simple)
					throw new Exception(Catalog.GetString("Please check function definition for errors."));
				else
					throw new Exception(error_text);
			}
			catch (Exception ex)
			{
				throw ex;
			}
			finally
			{
				this.user_defined_plot_dialog.GdkWindow.Cursor = new Cursor(CursorType.Arrow);
			}
		}

		private bool ApplyChanges()
		{
			if (!this.current_plot_type_iter.Equals(TreeIter.Zero))
			{
				try
				{
					this.Compile(false);
				}
				catch (Exception ex)
				{
					Plugin.host.ShowInfoMessage(this.user_defined_plot_dialog, 
					                            Catalog.GetString("Compilation of user-defined plot type failed: {0}\n"),
					                            ex.Message);
					return false;
				}
				
				this.UpdateCurrentPlotType();
				Plugin.host.UpdatePluginInfos();
			}
			return true;
		}			
		
		private void Close()
		{
			if (!this.ApplyChanges())
				return;
			
			try
			{
				WriteDefinitionFile(Path.Combine(Plugin.host.ConfigurationDirPath, Plugin.PlotDefinitionFile));
			}
			catch (Exception ex)
			{
				Plugin.host.ShowInfoMessage(this.user_defined_plot_dialog,
				                            Catalog.GetString("Cannot save user-defined plot types: {0}"),
				                            ex.Message);
			}

			this.user_defined_plot_dialog.Destroy();
		}
		
		internal static void InitPlotTypeInfos()
		{
			string plot_def_file = Path.Combine(Plugin.host.ConfigurationDirPath, Plugin.PlotDefinitionFile);
			if (File.Exists(plot_def_file))
			{
				try
				{
					Plugin.plot_type_infos.Clear();
					Plugin.plot_type_infos = new List<UserDefinedPlotTypeInfo>(UserDefinedPlotDialog.ReadDefinitionFile(plot_def_file));
				}
				catch (Exception ex)
				{
					Plugin.host.ShowInfoMessage(Plugin.host.MainWindow,
					                            Catalog.GetString("Cannot read user-defined plot definitions from '{0}':\n\n{1}"),
					                            plot_def_file, ex.Message);
				}
			}
		}
			
		internal static UserDefinedPlotTypeInfo[] ReadDefinitionFile(string fname)
		{
			UserDefinedPlotDataList list = UserDefinedPlotDataList.ReadFromFile(fname);
			List<UserDefinedPlotTypeInfo> user_defined_plot_type_infos = new List<UserDefinedPlotTypeInfo>();
			
			foreach (UserDefinedPlotData data in list)
			{
				user_defined_plot_type_infos.Add(new UserDefinedPlotTypeInfo(data));
			}
			
			List<string> names = new List<string>();
			foreach (UserDefinedPlotTypeInfo plot_type in Plugin.plot_type_infos)
			{
				names.Add(plot_type.Name);
			}
			
			// resolve name duplicates
			for (int ix=0; ix < user_defined_plot_type_infos.Count; ++ix)
			{
				while (names.Contains(user_defined_plot_type_infos[ix].Name))
				{
					user_defined_plot_type_infos[ix].Name = user_defined_plot_type_infos[ix].Name.Substring(1) + "_2";
				}
			}
			
			return user_defined_plot_type_infos.ToArray();
		}

		internal static void WriteDefinitionFile(string fname)
		{
			WriteDefinitionFile(fname, null);
		}
		
		internal static void WriteDefinitionFile(string fname, List<string> plot_type_names)
		{
			if (!fname.ToLower().EndsWith(".xml"))
				fname += ".xml";
			
			UserDefinedPlotDataList list = new UserDefinedPlotDataList();
			foreach (UserDefinedPlotTypeInfo info in Plugin.plot_type_infos)
			{
				if (plot_type_names != null && !plot_type_names.Contains(info.Name))
					continue;
				
				Console.WriteLine("Save user defined plot type: " + info.UserDefinedPlotData.Name);
				list.Add(info.UserDefinedPlotData);
			}
			list.WriteToFile(fname);
		}
	}
}
