// Created with MonoDevelop
//
//    QuickStep: Acquire, view and identify spectra 
//    Copyright (C) 2009 Thomas Klose & Torsten Schache, 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.Collections.Generic;
using System.Diagnostics;
using System.Text;
using Gtk;
using Glade;

using Hiperscan.FileIO;
using Hiperscan.Spectroscopy;
using Hiperscan.Unix;
using Hiperscan.I18n;
using System.Text.RegularExpressions;
using Hiperscan.PdfPrint;

namespace Hiperscan.Extensions.ApoIdent
{

	public class QueryDialog
	{
		
		private enum Store : int
		{
			Name,
			LatinName,
			IsMatch,
			Supplier,
			Batch,
			TestId,
			Pzn,
			Timestamp,
			ExpireDate,
			QueryMetaData,
			DateTime,
		}

		private struct SearchParameters
		{
			public string Name;
			public string LatinName;
			public string TestId;
			public string Pzn;
			public string User;
			public string Supplier;
			public string Batch;
			public string Comment;
			public DateTime UseByFrom;
			public DateTime UseByTo;
			public DateTime TimeStampFrom;
			public DateTime TimeStampTo;
			public bool QueryIsDisJointed;
		}
		
		
		#region GTK-Widgets Definitions
		
		[Widget] Gtk.Dialog query_dialog = null;
		[Widget] Gtk.Dialog info_dialog  = null;
		[Widget] Gtk.Notebook search_notebook = null;

		[Widget] Gtk.Entry substance_name_or_id_entry 	= null;
		
		[Widget] Gtk.ComboBox month_combo11 = null;
		[Widget] Gtk.ComboBox month_combo12 = null;
		[Widget] Gtk.ComboBox year_combo11  = null;
		[Widget] Gtk.ComboBox year_combo12  = null;
		
		[Widget] Gtk.Entry substance_name_entry	= null;
		[Widget] Gtk.Entry test_id_entry		= null;
		[Widget] Gtk.Entry user_entry     		= null;
		[Widget] Gtk.Entry supplier_entry 		= null;
		[Widget] Gtk.Entry comment_entry  		= null;
		[Widget] Gtk.Entry pzn_entry  			= null;
		
		
		[Widget] Gtk.ComboBox adv_timestamp_from_day_combo 		= null;
		[Widget] Gtk.ComboBox adv_timestamp_to_day_combo 		= null;
		[Widget] Gtk.ComboBox adv_timestamp_from_month_combo 	= null;
		[Widget] Gtk.ComboBox adv_timestamp_to_month_combo 		= null;
		[Widget] Gtk.ComboBox adv_timestamp_from_year_combo  	= null;
		[Widget] Gtk.ComboBox adv_timestamp_to_year_combo  		= null;
		[Widget] Gtk.ComboBox month_combo23 = null;
		[Widget] Gtk.ComboBox month_combo24 = null;
		[Widget] Gtk.ComboBox year_combo23  = null;
		[Widget] Gtk.ComboBox year_combo24  = null;
		[Widget] Gtk.ComboBox config_combo  = null;
		
		[Widget] Gtk.Button execute_button = null;
		[Widget] Gtk.Button save_button    = null;
		[Widget] Gtk.Button copy_button    = null;
		[Widget] Gtk.Button info_button    = null;
		[Widget] Gtk.Button report_button  = null;
		[Widget] Gtk.Button print_button   = null;
		[Widget] Gtk.Button label_button   = null;
		
		[Widget] Gtk.Label result_label        		= null;
		[Widget] Gtk.Label test_id_label       		= null;
		[Widget] Gtk.Label timestamp_label     		= null;
		[Widget] Gtk.Label primary_name_label  		= null;
		[Widget] Gtk.Label latin_name_label    		= null;
		[Widget] Gtk.Label is_match_label 			= null;
		[Widget] Gtk.Label user_name_label     		= null;
		[Widget] Gtk.Label supplier_label      		= null;
		[Widget] Gtk.Label pzn_label      			= null;
		[Widget] Gtk.Label use_by_label        		= null;
		[Widget] Gtk.Label batch_label         		= null;
		[Widget] Gtk.Label comment_label       		= null;
		[Widget] Gtk.Label spectrum_file_label 		= null;
		[Widget] Gtk.Label report_file_label   		= null; 
		
		[Widget] Gtk.HBox config_hbox = null;
		[Widget] Gtk.ProgressBar progressbar = null;
		[Widget] Gtk.ScrolledWindow scrolledwindow = null;
		
		#endregion
		
		
		
		private ListStore store;
		private TreeView  tv;
		private volatile bool close_clicked = false;
		
		//private FinderWindow finder_window;

        public QueryDialog(Configurations config, Gtk.Window parent)
		{
			this.Config = config;

            WaitWindow wait = new WaitWindow(parent,
                                           Catalog.GetString("Starting query dialog. Please wait..."), 600);

			Glade.XML gui = new Glade.XML(null, "ApoIdent.Resources.Glade.FinderWindow.glade", "query_dialog", "Hiperscan");
			gui.Autoconnect(this);

			this.query_dialog.SetWmclass(Plugin.name.ToLower(), 
			                             Plugin.name.ToLower());

            this.query_dialog.TransientFor = parent;
			this.InitializeDialog();
			this.query_dialog.DeleteEvent += this.OnDeleteEvent;

            this.query_dialog.IconList = ApoIdentStock.FinderIcons;
			this.query_dialog.ShowAll();

            wait.Destroy();
		}

		private void InitializeDialog()
		{
			if (this.Config.Profiles.Count > 1)
			{
				ListStore store = new ListStore(typeof(string));
				CellRendererText text = new CellRendererText();
				this.config_combo.Clear();
				this.config_combo.PackStart(text, false);
				this.config_combo.SetAttributes(text, "text", 0);
				this.config_combo.Model = store;
				this.config_combo.AppendText(Catalog.GetString("All profiles"));
				foreach (Profile profile in this.Config.Profiles.Values)
					this.config_combo.AppendText(profile.Name);

                this.config_combo.Active = this.Config.Profiles.CurrentIndex(this.Config.Profiles.Current) + 1;
			}
			else
			{
				this.config_hbox.Visible 	= false;
				this.config_hbox.NoShowAll 	= true;
			}
			
			int year  = DateTime.Now.Year;
			int month = DateTime.Now.Month;
			
			this.execute_button.CanDefault = true;
			this.query_dialog.Default = this.execute_button;
			this.substance_name_or_id_entry.ActivatesDefault = true;
			this.substance_name_or_id_entry.HasFocus = true;
			
			this.month_combo11.Active = month - 1;
			this.month_combo12.Active = month - 1;
			this.year_combo11.Active  = year-2010;
			this.year_combo12.Active  = year-2010;

			this.substance_name_entry.ActivatesDefault 	= true;
			this.user_entry.ActivatesDefault     		= true;
			this.supplier_entry.ActivatesDefault 		= true;
			this.comment_entry.ActivatesDefault  		= true;
			this.pzn_entry.ActivatesDefault		  		= true;
			this.test_id_entry.ActivatesDefault			= true;
			
			
			this.adv_timestamp_from_month_combo.Active = 0;
			this.adv_timestamp_to_month_combo.Active = this.adv_timestamp_to_month_combo.Model.IterNChildren()-1;
			this.adv_timestamp_from_year_combo.Active  = 0;
			this.adv_timestamp_to_year_combo.Active  = this.adv_timestamp_to_year_combo.Model.IterNChildren()-1;
			SetDaysComboBox(this.adv_timestamp_from_day_combo,this.adv_timestamp_from_month_combo, this.adv_timestamp_from_year_combo);
			this.adv_timestamp_from_day_combo.Active = 0;
			SetDaysComboBox(this.adv_timestamp_to_day_combo,this.adv_timestamp_to_month_combo, this.adv_timestamp_to_year_combo);
			this.adv_timestamp_to_day_combo.Active =this.adv_timestamp_to_day_combo.Model.IterNChildren()-1;	
			this.adv_timestamp_from_month_combo.Changed += OnAdvTimeStampComboChanged;
			this.adv_timestamp_from_year_combo.Changed += OnAdvTimeStampComboChanged;
			this.adv_timestamp_to_month_combo.Changed += OnAdvTimeStampComboChanged;
			this.adv_timestamp_to_year_combo.Changed += OnAdvTimeStampComboChanged;
			
			this.month_combo23.Active = 0;
			this.month_combo24.Active = this.month_combo24.Model.IterNChildren()-1;			
			this.year_combo23.Active  = 0;
			this.year_combo24.Active  = this.year_combo24.Model.IterNChildren()-1;
			
			this.progressbar.Hide();
			this.result_label.Hide();
			this.save_button.Sensitive = false;
			this.copy_button.Sensitive = false;
			
			this.tv = new TreeView();
			this.store = new ListStore (typeof(string),						//Specimen name
			                            typeof(string),						//Specimen latin name
			                            typeof(bool),						//Identification result
			                            typeof(string),						//Supplier
			                            typeof(string),						//Batch Id
			                            typeof(string),						//Test Id
			                            typeof(string),						//Pzn
			                            typeof(string),						//TimeStamp
			                            typeof(string),						//Expire Date
										typeof(ReportInfo),	
			                            typeof(DateTime));
			
			this.tv.RulesHint = true;
			this.tv.AppendColumn(Catalog.GetString("Primary Name"), 			new CellRendererText(), "text"		, Store.Name);
			this.tv.AppendColumn(Catalog.GetString("Latin Name"),   			new CellRendererText(), "text"		, Store.LatinName);
			this.tv.AppendColumn(Catalog.GetString("Identified"),				new CellRendererToggle(), "active"	, Store.IsMatch);
			this.tv.AppendColumn(Catalog.GetString("Manufacturer/Supplier"),	new CellRendererText(), "text"		, Store.Supplier);
			this.tv.AppendColumn(Catalog.GetString("Batch Number"), 			new CellRendererText(), "text"		, Store.Batch);
			this.tv.AppendColumn(Catalog.GetString("Test ID"),      			new CellRendererText(), "text"		, Store.TestId);
			this.tv.AppendColumn(Catalog.GetString("PZN"),   					new CellRendererText(), "text"		, Store.Pzn);
			this.tv.AppendColumn(Catalog.GetString("Timestamp"),    			new CellRendererText(), "text"		, Store.Timestamp);
			this.tv.AppendColumn(Catalog.GetString("Use-by Date/Shelf Life"),   new CellRendererText(), "text"		, Store.ExpireDate);
			
			this.tv.Columns[(int)Store.IsMatch].Visible = false;
			
			foreach (TreeViewColumn col in tv.Columns)
			{
				col.Clickable = true;
				col.SortOrder = SortType.Ascending;
				
				col.Clicked += this.OnTVColumnClicked;
			}

			this.store.SetSortFunc((int)Store.Name,       this.CompareNames);
			this.store.SetSortFunc((int)Store.LatinName,  this.CompareLatinNames);
			this.store.SetSortFunc((int)Store.IsMatch, 	  this.CompareIsMatches);
			this.store.SetSortFunc((int)Store.Supplier,   this.CompareSuppliers);
			this.store.SetSortFunc((int)Store.Batch,      this.CompareBatches);
			this.store.SetSortFunc((int)Store.TestId,     this.CompareTestIds);
			this.store.SetSortFunc((int)Store.Pzn,		  this.ComparePzns);
			this.store.SetSortFunc((int)Store.Timestamp,  this.CompareTimestamps);
			this.store.SetSortFunc((int)Store.ExpireDate, this.CompareExpireDates);

			this.LastActiveColumnId = (int)Store.Name;
			this.tv.Columns[(int)Store.Name].SortOrder = SortType.Ascending;
			this.store.SetSortColumnId((int)Store.Name, SortType.Ascending);
			this.tv.Columns[(int)Store.Name].SortIndicator = true;
 
			this.tv.Model = this.store;
			
			this.scrolledwindow.Add(this.tv);
			this.scrolledwindow.Sensitive = false;
			
			this.tv.Selection.Changed += delegate(object sender, EventArgs e) {
				TreeSelection selection = sender as TreeSelection;
				bool val = (selection.CountSelectedRows() > 0);
				this.info_button.Sensitive   = val;
				this.report_button.Sensitive = val;
				this.print_button.Sensitive  = val;
				
				
				this.label_button.Sensitive  =  this.CanLabelPrinterSensitive;
			};
			
			this.tv.ButtonPressEvent += this.OnTvButtonPressed;
			this.ReadQueryMetadata();
			
			this.info_button.Sensitive   = false;
			this.report_button.Sensitive = false;
			this.print_button.Sensitive  = false;
			this.label_button.Sensitive  = false;
		}

		private int LastActiveColumnId {get;set;}
		private void OnTVColumnClicked (object sender, EventArgs e)
		{
			Gtk.TreeViewColumn col = sender as Gtk.TreeViewColumn;
			if (col == null)
				return;
			
			int id = 0;
			foreach(Gtk.TreeViewColumn item in this.tv.Columns)
			{
				if (item == col)
					break;
				id++;
			}
			
			if(this.LastActiveColumnId == id)
			{
				if (col.SortOrder == SortType.Ascending)
					col.SortOrder = SortType.Descending;
				else
					col.SortOrder = SortType.Ascending;
			}
			else
			{
				col.SortOrder = SortType.Ascending;
				this.tv.Columns[this.LastActiveColumnId].SortIndicator = false;
				this.LastActiveColumnId = id;
			}

			
			this.store.SetSortColumnId(id, col.SortOrder);
			col.SortIndicator = true;
			
			TreeIter iter;
			if (this.tv.Selection.GetSelected(out iter) == true)
			{
				TreePath path = this.store.GetPath(iter);
				this.tv.ScrollToCell(path, this.tv.Columns[0], false, 0.5f, 0.5f);
			}
		}

		private bool CanLabelPrinterSensitive
		{
			get
			{
				bool is_match = false;

				if (tv.Selection.CountSelectedRows() == 1)		
                    is_match = this.Config.PrinterSettings.HasPrinter;

				return is_match;
			}
		}

		private void SetDaysComboBox(Gtk.ComboBox day_combo, Gtk.ComboBox month_combo, Gtk.ComboBox year_combo)
		{
			int day_old = day_combo.Active;	
			int month_new = month_combo.Active + 1;
			int yea_new = Convert.ToInt32(year_combo.ActiveText);			
			int number_of_days = DateTime.DaysInMonth(yea_new , month_new);
			
			 day_combo.Clear();
        	CellRendererText day_cell = new CellRendererText();
			day_combo.PackStart(day_cell,false);
      		day_combo.AddAttribute(day_cell, "text", 0);
			ListStore day_list = new ListStore(typeof(string));
			for (int i = 0; i < number_of_days; i++) {
				day_list.AppendValues((i + 1).ToString("D2"));
			}		
			day_combo.Model=day_list;
			
			if(day_old != -1)
			{
				if(day_old < day_combo.Model.IterNChildren())
					day_combo.Active = day_old;
				else
					day_combo.Active = day_combo.Model.IterNChildren() - 1;
				
			}
		}

		private int CompareNames(TreeModel model, TreeIter a, TreeIter b)
		{
			string s1 = (string)model.GetValue(a, (int)Store.Name);
			string s2 = (string)model.GetValue(b, (int)Store.Name);
			return string.Compare(s1, s2);
		}

		private int CompareLatinNames(TreeModel model, TreeIter a, TreeIter b)
		{
			string s1 = (string)model.GetValue(a, (int)Store.LatinName);
			string s2 = (string)model.GetValue(b, (int)Store.LatinName);
			return string.Compare(s1, s2);
		}

		private int CompareIsMatches(TreeModel model, TreeIter a, TreeIter b)
		{
			try
			{
				bool b1 = (bool)model.GetValue(a, (int)Store.IsMatch);
				bool b2 = (bool)model.GetValue(b, (int)Store.IsMatch);
				if (b1 == true && b2 == false)
					return -1;
				else
				{
					if(b1 == b2) 
						return 0;
					else
						return 1;
				}
			}
			catch(Exception)
			{
				return 0;
			}
		}

		private int CompareSuppliers(TreeModel model, TreeIter a, TreeIter b)
		{
			string s1 = (string)model.GetValue(a, (int)Store.Supplier);
			string s2 = (string)model.GetValue(b, (int)Store.Supplier);
			return string.Compare(s1, s2);
		}

		private int CompareBatches(TreeModel model, TreeIter a, TreeIter b)
		{
			string s1 = (string)model.GetValue(a, (int)Store.Batch);
			string s2 = (string)model.GetValue(b, (int)Store.Batch);
			return string.Compare(s1, s2);
		}

		private int CompareTestIds(TreeModel model, TreeIter a, TreeIter b)
		{
			try
			{
                long u1 = long.Parse((string)model.GetValue(a, (int)Store.TestId));
                long u2 = long.Parse((string)model.GetValue(b, (int)Store.TestId));
				return u1.CompareTo(u2);
			}
			catch(Exception)
			{
				return 0;
			}
		}

		private int ComparePzns(TreeModel model, TreeIter a, TreeIter b)
		{
			string s1 = StringHelper.CleanPzn((string)model.GetValue(a, (int)Store.Pzn));
			string s2 = StringHelper.CleanPzn((string)model.GetValue(b, (int)Store.Pzn));
			if(s1.Length != s2.Length)
				return s1.Length - s2.Length;
			return String.Compare(s1, s2);
		}

		private int CompareTimestamps(TreeModel model, TreeIter a, TreeIter b)
		{
			try
			{
				DateTime d1 = (DateTime)model.GetValue(a, (int)Store.DateTime);
				DateTime d2 = (DateTime)model.GetValue(b, (int)Store.DateTime);
				return DateTime.Compare(d1, d2);
			}
			catch
			{
				return 0;
			}
		}

		private int CompareExpireDates(TreeModel model, TreeIter a, TreeIter b)
		{
			try
			{
				DateTime d1 = ((ReportInfo)model.GetValue(a, (int)Store.QueryMetaData)).ExpireDate;
				DateTime d2 = ((ReportInfo)model.GetValue(b, (int)Store.QueryMetaData)).ExpireDate;
				return DateTime.Compare(d1, d2);
			}
			catch
			{
				return 0;
			}
		}

		[GLib.ConnectBefore]
		private void OnTvButtonPressed(object o, ButtonPressEventArgs args) 
		{
			if ((args.Event.Button == 1) && (args.Event.Type == Gdk.EventType.TwoButtonPress))
				this.OnInfoButtonClicked(null, EventArgs.Empty);
		}
		
		internal void OnExecuteButtonClicked(object o, EventArgs e)
		{
			this.tv.Sensitive = false;
			this.store.Clear();
			
			this.progressbar.Fraction = 0f;
			this.progressbar.Show();
			this.result_label.Hide();
			
			SearchParameters sp = this.GetSearchParameters();

			if (sp.UseByFrom > sp.UseByTo || sp.TimeStampFrom > sp.TimeStampTo)
			{
				Plugin.host.ShowInfoMessage(this.query_dialog,
				                            Catalog.GetString("Cannot perform query: The search time period is invalid."));
				this.progressbar.Hide();
				this.result_label.Show();
				this.tv.Sensitive = true;
				return;
			}
			
			bool is_empty = true;

			try
			{
				this.query_dialog.GdkWindow.Cursor = new Gdk.Cursor(Gdk.CursorType.Watch);
				List<string> directories = new List<string>();

				if (this.config_combo.Active < 1)
				{
					foreach (Profile profile in this.Config.Profiles.Values)
					{
						if (directories.Contains(profile.Archive) == false)
							directories.Add(profile.Archive);	
					}
				}
				else
				{
                    directories.Add(FilesAndDirs.Archive);
				}

				Console.WriteLine("Query data from following directories:");
				foreach (string directory in directories)
					Console.WriteLine("\t" + directory);
				
				List<ReportInfo> all_meta_data = new List<ReportInfo>();
				foreach (string directory in directories)	
                {
                    if (Directory.Exists(directory) == true)
					    all_meta_data.AddRange(this.Queries[directory]);
                }

				for (int ix = 0; ix < all_meta_data.Count; ++ix)
				{
					if (this.close_clicked)
					{
						this.tv.Sensitive = true;
						return;
					}
					
					if (ix % 10 == 0)
					{
						this.progressbar.Fraction = (double)(ix+1)/(double)all_meta_data.Count;
						Plugin.host.ProcessEvents();
					}
					
					ReportInfo meta_data = all_meta_data[ix];
					DateTime timestamp = meta_data.Date;

					
					if (this.ApplySearchParameters(sp, meta_data, timestamp) == false)
						continue;
					
					this.store.AppendValues(meta_data.DefaultName,
					                        meta_data.LatinName,
					                        meta_data.CustomerResult,
					                        meta_data.Supplier,
					                        meta_data.Batch,
					                        meta_data.TestId,
					                        meta_data.Pzn,
                                            Locale.LocalizedDateTime(meta_data.Date),
                                            Locale.LocalizedUseByDate(meta_data.ExpireDate),
					                        meta_data,
					                        timestamp);
					if (is_empty)
					{
						is_empty = false;
						this.scrolledwindow.Sensitive = true;
						this.save_button.Sensitive = true;
						this.copy_button.Sensitive = true;
					}
				}
			}
			catch (Exception ex)
			{
				Plugin.host.ShowInfoMessage(this.query_dialog,
				                            Catalog.GetString("Cannot extract data from archive: {0}"),
				                            ex.Message);
			}
			finally
			{
				if (this.close_clicked == false)
				{
					this.scrolledwindow.Sensitive = !is_empty;
					this.save_button.Sensitive    = !is_empty;
					this.copy_button.Sensitive    = !is_empty;
					this.progressbar.Hide();
					int n = this.store.IterNChildren();
					this.result_label.Text = string.Format(Catalog.GetString("{0} matches found."), n);
					this.result_label.Show();
					this.query_dialog.GdkWindow.Cursor = new Gdk.Cursor(Gdk.CursorType.Arrow);
				}
			}
			this.tv.Sensitive = true;
		}

		private SearchParameters GetSearchParameters()
		{
			SearchParameters sp = new SearchParameters();
			
			switch (this.search_notebook.CurrentPage)
			{
				
			case 0:
				sp.Name          = this.substance_name_or_id_entry.Text;
				sp.LatinName     = this.substance_name_or_id_entry.Text;
				sp.TestId        = this.substance_name_or_id_entry.Text;
				sp.Pzn			 = this.substance_name_or_id_entry.Text;
				sp.User          = String.Empty;
				sp.Supplier      = String.Empty;
				sp.Batch         = String.Empty;
				sp.Comment       = String.Empty;
				sp.UseByFrom     = DateTime.MinValue;
				sp.UseByTo       = DateTime.MaxValue;
				sp.TimeStampFrom = DateTime.MinValue;
				sp.TimeStampTo   = DateTime.MaxValue;
				sp.QueryIsDisJointed = true;
				break;
				
			case 1:
				sp.Name          = String.Empty;
				sp.LatinName     = String.Empty;
				sp.TestId        = String.Empty;
				sp.Pzn			 = String.Empty;
				sp.User          = String.Empty;
				sp.Supplier      = String.Empty;
				sp.Batch         = String.Empty;
				sp.Comment       = String.Empty;
				sp.UseByFrom     = new DateTime(Convert.ToInt32(this.year_combo11.ActiveText),
				                                this.month_combo11.Active + 1,
				                                1, 0, 0, 0);
				sp.UseByTo       = new DateTime(Convert.ToInt32(this.year_combo12.ActiveText),
				                                this.month_combo12.Active + 1,
				                                1, 23, 59, 59).AddMonths(1).AddDays(-1f);
				sp.TimeStampFrom = DateTime.MinValue;
				sp.TimeStampTo   = DateTime.MaxValue;
				sp.QueryIsDisJointed = false;
				break;
				
			case 2:
				sp.Name          = this.substance_name_entry.Text;
				sp.LatinName     = this.substance_name_entry.Text;
				sp.TestId        = this.test_id_entry.Text;
				sp.Pzn		 	 = this.pzn_entry.Text;
				sp.User          = this.user_entry.Text;
				sp.Supplier      = this.supplier_entry.Text;
				sp.Batch         = this.supplier_entry.Text;
				sp.Comment       = this.comment_entry.Text;
				sp.TimeStampFrom = new DateTime(Convert.ToInt32(this.adv_timestamp_from_year_combo.ActiveText),
				                                this.adv_timestamp_from_month_combo.Active + 1,
				                                this.adv_timestamp_from_day_combo.Active + 1, 
				                                0, 0, 0);
				sp.TimeStampTo   = new DateTime(Convert.ToInt32(this.adv_timestamp_to_year_combo.ActiveText),
				                                this.adv_timestamp_to_month_combo.Active + 1,
				                                this.adv_timestamp_to_day_combo.Active + 1, 
				                                23, 59, 59);
				
				sp.UseByFrom     = new DateTime(Convert.ToInt32(this.year_combo23.ActiveText),
				                                this.month_combo23.Active + 1,
				                                1, 0, 0, 0);
				sp.UseByTo       = new DateTime(Convert.ToInt32(this.year_combo24.ActiveText),
				                                this.month_combo24.Active + 1,
				                                1, 23, 59, 59).AddMonths(1).AddDays(-1f);
				sp.QueryIsDisJointed = false;
				break;
				
			}
			
			return sp;
		}
		
		private bool ApplySearchParameters(SearchParameters sp, ReportInfo info, DateTime timestamp)
		{
			if(sp.QueryIsDisJointed == true)
			{
				if (String.IsNullOrEmpty(sp.Name) == false 		&& 
				    String.IsNullOrEmpty(sp.LatinName) == false &&
				    String.IsNullOrEmpty(sp.TestId) == false 	&&
				    String.IsNullOrEmpty(sp.Pzn) == false)
					{
						if (info.DefaultName.ToLower().Contains(sp.Name.ToLower())    == false  &&
					    	info.LatinName.ToLower().Contains(sp.LatinName.ToLower()) == false	&&
					    	info.TestId.Contains(sp.TestId) == false							&&
					    	StringHelper.CleanPzn(info.Pzn).Contains(StringHelper.CleanPzn(sp.Pzn)) == false)
							return false;
					}
			}
			else
			{
				if (String.IsNullOrEmpty(sp.Name) == false && 
			    	String.IsNullOrEmpty(sp.LatinName) == false)
				{
					if (info.DefaultName.ToLower().Contains(sp.Name.ToLower())    == false &&
					    info.LatinName.ToLower().Contains(sp.LatinName.ToLower()) == false)
						return false;
				} 

				if (String.IsNullOrEmpty(sp.Pzn) == false)
				{
					if (StringHelper.CleanPzn(info.Pzn).Contains(StringHelper.CleanPzn(sp.Pzn)) == false)
						return false;
				}

				if (String.IsNullOrEmpty(sp.TestId) == false)
				{
					if (info.TestId.ToLower().Contains(sp.TestId.ToLower()) == false)
						return false;
				}
			}
			
			if (String.IsNullOrEmpty(sp.User) == false)
			{
				if (info.User.ToLower().Contains(sp.User.ToLower()) == false)
				    return false;
			}
			
			if (!string.IsNullOrEmpty(sp.Supplier) &&
			    !string.IsNullOrEmpty(sp.Batch))
			{
				if (!info.Supplier.ToLower().Contains(sp.Supplier.ToLower()) &&
				    !info.Batch.ToLower().Contains(sp.Batch.ToLower()))
					return false;
			}
			
			if (!string.IsNullOrEmpty(sp.Comment))
			{
				if (!info.Comment.ToLower().Contains(sp.Comment.ToLower()))
				    return false;
			}
			
			if (info.ExpireDate < sp.UseByFrom || info.ExpireDate > sp.UseByTo)
				return false;
			
			if (timestamp < sp.TimeStampFrom || timestamp > sp.TimeStampTo)
				return false;
			
			return true;
		}

		internal void OnInfoButtonClicked(object o, EventArgs e)
		{
			if (this.tv.Selection.CountSelectedRows() != 1)
				return;
			
			TreePath[] paths = this.tv.Selection.GetSelectedRows();
			TreeIter iter;
			this.store.GetIterFromString(out iter, paths[0].ToString());			
            ReportInfo info = this.store.GetValue(iter, (int)Store.QueryMetaData) as ReportInfo;
			
            string pdf_file = info.PdfFileFullName;
			if (string.IsNullOrEmpty(pdf_file))
				return;

			Glade.XML gui = new Glade.XML(null, "ApoIdent.Resources.Glade.FinderWindow.glade", "info_dialog", "Hiperscan");
			gui.Autoconnect(this);

			this.info_dialog.SetWmclass(Plugin.name.ToLower(), 
			                            Plugin.name.ToLower());

            this.info_dialog.TransientFor = this.query_dialog;
			
			this.test_id_label.Text       = info.TestId;
            this.timestamp_label.Text     = Locale.LocalizedDateTime(info.Date);
			this.primary_name_label.Text  = info.DefaultName;
			this.latin_name_label.Text    = info.LatinName;
			this.is_match_label.Text	  = info.CustomerResult == true ? Catalog.GetString("Yes") : Catalog.GetString("No");
 			this.user_name_label.Text     = info.User;
			this.supplier_label.Text      = info.Supplier;
			this.pzn_label.Text			  = info.Pzn;
            this.use_by_label.Text        = Locale.LocalizedUseByDate(info.ExpireDate);
			this.batch_label.Text         = info.Batch;
			this.comment_label.Text       = info.Comment;
            this.spectrum_file_label.Text = info.CsvFilFullName;
			this.report_file_label.Text   = pdf_file;

            this.spectrum_file_label.TooltipText = info.CsvFilFullName;
			this.report_file_label.TooltipText   = pdf_file;

            this.info_dialog.IconList = ApoIdentStock.FinderIcons;
			this.info_dialog.ShowAll();
		}

		internal void OnInfoCloseButtonClicked(object o, EventArgs e)
		{
			this.info_dialog.Destroy();
		}
		
		internal void OnReportButtonClicked(object o, EventArgs e)
		{
			if (this.tv.Selection.CountSelectedRows() != 1)
				return;
			
			TreePath[] paths = this.tv.Selection.GetSelectedRows();
			TreeIter iter;
			this.store.GetIterFromString(out iter, paths[0].ToString());
			
			ReportInfo info = this.store.GetValue(iter, (int)Store.QueryMetaData) as ReportInfo;			
            string pdf_file = info.PdfFileFullName;
			
			if (string.IsNullOrEmpty(pdf_file))
				return;

			try
			{
                string url = "file://" + pdf_file.Replace("\\", "/");
                System.Diagnostics.Process.Start(url);
			}
			catch (Exception ex)
			{
				Plugin.host.ShowInfoMessage(this.query_dialog,
				                            Catalog.GetString("Cannot show report '{0}': {1}"),
				                            pdf_file,
				                            ex.Message);
			}
		}

		internal void OnPrintButtonClicked(object o, EventArgs e)
		{
			if (this.tv.Selection.CountSelectedRows() != 1)
				return;
			
			TreePath[] paths = this.tv.Selection.GetSelectedRows();
			TreeIter iter;
			this.store.GetIterFromString(out iter, paths[0].ToString());
			
			ReportInfo info = this.store.GetValue(iter, (int)Store.QueryMetaData) as ReportInfo;			
            string pdf_file = info.PdfFileFullName;
			if (string.IsNullOrEmpty(pdf_file))
				return;
			
			try
			{
                PdfPrinting.Print(pdf_file);
			}
			catch (Exception ex)
			{
				Plugin.host.ShowInfoMessage(this.query_dialog,
				                            Catalog.GetString("Cannot print report '{0}': {1}"),
				                            pdf_file,
				                            ex.Message);
			}
		}

		private void OnLabelButtonClicked(object o, EventArgs e)
		{
			string pdf_file = String.Empty;
			try
			{
				TreePath[] paths = this.tv.Selection.GetSelectedRows();
				TreeIter iter;
				this.store.GetIterFromString(out iter, paths[0].ToString());			
				ReportInfo info = this.store.GetValue(iter, (int)Store.QueryMetaData) as ReportInfo;

				LabelPrinter label_printer = new LabelPrinter(this.Config.PrinterSettings);
				label_printer.Print(info);
			}
			catch(IOException ex)
			{
				Plugin.host.ShowInfoMessage(this.query_dialog,
				                            Catalog.GetString("Report \"{0}\" cannot be opened: {1}"),
				                            pdf_file,
				                            ex.Message);
			}
			catch(Exception ex)
			{
				Plugin.host.ShowInfoMessage(this.query_dialog,
				                            Catalog.GetString("Label cannot be printed: {0}"),
				                            ex.Message);
			}
		}

		internal void OnSaveButtonClicked(object o, EventArgs e)
		{
			using (FileChooserDialog chooser = new FileChooserDialog(Catalog.GetString("Specify filename"),
			                                                         this.query_dialog,
			                                                         FileChooserAction.Save))
			{
				chooser.AddButton(Stock.Save,   ResponseType.Ok);
				chooser.AddButton(Stock.Cancel, ResponseType.Cancel);
				
				FileFilter excel_filter = new FileFilter();
                excel_filter.Name = Catalog.GetString("CSV files (Excel)");
				excel_filter.AddPattern("*.csv");			


                FileFilter  libre_filter = new FileFilter();
                libre_filter.Name = Catalog.GetString("CSV files (LibreOffice)");
                libre_filter.AddPattern("*.csv");

                if (EnvironmentInfo.OS == OperatingSystem.Linux)
                {
                    chooser.AddFilter(libre_filter);
                    chooser.AddFilter(excel_filter);
                }
                else
                {
                    chooser.AddFilter(excel_filter); 
                    chooser.AddFilter(libre_filter);
                }
        

				
				if (String.IsNullOrEmpty(this.Config.GuiSettings.LastQueryExportFolder) == true || 
					Directory.Exists(this.Config.GuiSettings.LastQueryExportFolder) == false)
                    chooser.SetCurrentFolder(FilesAndDirs.DefaultDataExportDir);
				else
					chooser.SetCurrentFolder(this.Config.GuiSettings.LastQueryExportFolder);
				
				chooser.CurrentName = Catalog.GetString("query") + "_" + DateTime.Now.ToString("yyyy-MM-dd_HH-mm") + ".csv";
				chooser.DoOverwriteConfirmation = true;

                chooser.IconList = ApoIdentStock.FinderIcons;
				chooser.ShowAll();
				int choice = chooser.Run();
				chooser.Hide();
				
				if ((ResponseType)choice == ResponseType.Ok)
				{
					try
					{
                        bool is_excel = chooser.Filter.Name == Catalog.GetString("CSV files (Excel)");

						using (StreamWriter writer = new StreamWriter(chooser.Filename, false, Encoding.Unicode))
						{
							StringBuilder sb = new StringBuilder();
							sb.Append( Catalog.GetString("Primary Name") +  "\t");
							sb.Append( Catalog.GetString("Latin Name") +  "\t");
							sb.Append( Catalog.GetString("Identified") + "\t");
							sb.Append( Catalog.GetString("Supplier") +  "\t");
							sb.Append( Catalog.GetString("Batch Number") +  "\t");
							sb.Append( Catalog.GetString("Test ID") +  "\t");
							sb.Append( Catalog.GetString("PZN") +  "\t");
							sb.Append( Catalog.GetString("Timestamp") +  "\t");
							sb.Append( Catalog.GetString("Use-by Date/Shelf Life") +  "\n");
						
							this.store.Foreach(delegate(TreeModel model, TreePath path, TreeIter iter) {
								ReportInfo info = this.store.GetValue(iter, (int)Store.QueryMetaData) as ReportInfo;

								sb.Append(info.DefaultName + "\t");
								sb.Append(info.LatinName + "\t");
								sb.Append((info.CustomerResult == true ? Catalog.GetString("Yes") : Catalog.GetString("No")) + "\t");
								sb.Append(info.Supplier + "\t");
                                sb.AppendFormat("{0}{1}{2}", is_excel ? "\u0081" : "\"", info.Batch, is_excel ? "\t" : "\"\t");
                                sb.AppendFormat("{0}{1}{2}", is_excel ? "\u0081" : "\"", info.TestId, is_excel ? "\t" : "\"\t");
                                sb.AppendFormat("{0}{1}{2}", is_excel ? "\u0081" : "\"", info.Pzn, is_excel ? "\t" : "\"\t");
                                sb.Append(Locale.LocalizedDateTime(info.Date) + "\t");
                                sb.Append(Locale.LocalizedUseByDate(info.ExpireDate) + "\n");

								return false;
							});
							writer.Write(sb.ToString());
						}
						this.Config.GuiSettings.LastQueryExportFolder = chooser.CurrentFolder;
					}
					catch (Exception ex)
					{
						Plugin.host.ShowInfoMessage(this.query_dialog,
						                            Catalog.GetString("Cannot save query information: {0}"), 
						                            ex.Message);
					}
				}
			}
		}

		private void OnConfigChanged(object sender, EventArgs e)
		{
			if (this.Config.Profiles.ContainsKey(this.config_combo.ActiveText) == false)
				return;
			
            this.Config.Profiles.Current = this.Config.Profiles[this.config_combo.ActiveText];
		}

		internal void OnCopyButtonClicked(object o, EventArgs e)
		{
			using (FileChooserDialog chooser = new FileChooserDialog(Catalog.GetString("Specify directory"),
			                                                         this.query_dialog,
			                                                         FileChooserAction.SelectFolder))
			{
				CheckButton csv_checkbutton = new CheckButton(Catalog.GetString("Copy CSV files"));
				csv_checkbutton.Active = true;
				Alignment align = new Alignment(1f, 0.5f, 0f, 0f);
				align.Add(csv_checkbutton);
				chooser.ActionArea.PackStart(align);
				
				chooser.AddButton(Stock.Ok,     ResponseType.Ok);
				chooser.AddButton(Stock.Cancel, ResponseType.Cancel);
				
				if (string.IsNullOrEmpty(this.Config.GuiSettings.LastQueryCopyFolder) == true ||
					Directory.Exists(this.Config.GuiSettings.LastQueryCopyFolder) == false)
                    chooser.SetCurrentFolder(FilesAndDirs.DefaultDataExportDir);
				else
					chooser.SetCurrentFolder(this.Config.GuiSettings.LastQueryCopyFolder);

                chooser.IconList = ApoIdentStock.FinderIcons;
				chooser.ShowAll();
				int choice = chooser.Run();
				chooser.Hide();
				
				Plugin.host.ProcessEvents();
				
				bool pdf_not_found = false;
				if ((ResponseType)choice == ResponseType.Ok)
				{
					List<string> sources = new List<string>();
					try
					{
						this.store.Foreach(delegate(TreeModel model, TreePath path, TreeIter iter) {
							ReportInfo info = this.store.GetValue(iter, (int)Store.QueryMetaData) as ReportInfo;
							
                            string csv_file = info.CsvFilFullName;
                            string pdf_file = info.PdfFileFullName;
							
							if (File.Exists(csv_file) && csv_checkbutton.Active)
							    sources.Add(csv_file);

							if (File.Exists(pdf_file))
								sources.Add(pdf_file);
							else
								pdf_not_found = true;
							return false;
						});
						
						if (sources.Count == 0)
							throw new Exception(Catalog.GetString("No files found."));
						
						if (pdf_not_found)
							Plugin.host.ShowInfoMessage(this.query_dialog,
							                            Catalog.GetString("At least one report (PDF file) is missing. Copied files will be incomplete."));
						if (EnvironmentInfo.OS == OperatingSystem.Windows)
						{
							WindowsShell.CopyFiles(sources.ToArray(), 
							                       chooser.CurrentFolder, 
							                       Catalog.GetString("Copying query results ..."));
						}
						else
						{
							foreach (string source_file in sources)
							{
								string file_name = new FileInfo(source_file).Name;
								string destination_file = Path.Combine(chooser.CurrentFolder, file_name);
								File.Copy(source_file, destination_file, true);
							}
						}
						
						this.Config.GuiSettings.LastQueryCopyFolder = chooser.CurrentFolder;
					}
					catch (Exception ex)
					{
						Plugin.host.ShowInfoMessage(this.query_dialog,
						                            Catalog.GetString("Cannot copy files: {0}"), 
						                            ex.Message);
					}
				}
			}
		}
		
		internal void OnCloseButtonClicked(object o, EventArgs e)
		{
			this.close_clicked = true;
			this.query_dialog.Destroy();          
		}

		private void OnDeleteEvent(System.Object o, DeleteEventArgs e)
		{
			this.OnCloseButtonClicked(o, e);
			e.RetVal = true;
		}

		private void OnAdvTimeStampComboChanged(object sender, EventArgs e)
		{
			if((sender as ComboBox).Name.Contains("from"))
				SetDaysComboBox(this.adv_timestamp_from_day_combo, this.adv_timestamp_from_month_combo, this.adv_timestamp_from_year_combo);
			else
				SetDaysComboBox(this.adv_timestamp_to_day_combo, this.adv_timestamp_to_month_combo, this.adv_timestamp_to_year_combo);				
		}
                         
		private void ReadQueryMetadata()
		{
			try
			{
				this.Queries = new Dictionary<string, List<ReportInfo>>();
				List<string> directories = new List<string>();
				
				foreach (Profile profile in this.Config.Profiles.Values)
				{     
					if (Directory.Exists(profile.Archive) == false)
                        continue;
					
					if (directories.Contains(profile.Archive) == false)
						directories.Add(profile.Archive);
				}

				foreach (string archive_folder in directories)
				{
					this.Queries.Add(archive_folder, new List<ReportInfo>());
					this.ReadQueryMetadata(archive_folder, archive_folder);
				}
			}
			catch(Exception ex)
			{
				Console.WriteLine("Error while reading query meta data: {0}", ex.Message);
			}
		}
		
		private void ReadQueryMetadata(string directory, string key)
		{
			Regex folder_pattern = new Regex(@"^20\d\d$");

			foreach (string sub_dir in Directory.GetDirectories(directory))
			{
				if (folder_pattern.Match(Path.GetFileName(sub_dir)).Success == false)
					continue;

				string query_dir = Path.Combine(sub_dir, FilesAndDirs.QUERY_DIR);
				if (Directory.Exists(query_dir) == false)
					continue;

				foreach (string xml_file in Directory.GetFiles(query_dir, "*.xml"))
				{
					try
                    {
                        ReportInfo query = ReportInfo.Read(xml_file);
                        this.Queries[key].Add(query);
                    }
                    catch (Exception ex)
                    {
                        Console.WriteLine("Error while reading query meta data: {0}", ex.ToString());
                    }
				}               
			}
		}

        private void OnQueryPaageChanged(object sender, SwitchPageArgs e)
        {
            this.store.Clear();
            this.info_button.Sensitive = false;
            this.report_button.Sensitive = false;
            this.print_button.Sensitive = false;
            this.label_button.Sensitive = false;
            this.save_button.Sensitive = false;
            this.copy_button.Sensitive = false;
            this.progressbar.Hide();
            this.result_label.Hide();
        }

		private Dictionary<string, List<ReportInfo>> Queries    {get; set;}
		private Configurations Config							{get; set;}
	}
}