// PlotTv.cs created with MonoDevelop
// User: klose at 11:16 27.11.2009
// CVS release: $Id: PlotTv.cs,v 1.6 2011-04-19 12:36:58 klose Exp $
//
//    QuickStep: Acquire, view and identify spectra 
//    Copyright (C) 2009  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.Collections;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using Gtk;

using Hiperscan.Spectroscopy;
using Hiperscan.GUI.Figure;
using Hiperscan.Extensions;
using Hiperscan.Unix;


namespace Hiperscan.GUI
{
	
	internal class PlotTv : Gtk.TreeView
	{
		public delegate void ReturnPressedHandler(PlotTv tv);
		public event         ReturnPressedHandler ReturnPressed;

		public delegate void DeletePressedHandler(PlotTv tv);
		public event         DeletePressedHandler DeletePressed;
		
		public delegate void F2PressedHandler(PlotTv tv);
		public event         F2PressedHandler F2Pressed;

		public delegate void ButtonClickedHandler(PlotTv tv);
		public event         ButtonClickedHandler Button1Clicked;
		public event         ButtonClickedHandler Button2Clicked;
		public event         ButtonClickedHandler Button3Clicked;
		public event         ButtonClickedHandler DoubleClicked;
		
		public delegate void MotionHandler(object o, Gdk.EventMotion e);
		public event         MotionHandler Motion;
		
		public delegate void LeftHandler(object o, Gdk.EventCrossing e);
		public event         LeftHandler Left;
		
		
		public PlotTv(TreeStore store) : base(store) {}

		protected override bool OnKeyPressEvent(Gdk.EventKey e)
		{
			if ((e.Key == Gdk.Key.Return) &&  (this.ReturnPressed != null))
				this.ReturnPressed(this);
			
			if ((e.Key == Gdk.Key.Delete) &&  (this.DeletePressed != null))
			{
				this.DeletePressed(this);
				return true;
			}

			if ((e.Key == Gdk.Key.F2) &&  (this.F2Pressed != null))
				this.F2Pressed(this);
			
			return base.OnKeyPressEvent(e);
		}
		
		protected override bool OnButtonPressEvent(Gdk.EventButton e)
		{
			if ((e.Button == 1) && (this.Button1Clicked != null))
				this.Button1Clicked(this);
			
			if ((e.Button == 2) && (this.Button2Clicked != null))
				this.Button2Clicked(this);

			if ((e.Button == 3) && (this.Button3Clicked != null))
				this.Button3Clicked(this);

			if ((e.Button == 1) && (e.Type == Gdk.EventType.TwoButtonPress) && (this.DoubleClicked != null))
				this.DoubleClicked(this);
			
			return base.OnButtonPressEvent(e);
		}
		
		protected override bool OnMotionNotifyEvent(Gdk.EventMotion e)
		{
			if (this.Motion != null)
				this.Motion(this, e);
			
			return base.OnMotionNotifyEvent(e);
		}
		
		protected override bool OnLeaveNotifyEvent(Gdk.EventCrossing e)
		{
			if (this.Left != null)
				this.Left(this, e);
			
			return base.OnLeaveNotifyEvent(e);
		}
	}
		
	public class ScrolledPlotTv : Gtk.ScrolledWindow
	{
		private enum Store : int
		{
			LineStyle,
			Label,
			Mode,
			PlotInfo,
			Spectrum,
			Annotation
		}
		
		private PlotTv    tv;
		private PlotInfoList plot_info_list;
		
		private ArrayList      columns = new ArrayList();
		private List<TreeIter> _iters  = new List<TreeIter>();

		private static Dictionary<string,Gdk.Color> auto_colors = new Dictionary<string,Gdk.Color>();
		
		public delegate void SelectionChangedHandler(List<PlotInfo> plot_infos, List<Spectrum> spectra, bool clicked);
		public event         SelectionChangedHandler SelectionChanged;

		public delegate void SpectrumSelectionPositionChangedHandler(List<Spectrum> spectra);
		public event         SpectrumSelectionPositionChangedHandler SpectrumSelectionPositionChanged;
		
		public delegate void SpectrumDoubleClickedHandler(Spectrum spectrum);
		public event         SpectrumDoubleClickedHandler SpectrumDoubleClicked;
		
		public delegate void FigureDoubleClickedHandler(PlotInfo plot_info);
		public event         FigureDoubleClickedHandler FigureDoubleClicked;
		
		public delegate void ZOrderChangedHandler(PlotInfo plot_info);
		public event         ZOrderChangedHandler ZOrderChanged;
		
		public ScrolledPlotTv(PlotInfoList plot_info_list) : base()
		{
			this.plot_info_list = plot_info_list;
			
			this.tv = new PlotTv(null);
			this.ResetStore();
			
			this.tv.WidthRequest  = 210;
			
			this.tv.RulesHint = false;
			this.tv.Selection.Mode = SelectionMode.Multiple;
			
			this.tv.Motion            += new PlotTv.MotionHandler(this.OnMotion);
			this.tv.Left              += new PlotTv.LeftHandler(this.OnLeft);
			this.tv.Selection.Changed += new EventHandler(this.OnSelectionChanged);
			
			CellRendererPixbuf pixbuf;
			CellRendererText   text;
			TreeViewColumn     column;
				
			// line style column
			pixbuf = new CellRendererPixbuf();
			pixbuf.Xalign = 0f;
			this.columns.Add(pixbuf);
			column = new TreeViewColumn(Catalog.GetString("Line style"), pixbuf,
			                            "pixbuf", Store.LineStyle,
			                            "mode",   Store.Mode);
			column.Clickable = true;
			column.SortOrder = SortType.Ascending;
			column.Clicked  += new EventHandler(this.OnLineStyleColumnClicked);
			column.SetCellDataFunc(pixbuf, new TreeCellDataFunc(this.RenderEntry));
			this.tv.InsertColumn(column, (int)Store.LineStyle);
			column.SortIndicator = true;
			
			// label column
			text = new CellRendererText();
			text.Xalign = 0f;
			text.Editable = true;
			text.Edited += this.OnLabelEdited;
			this.columns.Add(text);
			column = new TreeViewColumn(Catalog.GetString("Label"), text,
			                            "markup", Store.Label,
			                            "mode",   Store.Mode);
			column.Clickable = true;
			column.SortOrder = SortType.Ascending;
			column.Clicked  += new EventHandler(this.OnLabelColumnClicked);
			column.SetCellDataFunc(text, new TreeCellDataFunc(this.RenderEntry));
			this.tv.InsertColumn(column, (int)Store.Label);
						
			tv.HeadersClickable = true;
			tv.HeadersVisible   = true;
			
			this.Add(this.tv);
			
			this.InitializeList();
			
			this.tv.DoubleClicked  += new PlotTv.ButtonClickedHandler(this.OnDoubleClicked);
			this.tv.ReturnPressed  += new PlotTv.ReturnPressedHandler(this.OnDoubleClicked);
			this.tv.DeletePressed  += new PlotTv.DeletePressedHandler(this.OnDeletePressed);
			this.tv.F2Pressed      += new PlotTv.F2PressedHandler(this.OnF2Pressed);
			this.tv.Button1Clicked += new PlotTv.ButtonClickedHandler(this.OnButton1Clicked);
			this.tv.Button2Clicked += new PlotTv.ButtonClickedHandler(this.OnButton2Clicked);
			this.tv.Button3Clicked += new PlotTv.ButtonClickedHandler(this.OnButton3Clicked);
			
			this.ShowAll();
		}
		
		public TreeIter AddFigure(PlotInfo plot_info)
		{
			TreeIter iter = TreeIter.Zero;

			if (this.TreeStore == null)
				return iter;
			
			if (plot_info.IsBackground)
			{
				iter = this.TreeStore.AppendValues(plot_info.Thumbnail, 
				                                   "<b><i>" + plot_info.Name + "</i></b>",
				                                   CellRendererMode.Editable,
				                                   plot_info,
				                                   null);
			}
			else
			{
				iter = this.TreeStore.AppendValues(plot_info.Thumbnail, 
				                                   "<b>" + plot_info.Name + "</b>",
				                                   CellRendererMode.Editable,
				                                   plot_info,
				                                   null);
			}
			
			plot_info.TreeIter = iter;
			plot_info.ThumbnailChanged += this.OnThumbnailChanged;
			
			return iter;
		}
		
		public void RemoveFigure(PlotInfo plot_info)
		{
			foreach (TreeIter iter in this.GetIters())
			{
				TreeIter i;
				if (this.TreeStore.IterParent(out i, iter))
					continue;
				
				PlotInfo info = this.TreeStore.GetValue(iter, (int)Store.PlotInfo) as PlotInfo;
				
				if (info != null && info.UUID == plot_info.UUID)
				{
					plot_info.ThumbnailChanged -= this.OnThumbnailChanged;
					i = iter;
					this.TreeStore.Remove(ref i);
					break;
				}
			}
		}
		
		public void UpdateFigureNames()
		{
			foreach (TreeIter iter in this.GetIters())
			{
				TreeIter i;
				if (this.TreeStore.IterParent(out i, iter))
					continue;
				
				PlotInfo info = this.TreeStore.GetValue(iter, (int)Store.PlotInfo) as PlotInfo;
				
				if (info.IsBackground)
					this.TreeStore.SetValue(iter, (int)Store.Label, "<b><i>" + info.Name + "</i></b>");
				else
					this.TreeStore.SetValue(iter, (int)Store.Label, "<b>" + info.Name + "</b>");

				info.NotifyNameChanged();
			}
		}
		
		private void AddSpectrum(PlotInfo plot_info, Spectrum spectrum)
		{
			if (this.TreeStore == null)
				return;
			
			TreeIter parent = TreeIter.Zero;
			
			foreach (TreeIter iter in this.GetIters())
			{
				TreeIter i;
				if (this.TreeStore.IterParent(out i, iter))
					continue;
				PlotInfo info = this.TreeStore.GetValue(iter, (int)Store.PlotInfo) as PlotInfo;
				if (info != null && info.UUID == plot_info.UUID)
				{
					parent = iter;
					break;
				}
			}
			
			if (parent.Equals(TreeIter.Zero))
				parent = this.AddFigure(plot_info);

			CellRendererMode mode = spectrum.IsVisible ? CellRendererMode.Editable : CellRendererMode.Inert;
			this.TreeStore.AppendValues(parent,
			                            plot_info.ScrolledPlot.Plot.GetLineStylePixbuf(spectrum),
			                            spectrum.Label,
			                            mode,
			                            plot_info,
			                            spectrum);
			
			this.tv.ExpandRow(this.TreeStore.GetPath(parent), true);
		}
		
		private void AddAnnotation(PlotInfo plot_info, Annotation annotation)
		{
			TreeIter parent      = TreeIter.Zero;
			TreeIter grandparent = TreeIter.Zero;
			
			foreach (TreeIter iter in this.GetIters())
			{
				PlotInfo info     = this.TreeStore.GetValue(iter, (int)Store.PlotInfo) as PlotInfo;
				Spectrum spectrum = this.TreeStore.GetValue(iter, (int)Store.Spectrum) as Spectrum;

				TreeIter i;
				if (!this.TreeStore.IterParent(out i, iter) && info != null && info.UUID == plot_info.UUID)
					grandparent = iter;
				
				if (!this.TreeStore.IterParent(out i, iter) || spectrum != null )
					continue;
				
				Annotation a = this.TreeStore.GetValue(iter, (int)Store.Annotation) as Annotation;
				if (info != null && info.UUID == plot_info.UUID && a == null)
				{
					parent = iter;
					break;
				}
			}
			
			if (parent.Equals(TreeIter.Zero))
			{
				parent = this.TreeStore.AppendValues(grandparent,
				                                     null, 
				                                     "<b>" + Catalog.GetString("Annotations") + "</b>",
				                                     CellRendererMode.Activatable,
				                                     plot_info,
				                                     null,
				                                     null);
			}

			this.TreeStore.AppendValues(parent,
			                            null,
			                            annotation.Type,
			                            CellRendererMode.Activatable,
			                            plot_info,
			                            null,
			                            annotation);
		}

		private void OnThumbnailChanged(PlotInfo plot_info)
		{
			if (this.TreeStore == null)
				return;
			
			this.TreeStore.SetValue(plot_info.TreeIter, (int)Store.LineStyle, plot_info.Thumbnail);			
		}
		
		private void RenderEntry(TreeViewColumn column, CellRenderer cell, TreeModel model, TreeIter iter)
		{
			CellRendererMode mode = (CellRendererMode)model.GetValue(iter, (int)Store.Mode);

			if (mode == CellRendererMode.Inert)
				cell.Sensitive = false;
			else
				cell.Sensitive = true;
		}
		
		private void ResetStore()
		{
			if (this.TreeStore != null)
				this.TreeStore.Dispose();
			
			this.TreeStore = new TreeStore(typeof(Gdk.Pixbuf),         // line pixbuffer
			                               typeof(string),             // label
			                               typeof(CellRendererMode),   // renderer mode
			                               typeof(PlotInfo),           // plot info
			                               typeof(Spectrum),           // spectrum object
			                               typeof(Annotation));        // annotation object
			
			this.TreeStore.SetSortFunc(0, new TreeIterCompareFunc(this.ComparePlotStyles));
			this.TreeStore.SetSortFunc(1, new TreeIterCompareFunc(this.CompareLabels));
			this.TreeStore.SetSortColumnId(0, SortType.Ascending);
		}
		
		internal void InitializeList()
		{
			this.ResetStore();

			foreach (PlotInfo plot_info in this.plot_info_list.Values)
			{
				foreach (Spectrum spectrum in plot_info.CurrentSpectra.Values)
				{
					this.AddSpectrum(plot_info, spectrum);
				}
			}

			this.plot_info_list.CurrentSpectraChanged += this.OnCurrentSpectraChanged;
		}
		
		private int ComparePlotStyles(TreeModel model, TreeIter a, TreeIter b)
		{
			Spectrum spectruma = model.GetValue(a, (int)Store.Spectrum) as Spectrum;
			Spectrum spectrumb = model.GetValue(b, (int)Store.Spectrum) as Spectrum;
			
			int cid; SortType st;
			this.TreeStore.GetSortColumnId(out cid, out st);

			if (spectruma != null && spectruma.IsPreview)
				return st == SortType.Ascending ? 1 : -1;

			if (spectrumb != null && spectrumb.IsPreview)
				return st == SortType.Ascending ? -1 : 1;

			if (spectruma == null || spectrumb == null)
				return 0;
			
			if (spectruma.ZOrder != spectrumb.ZOrder)
				return st == SortType.Ascending ? spectruma.ZOrder.CompareTo(spectrumb.ZOrder) : spectrumb.ZOrder.CompareTo(spectruma.ZOrder);
			
			return string.Compare(spectruma.Id, spectrumb.Id);
		}
		
		private int CompareLabels(TreeModel model, TreeIter a, TreeIter b)
		{
			Spectrum spectruma = (Spectrum)model.GetValue(a, (int)Store.Spectrum);
			Spectrum spectrumb = (Spectrum)model.GetValue(b, (int)Store.Spectrum);
			int cid; SortType st;
			this.TreeStore.GetSortColumnId(out cid, out st);

			if (spectruma != null && spectruma.IsPreview)
				return st == SortType.Ascending ? 1 : -1;

			if (spectrumb != null && spectrumb.IsPreview)
				return st == SortType.Ascending ? -1 : 1;
			
			if (spectruma == null || spectrumb == null)
				return 0;
			
			if (spectruma.ZOrder != spectrumb.ZOrder)
				return st == SortType.Ascending ? spectruma.ZOrder.CompareTo(spectrumb.ZOrder) : spectrumb.ZOrder.CompareTo(spectruma.ZOrder);

			return String.Compare((string)model.GetValue(a, (int)Store.Label),
			                      (string)model.GetValue(b, (int)Store.Label));
		}

		internal List<Spectrum> GetSelectedSpectra()
		{
			TreeSelection selection = this.tv.Selection;
			
			TreeModel model;
			TreeIter iter;
			
			TreePath[] paths = selection.GetSelectedRows(out model);
			List<Spectrum> selected_spectra = new List<Spectrum>();

			foreach (TreePath path in paths)
			{
				if (this.TreeStore.GetIterFromString(out iter, path.ToString()))
				{
					Spectrum spectrum  = this.TreeStore.GetValue(iter, (int)Store.Spectrum) as Spectrum;
					PlotInfo plot_info = this.TreeStore.GetValue(iter, (int)Store.PlotInfo) as PlotInfo;
					
					if (spectrum == null)
						continue;
					
					Console.WriteLine("Selected: " + spectrum.Id);
					spectrum.PlotId = plot_info.UUID;
					selected_spectra.Add(spectrum);
				}
			}
			
			return selected_spectra;
		}
		
		internal List<PlotInfo> GetSelectedFigures()
		{
			TreeSelection selection = this.tv.Selection;
			
			TreeModel model;
			TreeIter iter;
			
			TreePath[] paths = selection.GetSelectedRows(out model);
			List<PlotInfo> selected_figures = new List<PlotInfo>();

			foreach (TreePath path in paths)
			{
				if (this.TreeStore.GetIterFromString(out iter, path.ToString()))
				{
					Spectrum spectrum  = this.TreeStore.GetValue(iter, (int)Store.Spectrum) as Spectrum;
					PlotInfo plot_info = this.TreeStore.GetValue(iter, (int)Store.PlotInfo) as PlotInfo;
					
					if (spectrum != null)
						continue;
					
					Console.WriteLine("Selected figure: " + plot_info.Name);
					selected_figures.Add(plot_info);
				}
			}
			
			return selected_figures;
		}

		internal void SetSelectedSpectra(List<Spectrum> spectra)
		{
			List<string> ids = new List<string>();
			foreach (Spectrum spectrum in spectra)
			{
				ids.Add(spectrum.Id);
			}
			
			this.tv.Model.Foreach(delegate(TreeModel model, TreePath path, TreeIter iter) {
				Spectrum spectrum = (Spectrum)this.TreeStore.GetValue(iter, (int)Store.Spectrum);
				Console.WriteLine(spectrum.Id);
				if (ids.Contains(spectrum.Id))
				{
					this.tv.Selection.SelectIter(iter);
					Console.WriteLine("Reselect " + spectrum.Id);
				}
				return false;
			});
		}
		
		private List<TreeIter> GetIters()
		{
			this._iters.Clear();

			if (this.TreeStore == null)
				return new List<TreeIter>();
			
			this.TreeStore.Foreach(new TreeModelForeachFunc(this.GetIterCallback));
			
			return new List<TreeIter>(this._iters);
		}

		private bool GetIterCallback(TreeModel model, TreePath path, TreeIter iter)
		{
			this._iters.Add(iter);
			return false;
		}
		
		private TreeIter GetIterBySpectrum(Spectrum spectrum)
		{
			lock (this)
			{
				foreach (TreeIter iter in this.GetIters())
				{
					Spectrum s = (Spectrum)this.TreeStore.GetValue(iter, (int)Store.Spectrum);
					
					if (s == null)
						continue;
					
					if (s.Id == spectrum.Id && s.PlotId == spectrum.PlotId)
						return iter;
				}
			}
			
			throw new Exception(Catalog.GetString("Tree store does not contain the spectrum with id") +
			                    " '" + spectrum.Id + "'");
		}
		
		public SortType SetSortOrder(TreeViewColumn column)
		{
			if (column.SortIndicator)
			{
				if (column.SortOrder == SortType.Ascending)
					return SortType.Descending;
				else
					return SortType.Ascending;
			}
			return SortType.Ascending;
		}
		
		public void Update()
		{
			this.OnCurrentSpectraChanged(this.plot_info_list.Current.CurrentSpectra, true);
		}
		
		private void OnCurrentSpectraChanged(CurrentSpectra current_spectra, bool ds_changed)
		{
			if (this.tv?.Selection == null)
				return;
			
			bool selected_spectra_changed = ds_changed;
			List<string> current_ids = new List<string>();
			foreach (TreeIter iter in this.GetIters())
			{
				Spectrum spectrum  = this.TreeStore.GetValue(iter, (int)Store.Spectrum) as Spectrum;
				PlotInfo info      = this.TreeStore.GetValue(iter, (int)Store.PlotInfo) as PlotInfo;
				
				if (spectrum == null)
				{
					// its a parent (figure n)
					if (info.IsBackground)
					{
						this.TreeStore.SetValues(iter,
						                         info.Thumbnail,
						                         "<b><i>" + info.Name + "</i></b>",
						                         CellRendererMode.Editable,
						                         info,
						                         null);
					}
					else
					{
						this.TreeStore.SetValues(iter,
						                         info.Thumbnail,
						                         "<b>" + info.Name + "</b>",
						                         CellRendererMode.Editable,
						                         info,
						                         null);
					}
					continue;
				}
				
				current_ids.Add(info.UUID + spectrum.Id);
				bool iter_removed = false;
				
				if (this.plot_info_list.ContainsValue(info) && info.CurrentSpectra.Contains(spectrum.Id))
				{
					// update values
					CellRendererMode mode = spectrum.IsVisible ? CellRendererMode.Editable : CellRendererMode.Inert;
					this.TreeStore.SetValues(iter,
					                         info.ScrolledPlot.Plot.GetLineStylePixbuf(spectrum),
					                         spectrum.Label,
					                         mode,
					                         info,
					                         info.CurrentSpectra[spectrum.Id]);
				}
				else
				{
					TreeIter remove_iter = iter;
					TreeIter parent;
					this.TreeStore.IterParent(out parent, iter);
					this.TreeStore.Remove(ref remove_iter);
					if (TreeIter.Zero.Equals(remove_iter))
					{
						TreeIter last_iter;
						int n = this.TreeStore.IterNChildren(parent);
						if (n > 0)
						{
							this.TreeStore.IterNthChild(out last_iter, parent, n-1);
							this.tv.Selection.SelectIter(last_iter);
						}
					}
					else
					{
						this.tv.Selection.SelectIter(remove_iter);
					}
					iter_removed = true;
				}

				if (!iter_removed &&
				    this.Tv.Selection.IterIsSelected(iter) &&
				    (spectrum.IsStream || spectrum.IsPreview))
					selected_spectra_changed = true;
			}
			
			foreach (PlotInfo pi in this.plot_info_list.Values)
			{
				foreach (Spectrum spectrum in pi.CurrentSpectra.Values)
				{
					if (!current_ids.Contains(pi.UUID + spectrum.Id))
						this.AddSpectrum(pi, spectrum);
					else
						current_ids.Remove(pi.UUID + spectrum.Id);
				}
			}
			
			
			if (selected_spectra_changed && 
			    this.tv.Selection.CountSelectedRows() > 0)
			{
				// a selected spectrum may have changed -> schedule an update
				GLib.Timeout.Add(0, delegate {
					if (this.SelectionChanged != null)
						this.SelectionChanged(this.GetSelectedFigures(), this.GetSelectedSpectra(), false);
					return false;
				});
			}
			else if (this.tv.Selection.CountSelectedRows() > 0)
			{
				// a position of selected spectra may have changed
				GLib.Timeout.Add(0, delegate {
					if (this.SpectrumSelectionPositionChanged != null)
						this.SpectrumSelectionPositionChanged(this.GetSelectedSpectra());
					return false;
				});
			}
		}
		
		private void OnLineStyleColumnClicked(object o, EventArgs e)
		{
			this.ResetZOrder();
			
			TreeViewColumn c = o as TreeViewColumn;
			c.SortOrder      = this.SetSortOrder(c);
			c.SortIndicator  = true;
			
			lock (this)
				this.TreeStore.SetSortColumnId(0, c.SortOrder);
			
			this.SetZOrder();
		}
		
		private void OnLabelColumnClicked(object o, EventArgs e)
		{
			this.ResetZOrder();
			
			TreeViewColumn c = o as TreeViewColumn;
			c.SortOrder      = this.SetSortOrder(c);
			c.SortIndicator  = true;
			
			lock (this)
				this.TreeStore.SetSortColumnId(1, c.SortOrder);
			
			this.SetZOrder();
		}
		
		private void OnLabelEdited(object o, EditedArgs args)
		{
			TreeIter iter;
			this.TreeStore.GetIter(out iter, new Gtk.TreePath(args.Path));
 
			Spectrum spectrum  = this.TreeStore.GetValue(iter, (int)Store.Spectrum) as Spectrum;
			PlotInfo plot_info = this.TreeStore.GetValue(iter, (int)Store.PlotInfo) as PlotInfo;
			
			if (spectrum == null)
			{
				// its a figure name
				plot_info.Name = args.NewText;
				if (plot_info.IsBackground)
					this.TreeStore.SetValue(iter, (int)Store.Label, "<b><i>" + args.NewText + "</i></b>");
				else
					this.TreeStore.SetValue(iter, (int)Store.Label, "<b>" + args.NewText + "</b>");
				return;
			}
			
			if (spectrum.IsPreview)
				return;
			
			if (spectrum.Label != args.NewText)
			{
				spectrum.Label = args.NewText;
				MainWindow.UI.UpdatePlot(plot_info);
			}
		}

		private void OnMotion(object o, Gdk.EventMotion e)
		{
		}
		
		private void OnLeft(object o, Gdk.EventCrossing e)
		{
		}
		
		public void DeleteSelected()
		{
			this.OnDeletePressed(this.tv);
		}

		public void AutoColorSelected()
		{
			List<Spectrum> spectra = this.GetSelectedSpectra();
			List<string> plot_ids  = new List<string>();
			List<PlotInfo> figures = this.GetSelectedFigures();

			foreach (PlotInfo figure in figures)
			{
				foreach (Spectrum spectrum in figure.CurrentSpectra.Values)
				{
					if (!spectra.Contains(spectrum))
					{
						spectrum.PlotId = figure.UUID;
						spectra.Add(spectrum);
					}
				}
			}

			foreach (Spectrum spectrum in spectra)
			{
				if (spectrum.IsPreview)
					continue;

				if (!spectrum.IsVisible)
					continue;
			
				string[] s = Regex.Split(spectrum.Label, "_");
				string group_id;
				if (s.Length > 1)
					group_id = s[0];
				else
					group_id = spectrum.Label;

				if (!ScrolledPlotTv.auto_colors.ContainsKey(group_id))
					ScrolledPlotTv.auto_colors.Add(group_id, ScrolledPlotTv.NextAutoColor);

				spectrum.Color = UserInterface.DrawingColor(ScrolledPlotTv.auto_colors[group_id]);

				if (!plot_ids.Contains(spectrum.PlotId))
					plot_ids.Add(spectrum.PlotId);
			}

			MainWindow.UI.UpdatePlot(plot_ids);
		}
		
		public void AddSelected()
		{
			List<Spectrum> spectra = this.GetSelectedSpectra();
			List<string> plot_ids  = new List<string>();
			
			foreach (Spectrum spectrum in spectra)
			{
				if (spectrum.IsPreview)
					continue;
				
				spectrum.IsVisible = true;
				
				if (!plot_ids.Contains(spectrum.PlotId))
					plot_ids.Add(spectrum.PlotId);
			}
			MainWindow.UI.UpdatePlot(plot_ids);
		}
		
		public void RemoveSelected()
		{
			List<Spectrum> spectra = this.GetSelectedSpectra();
			List<string> plot_ids  = new List<string>();

			foreach (Spectrum spectrum in spectra)
			{
				if (spectrum.IsPreview)
					continue;
				
				spectrum.IsVisible = false;

				if (!plot_ids.Contains(spectrum.PlotId))
					plot_ids.Add(spectrum.PlotId);
			}
			MainWindow.UI.UpdatePlot(plot_ids);
		}
		
		public void MoveSelectedUp()
		{
			List<Spectrum> spectra = this.GetSelectedSpectra();
			
			if (spectra.Count != 1)
				return;
			
			Spectrum spectrum = spectra[0];

			TreeIter iter = this.GetIterBySpectrum(spectrum);
			
			TreeIter before = TreeIter.Zero;
			this.tv.Model.Foreach(delegate(TreeModel _model, TreePath _path, TreeIter _iter) {
				TreeIter next = _iter;
				if (!this.TreeStore.IterNext(ref next))
					return false;
				if (next.Equals(iter))
				{
					before = _iter;
					return true;
				}
				return false;
			});
			
			if (before.Equals(TreeIter.Zero))
				return;
				
			Spectrum before_spectrum = (Spectrum)this.TreeStore.GetValue(before, (int)Store.Spectrum);
				
			if (spectrum.IsPreview || before_spectrum.IsPreview)
				return;
			
			this.TreeStore.Swap(iter, before);
			this.SetZOrder(spectrum.PlotId);
		}
		
		public void MoveSelectedDown()
		{
			List<Spectrum> spectra = this.GetSelectedSpectra();
			
			if (spectra.Count != 1)
				return;
			
			Spectrum spectrum = spectra[0];

			TreeIter iter = this.GetIterBySpectrum(spectrum);
			TreeIter next = iter;
			if (!this.TreeStore.IterNext(ref next))
				return;
				
			Spectrum next_spectrum = (Spectrum)this.TreeStore.GetValue(next, (int)Store.Spectrum);
				
			if (spectrum.IsPreview || next_spectrum.IsPreview)
				return;

			this.TreeStore.Swap(iter, next);
			this.SetZOrder(spectrum.PlotId);
		}

		private void SetZOrder()
		{
			this.SetZOrder(null);
		}

		private void SetZOrder(string plot_id)
		{
			List<PlotInfo> plot_infos = new List<PlotInfo>();
			bool found_new;
			
			do
			{
				found_new = false;
				int z_order = 0;
				PlotInfo current = null;
				
				this.tv.Model.Foreach(delegate(TreeModel _model, TreePath _path, TreeIter iter) {
					Spectrum spectrum  = this.TreeStore.GetValue(iter, (int)Store.Spectrum) as Spectrum;
					PlotInfo plot_info = this.TreeStore.GetValue(iter, (int)Store.PlotInfo) as PlotInfo;
					
					if (!plot_infos.Contains(plot_info))
					{
						if (found_new)
							return false;
						found_new = true;
						current = plot_info;
						plot_infos.Add(plot_info);
					}
					
					if (plot_info != current || spectrum == null)
						return false;
					
					Console.WriteLine("set spectrum: " + spectrum.Label);
					Console.WriteLine("plot: " + plot_info.UUID);
					
					if (spectrum.IsPreview)
						spectrum.ZOrder = int.MaxValue;
					else
						spectrum.ZOrder = z_order++;
				
					return false;
				});
			} while (found_new);
			
			foreach (PlotInfo plot_info in plot_infos)
			{
				if (plot_id != null && plot_info.UUID != plot_id)
					continue;
				
				if (this.ZOrderChanged != null)
					this.ZOrderChanged(plot_info);
			}
		}
		
		private void ResetZOrder()
		{
			this.tv.Model.Foreach(delegate(TreeModel _model, TreePath _path, TreeIter iter) {
				Spectrum spectrum = (Spectrum)this.TreeStore.GetValue(iter, (int)Store.Spectrum);
				if (spectrum != null)
					spectrum.ZOrder = int.MaxValue;
				return false;
			});
		}
		
		public bool IsFirst(Spectrum spectrum)
		{
			TreeIter iter = this.GetIterBySpectrum(spectrum);
			TreeIter parent, child;
			this.TreeStore.IterParent(out parent, iter);
			this.TreeStore.IterNthChild(out child, parent, 0);
			if (this.TreeStore.IterHasChild(child))
				this.TreeStore.IterNthChild(out child, parent, 1);
			return iter.Equals(child);
		}
		
		public bool IsLast(Spectrum spectrum)
		{
			TreeIter iter = this.GetIterBySpectrum(spectrum);
			if (!this.TreeStore.IterNext(ref iter))
				return true;
			return ((Spectrum)this.TreeStore.GetValue(iter, (int)Store.Spectrum)).IsPreview;
		}
		
		internal void OnDeletePressed(PlotTv tv)
		{
			List<Spectrum> spectra = this.GetSelectedSpectra();
			List<string> plot_ids = new List<string>();
			
			try
			{
				foreach (Spectrum spectrum in spectra)
				{
					this.plot_info_list[spectrum.PlotId].CurrentSpectra.AddHistoryEntry();
				}

				foreach (Spectrum spectrum in spectra)
				{
					if (spectrum.IsPreview)
						continue;
					
					Console.WriteLine("Remove spectrum: " + spectrum.Id);
					this.plot_info_list[spectrum.PlotId].CurrentSpectra.Remove(spectrum.Id, false);
					
					if (!plot_ids.Contains(spectrum.PlotId))
						plot_ids.Add(spectrum.PlotId);
				}

				foreach (string id in plot_ids)
				{
					this.plot_info_list[id].CurrentSpectra.NotifyChanges();
				}
				MainWindow.UI.UpdatePlot(plot_ids);
			}
			catch (Exception ex)
			{
				new InfoMessage(null, Catalog.GetString("Cannot remove spectrum from plot:") +
				                " " + ex.Message);
			}
		}
		
		private void OnF2Pressed(PlotTv tv)
		{
			int selected = tv.Selection.CountSelectedRows();

			if (selected == 0)
				return;

			if (selected == 1)
			{
				TreeIter iter;
				TreeModel model;
				TreePath[] paths = tv.Selection.GetSelectedRows(out model);
				if (this.TreeStore.GetIterFromString(out iter, paths[0].ToString()))
				{
					this.tv.SetCursorOnCell(paths[0], this.tv.Columns[1], (CellRendererText)this.columns[1], true);
				}
				return;
			}

			Dialog dialog = new Dialog(Catalog.GetString("New label"), MainWindow.UI.MainWindow, DialogFlags.Modal,
			                           Stock.Cancel, ResponseType.Cancel, Stock.Ok, ResponseType.Ok);
			dialog.Default = dialog.ActionArea.Children[0];

			HBox hbox = new HBox();
			hbox.BorderWidth = 5;
			hbox.Spacing = 2;

			Label label = new Label(Catalog.GetString("Enter new label:"));
			hbox.PackStart(label);

			Entry entry = new Entry();
			entry.ActivatesDefault = true;
			hbox.PackStart(entry);

			dialog.VBox.PackStart(hbox);

			dialog.ShowAll();
			ResponseType choice = (ResponseType)dialog.Run();

			string new_label = string.Empty;
			if (choice == ResponseType.Ok)
				new_label = entry.Text;

			dialog.Destroy();
			UserInterface.ProcessEvents_();

			if (string.IsNullOrEmpty(new_label))
			    return;

			List<Spectrum> spectra = this.GetSelectedSpectra();
			foreach (Spectrum spectrum in spectra)
			{
				spectrum.Label = new_label;
			}

			this.Update();
		}

		private void OnDoubleClicked(PlotTv tv)
		{
			List<Spectrum> spectra    = this.GetSelectedSpectra();
			List<PlotInfo> plot_infos = this.GetSelectedFigures();

			if (spectra.Count == 1 && this.SpectrumDoubleClicked != null)
				this.SpectrumDoubleClicked(spectra[0]);

			if (plot_infos.Count == 1 && this.FigureDoubleClicked != null)
				this.FigureDoubleClicked(plot_infos[0]);
		}
		
		private void OnButton1Clicked(PlotTv tv)
		{
			if (this.SelectionChanged != null)
				this.SelectionChanged(this.GetSelectedFigures(), this.GetSelectedSpectra(), true);
		}

		private void OnButton2Clicked(PlotTv tv)
		{
			List<Spectrum> spectra = this.GetSelectedSpectra();
			List<string> plot_ids  = new List<string>();
			
			foreach (Spectrum spectrum in spectra)
			{
				if (spectrum.IsPreview)
					continue;
				
				spectrum.IsVisible = !spectrum.IsVisible;
				
				if (!plot_ids.Contains(spectrum.PlotId))
					plot_ids.Add(spectrum.PlotId);
			}
			
			MainWindow.UI.UpdatePlot(plot_ids);
		}
				
		private void OnButton3Clicked(PlotTv tv)
		{
			Console.WriteLine("Right Click");
		}
				
		private void OnSelectionChanged(object o, System.EventArgs e)
		{
			if (this.SelectionChanged != null)
				this.SelectionChanged(this.GetSelectedFigures(), this.GetSelectedSpectra(), false);
		}
		
		public TreeView Tv
		{
			get { return this.tv; }
		}
		
		private TreeStore TreeStore
		{
			get { return (TreeStore)this.tv.Model; }
			set { this.tv.Model = value;           }
		}
		
		public bool IsEmpty
		{
			get 
			{ 
				if (this.TreeStore == null)
					return true;
				
				TreeIter iter;
				return !this.TreeStore.GetIterFirst(out iter);
			}
		}

		private static int _auto_color_ix = 0;
		private static Gdk.Color[] _auto_colors = null;
		private static readonly string[] _auto_color_strings = new string[] { "black", "blue", "red", "green", "yellow", "orange", "magenta", "peru", "brown" };
		
		public static Gdk.Color NextAutoColor
		{
			get
			{
				if (_auto_colors == null)
				{
					_auto_colors = new Gdk.Color[_auto_color_strings.Length];
					for (int ix=0; ix < _auto_color_strings.Length; ++ix)
					{
						Gdk.Color c = Gdk.Color.Zero;
						Gdk.Color.Parse(_auto_color_strings[ix], ref c);
						_auto_colors[ix] = c;
					}
				}
				return _auto_colors[_auto_color_ix++ % _auto_color_strings.Length];
			}
		}
	}	
}