// 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.Collections.Generic;
using System.IO;
using System.Text;
using System.Text.RegularExpressions;

using Glade;

using Gtk;

using Hiperscan.Common.I18n;
using Hiperscan.PdfPrint;
using Hiperscan.Unix;


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;

        public static QueryDialog Start(Configurations config, Gtk.Window parent, object transaction)
        {
            return new QueryDialog(config, parent, transaction);
        }

        private QueryDialog(Configurations config, Gtk.Window parent, object transaction)
        {
            this.Config = config;
            this.Config.StashAll();
            this.Transaction = transaction;

            WaitWindow wait_window = 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;
            wait_window.Destroy();

            lock (this.Transaction)
            {
                this.query_dialog.Run();
            }
        }

        private void InitializeDialog()
        {
            if (this.Config.Profiles.AllProfiles.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 (Configurations.ProfilePreferences profile in this.Config.Profiles.AllProfiles.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.LabelPrinterSettings.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
            {
                if (this.query_dialog.GdkWindow != null)
                    this.query_dialog.GdkWindow.Cursor = new Gdk.Cursor(Gdk.CursorType.Watch);

                List<string> directories = new List<string>();

                if (this.config_combo.Active < 1)
                {
                    foreach (Configurations.ProfilePreferences profile in this.Config.Profiles.AllProfiles.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.scrolledwindow.Show();
                    this.tv.Show();

                    if (this.query_dialog.GdkWindow != null)
                        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, 0, 0, 0).AddMonths(1).AddTicks(-1);
                    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, 0, 0, 0).AddMonths(1).AddTicks(-1);
                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
            {
                Process.OpenUri(pdf_file);
            }
            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.LabelPrinterSettings);
                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.AllProfiles.ContainsKey(this.config_combo.ActiveText) == false)
                return;
            
            this.Config.Profiles.Current = this.Config.Profiles.AllProfiles[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.Config.UnstashAll();
            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 (Configurations.ProfilePreferences profile in this.Config.Profiles.AllProfiles.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 from file {0}: {1}", xml_file, ex.ToString());
                    }
                }               
            }
        }

        private void OnQueryPageChanged(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;}
        private object Transaction { get; set; }
    }
}