// 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.PdfPrint;
using Hiperscan.Unix;
using Hiperscan.ApoIdentCore;
using Hiperscan.ApoIdentCore.Reporting.ReportData;
using Hiperscan.ApoIdentCore.Misc;
using Hiperscan.ApoIdentCore.Reporting.LabelPrint;
using Hiperscan.ApoIdentCommon.ApoConfig;

namespace Hiperscan.Extensions.ApoIdent
{

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

        private struct SearchParameters
        {
            public string Name;
            public string LatinName;
            public string TestId;
            public string Pzn;
            public string Operator;
            public string ProducerDistributor;
            public string Batch;
            public string Comment;
            public DateTime ExpiryDateFrom;
            public DateTime ExpiryDateTo;
            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 expiry_date_from_day_combo = null;
        [Widget] Gtk.ComboBox expiry_date_from_month_combo = null;
        [Widget] Gtk.ComboBox expiry_date_from_year_combo = null;

        [Widget] Gtk.ComboBox expiry_date_to_day_combo = null;
        [Widget] Gtk.ComboBox expiry_date_to_month_combo = null;
        [Widget] Gtk.ComboBox expiry_date_to_year_combo = null;

        [Widget] Gtk.Entry substance_name_entry = null;
        [Widget] Gtk.Entry test_id_entry = null;
        [Widget] Gtk.Entry operator_entry = null;
        [Widget] Gtk.Entry producer_distributor_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_from_month_combo = null;
        [Widget] Gtk.ComboBox adv_timestamp_from_year_combo = null;

        [Widget] Gtk.ComboBox adv_timestamp_to_day_combo = null;
        [Widget] Gtk.ComboBox adv_timestamp_to_month_combo = null;
        [Widget] Gtk.ComboBox adv_timestamp_to_year_combo = null;


        [Widget] Gtk.ComboBox adv_expiry_date_from_day_combo = null;
        [Widget] Gtk.ComboBox adv_expiry_date_from_month_combo = null;
        [Widget] Gtk.ComboBox adv_expiry_date_from_year_combo = null;

        [Widget] Gtk.ComboBox adv_expiry_date_to_day_combo = null;
        [Widget] Gtk.ComboBox adv_expiry_date_to_month_combo = null;
        [Widget] Gtk.ComboBox adv_expiry_date_to_year_combo = 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 producer_distributor_label = null;
        [Widget] Gtk.Label pzn_label = null;
        [Widget] Gtk.Label expiry_date_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 Run(Gtk.Window parent, AppControl control)
        {
            return new QueryDialog(parent, control);
        }

        private QueryDialog(Gtk.Window parent, AppControl control)
        {
            this.Control = control;
            this.Control.ProfileManager.Stash();

            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);

            ApoIdent.ApoIdentApplication.SetWmcClassName(this.query_dialog);

            this.query_dialog.TransientFor = parent;
            this.InitializeDialog();
            this.query_dialog.DeleteEvent += this.OnDeleteEvent;
            this.query_dialog.IconList = ApoIdentStock.FinderIcons;
            this.query_dialog.WidthRequest = 790;
            wait_window.Destroy();

            GuiDesigner.SetSchema(this.query_dialog);
            this.query_dialog.ShowAll();
            this.query_dialog.Run();
            this.query_dialog.Destroy();
        }

        private void InitializeDialog()
        {
            if (this.Control.ProfileManager.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 (ProfileSettings profile in this.Control.ProfileManager.AllProfiles.Values)
                    this.config_combo.AppendText(profile.Name);

                this.config_combo.Active = this.Control.ProfileManager.CurrentIndex(this.Control.ProfileManager.CurrentProfile) + 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.substance_name_entry.ActivatesDefault = true;
            this.operator_entry.ActivatesDefault = true;
            this.producer_distributor_entry.ActivatesDefault = true;
            this.comment_entry.ActivatesDefault = true;
            this.pzn_entry.ActivatesDefault = true;
            this.test_id_entry.ActivatesDefault = true;

            DateTime from_date = new DateTime(2010, 1, 1);
            DateTime to_date = new DateTime(2035, 12, 31);

            this.InitExpiryDateComboboxes(from_date, to_date);
            this.InitAdvancedExpiryDateComboboxes(from_date, to_date);
            this.InitAdvancedTimestampComboboxes(from_date, to_date);

            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),                        //Producer - Distributor
                                        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("Producer/distributor"), new CellRendererText(), "text", Store.ProducerDistributor);
            this.tv.AppendColumn(Catalog.GetString("Batch"), new CellRendererText(), "text", Store.Batch);
            this.tv.AppendColumn(Catalog.GetString("Test number"), new CellRendererText(), "text", Store.TestId);
            this.tv.AppendColumn(Catalog.GetString("PPN"), new CellRendererText(), "text", Store.Ppn);
            this.tv.AppendColumn(Catalog.GetString("Timestamp"), new CellRendererText(), "text", Store.Timestamp);
            this.tv.AppendColumn(Catalog.GetString("Expiry date"), 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.ProducerDistributor, this.CompareProducersDistributors);
            this.store.SetSortFunc((int)Store.Batch, this.CompareBatches);
            this.store.SetSortFunc((int)Store.TestId, this.CompareTestIds);
            this.store.SetSortFunc((int)Store.Ppn, 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 void InitExpiryDateComboboxes(DateTime from_date, DateTime to_date)
        {
            this.SetDayComboBox(this.expiry_date_from_day_combo, from_date);
            this.expiry_date_from_day_combo.Active = from_date.Day - 1;
            this.expiry_date_from_month_combo.Active = from_date.Month - 1;
            this.expiry_date_from_year_combo.Active = from_date.Year - 2010;
            this.expiry_date_from_month_combo.Changed += this.OnExpiryDateComboChanged;
            this.expiry_date_from_year_combo.Changed += this.OnExpiryDateComboChanged;

            this.SetDayComboBox(this.expiry_date_to_day_combo, to_date);
            this.expiry_date_to_day_combo.Active = to_date.Day - 1;
            this.expiry_date_to_month_combo.Active = to_date.Month - 1;
            this.expiry_date_to_year_combo.Active = to_date.Year - 2010;
            this.expiry_date_to_month_combo.Changed += this.OnExpiryDateComboChanged;
            this.expiry_date_to_year_combo.Changed += this.OnExpiryDateComboChanged;
        }

        private void OnExpiryDateComboChanged(object sender, EventArgs e)
        {
            if ((sender as ComboBox).Name.Contains("_from_"))
            {
                DateTime tmp = new DateTime(this.expiry_date_from_year_combo.Active + 2010,
                                            this.expiry_date_from_month_combo.Active + 1, 1);
                this.UpdateDayCombobox(this.expiry_date_from_day_combo, tmp);
            }
            else
            {
                DateTime tmp = new DateTime(this.expiry_date_to_year_combo.Active + 2010,
                                            this.expiry_date_to_month_combo.Active + 1, 1);
                this.UpdateDayCombobox(this.expiry_date_to_day_combo, tmp);
            }
        }

        private void InitAdvancedExpiryDateComboboxes(DateTime from_date, DateTime to_date)
        {
            this.SetDayComboBox(this.adv_expiry_date_from_day_combo, from_date);
            this.adv_expiry_date_from_day_combo.Active = from_date.Day - 1;
            this.adv_expiry_date_from_month_combo.Active = from_date.Month - 1;
            this.adv_expiry_date_from_year_combo.Active = from_date.Year - 2010;
            this.adv_expiry_date_from_month_combo.Changed += this.OnAdvExpiryDateComboChanged;
            this.adv_expiry_date_from_year_combo.Changed += this.OnAdvExpiryDateComboChanged;

            this.SetDayComboBox(this.adv_expiry_date_to_day_combo, to_date);
            this.adv_expiry_date_to_day_combo.Active = to_date.Day - 1;
            this.adv_expiry_date_to_month_combo.Active = to_date.Month - 1;
            this.adv_expiry_date_to_year_combo.Active = to_date.Year - 2010;
            this.adv_expiry_date_to_month_combo.Changed += this.OnAdvExpiryDateComboChanged;
            this.adv_expiry_date_to_year_combo.Changed += this.OnAdvExpiryDateComboChanged;
        }

        private void OnAdvExpiryDateComboChanged(object sender, EventArgs e)
        {
            if ((sender as ComboBox).Name.Contains("_from_"))
            {
                DateTime tmp = new DateTime(this.adv_expiry_date_from_year_combo.Active + 2010,
                                            this.adv_expiry_date_from_month_combo.Active + 1, 1);
                this.UpdateDayCombobox(this.adv_expiry_date_from_day_combo, tmp);
            }
            else
            {
                DateTime tmp = new DateTime(this.adv_expiry_date_to_year_combo.Active + 2010,
                                            this.adv_expiry_date_to_month_combo.Active + 1, 1);
                this.UpdateDayCombobox(this.adv_expiry_date_to_day_combo, tmp);
            }
        }

        private void InitAdvancedTimestampComboboxes(DateTime from_date, DateTime to_date)
        {
            this.SetDayComboBox(this.adv_timestamp_from_day_combo, from_date);
            this.adv_timestamp_from_day_combo.Active = from_date.Day - 1;
            this.adv_timestamp_from_month_combo.Active = from_date.Month - 1;
            this.adv_timestamp_from_year_combo.Active = from_date.Year - 2010;
            this.adv_timestamp_from_month_combo.Changed += this.OnAdvTimeStampComboChanged;
            this.adv_timestamp_from_year_combo.Changed += this.OnAdvTimeStampComboChanged;

            this.SetDayComboBox(this.adv_timestamp_to_day_combo, to_date);
            this.adv_timestamp_to_day_combo.Active = to_date.Day - 1;
            this.adv_timestamp_to_month_combo.Active = to_date.Month - 1;
            this.adv_timestamp_to_year_combo.Active = to_date.Year - 2010;
            this.adv_timestamp_to_month_combo.Changed += this.OnAdvTimeStampComboChanged;
            this.adv_timestamp_to_year_combo.Changed += this.OnAdvTimeStampComboChanged;
        }

        private void OnAdvTimeStampComboChanged(object sender, EventArgs e)
        {
            if ((sender as ComboBox).Name.Contains("_from_"))
            {
                DateTime tmp = new DateTime(this.adv_timestamp_from_year_combo.Active + 2010,
                                            this.adv_timestamp_from_month_combo.Active + 1, 1);
                this.UpdateDayCombobox(this.adv_timestamp_from_day_combo, tmp);
            }
            else
            {
                DateTime tmp = new DateTime(this.adv_timestamp_to_year_combo.Active + 2010,
                                            this.adv_timestamp_to_month_combo.Active + 1, 1);
                this.UpdateDayCombobox(this.adv_timestamp_to_day_combo, tmp);
            }
        }

        private int SetDayComboBox(Gtk.ComboBox day_combo, DateTime date)
        {

            int days = DateTime.DaysInMonth(date.Year, date.Month);

            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 < days; i++)
            {
                day_list.AppendValues((i + 1).ToString("D2"));
            }
            day_combo.Model = day_list;

            return days;
        }

        private void UpdateDayCombobox(ComboBox day_combo, DateTime date)
        {
            int old_days = day_combo.Active;
            int new_days = this.SetDayComboBox(day_combo, date) - 1;

            if (new_days < old_days)
                old_days = new_days;

            day_combo.Active = old_days;
        }

        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.Control.LabelPrinterSettings.HasPrinter;

                return is_match;
            }
        }

        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 CompareProducersDistributors(TreeModel model, TreeIter a, TreeIter b)
        {
            string s1 = (string)model.GetValue(a, (int)Store.ProducerDistributor);
            string s2 = (string)model.GetValue(b, (int)Store.ProducerDistributor);
            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.Ppn));
            string s2 = StringHelper.CleanPzn((string)model.GetValue(b, (int)Store.Ppn));
            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 this.SpecialDateTimeComparer(d1, d2);
            }
            catch
            {
                return 0;
            }
        }

        private int SpecialDateTimeComparer(DateTime d1, DateTime d2)
        {
            if (d1 == I18nDateFormat.EmptyDate)
                return 1;

            if (d2 == I18nDateFormat.EmptyDate)
                return -1;

            return DateTime.Compare(d1, d2);
        }

        [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.ExpiryDateFrom > sp.ExpiryDateTo || sp.TimeStampFrom > sp.TimeStampTo)
            {
                Plugin.host.ShowInfoMessage(this.query_dialog,
                                            Catalog.GetString("Cannot start query: The 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 (ProfileSettings profile in this.Control.ProfileManager.AllProfiles.Values)
                    {
                        if (directories.Contains(profile.Archive) == false)
                            directories.Add(profile.Archive);
                    }
                }
                else
                {
                    if (this.Control.ProfileManager.CurrentProfile != null)
                        directories.Add(this.Control.ProfileManager.CurrentProfile.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 (FileIO.ExistsDirectory(directory))
                        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.ProduceAndDistributor,
                                            meta_data.Batch,
                                            meta_data.TestId,
                                            meta_data.Ppn,
                                            meta_data.Date.ToString("d"),
                                            this.GetExpiryDate(meta_data.MinExpiryDate),
                                            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.Operator = String.Empty;
                    sp.ProducerDistributor = String.Empty;
                    sp.Batch = String.Empty;
                    sp.Comment = String.Empty;
                    sp.ExpiryDateFrom = DateTime.MinValue;
                    sp.ExpiryDateTo = 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.Operator = String.Empty;
                    sp.ProducerDistributor = String.Empty;
                    sp.Batch = String.Empty;
                    sp.Comment = String.Empty;

                    sp.ExpiryDateFrom = new DateTime(this.expiry_date_from_year_combo.Active + 2010,
                                                        this.expiry_date_from_month_combo.Active + 1,
                                                        this.expiry_date_from_day_combo.Active + 1, 0, 0, 0);


                    sp.ExpiryDateTo = new DateTime(this.expiry_date_to_year_combo.Active + 2010,
                                                        this.expiry_date_to_month_combo.Active + 1,
                                                        this.expiry_date_to_day_combo.Active + 1,
                                                        0, 0, 0).AddDays(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.Operator = this.operator_entry.Text;
                    sp.ProducerDistributor = this.producer_distributor_entry.Text;
                    sp.Batch = this.producer_distributor_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,
                                                    0, 0, 0).AddDays(1).AddTicks(-1);

                    sp.ExpiryDateFrom = new DateTime(this.adv_expiry_date_from_year_combo.Active + 2010,
                                                        this.adv_expiry_date_from_month_combo.Active + 1,
                                                        this.adv_expiry_date_from_day_combo.Active + 1, 0, 0, 0);

                    sp.ExpiryDateTo = new DateTime(this.adv_expiry_date_to_year_combo.Active + 2010,
                                                        this.adv_expiry_date_to_month_combo.Active + 1,
                                                        this.adv_expiry_date_to_day_combo.Active + 1,
                                                         0, 0, 0).AddDays(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.Ppn).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.Ppn).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.Operator) == false)
            {
                if (info.Operator.ToLower().Contains(sp.Operator.ToLower()) == false)
                    return false;
            }

            if (!string.IsNullOrEmpty(sp.ProducerDistributor) &&
                !string.IsNullOrEmpty(sp.Batch))
            {
                if (!info.Distributor.ToLower().Contains(sp.ProducerDistributor.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.ExpiryDateFrom || info.ExpireDate > sp.ExpiryDateTo)
                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);

            ApoIdent.ApoIdentApplication.SetWmcClassName(this.info_dialog);

            this.info_dialog.TransientFor = this.query_dialog;

            this.test_id_label.Text = info.TestId;
            this.timestamp_label.Text = info.Date.ToString("d");
            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.Operator;
            this.producer_distributor_label.Text = info.ProduceAndDistributor;
            this.pzn_label.Text = info.Ppn;
            this.expiry_date_label.Text = this.GetExpiryDate(info.MinExpiryDate);
            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;

            GuiDesigner.SetSchema(this.info_dialog);
            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.Print(this.Control.LabelPrinterSettings, info);
            }
            catch (IOException ex)
            {
                Plugin.host.ShowInfoMessage(this.query_dialog,
                                            Catalog.GetString("Cannot open report \"{0}\": {1}"),
                                            pdf_file,
                                            ex.Message);
            }
            catch (Exception ex)
            {
                Plugin.host.ShowInfoMessage(this.query_dialog,
                                            Catalog.GetString("Cannot print label: {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 == ApoIdentCore.Misc.OperatingSystem.Linux)
                {
                    chooser.AddFilter(libre_filter);
                    chooser.AddFilter(excel_filter);
                }
                else
                {
                    chooser.AddFilter(excel_filter);
                    chooser.AddFilter(libre_filter);
                }



                if (String.IsNullOrEmpty(this.Control.GuiSettings.LastQueryExportFolder) == true ||
                    false == FileIO.ExistsDirectory(this.Control.GuiSettings.LastQueryExportFolder))
                    chooser.SetCurrentFolder(this.Control.FileSystem.DefaultDataExportDir);
                else
                    chooser.SetCurrentFolder(this.Control.GuiSettings.LastQueryExportFolder);

                chooser.CurrentName = Catalog.GetString("query") + "_" + DateTime.Now.ToString("yyyy-MM-dd_HH-mm") + ".csv";
                chooser.DoOverwriteConfirmation = true;

                chooser.IconList = ApoIdentStock.FinderIcons;

                GuiDesigner.SetSchema(chooser);

                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 (var writer = new FileIO.FileStreamWriter(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("Producer") + "\t");
                            sb.Append(Catalog.GetString("Batch") + "\t");
                            sb.Append(Catalog.GetString("Test number") + "\t");
                            sb.Append(Catalog.GetString("PPN") + "\t");
                            sb.Append(Catalog.GetString("Timestamp") + "\t");
                            sb.Append(Catalog.GetString("Expiry date") + "\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.ProduceAndDistributor + "\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.Ppn, is_excel ? "\t" : "\"\t");
                                sb.Append(info.Date.ToString("d") + "\t");
                                sb.Append(this.GetExpiryDate(info.MinExpiryDate) + "\n");

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

        private string GetExpiryDate(DateTime date)
        {
            if (date == I18nDateFormat.EmptyDate)
                return "-";

            return date.ToString("d");
        }

        private void OnConfigChanged(object sender, EventArgs e)
        {
            if (this.Control.ProfileManager.AllProfiles.ContainsKey(this.config_combo.ActiveText) == false)
                return;

            this.Control.ProfileManager.CurrentProfile = this.Control.ProfileManager.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.Control.GuiSettings.LastQueryCopyFolder) == true ||
                    false == FileIO.ExistsDirectory(this.Control.GuiSettings.LastQueryCopyFolder))
                    chooser.SetCurrentFolder(this.Control.FileSystem.DefaultDataExportDir);
                else
                    chooser.SetCurrentFolder(this.Control.GuiSettings.LastQueryCopyFolder);

                chooser.IconList = ApoIdentStock.FinderIcons;

                GuiDesigner.SetSchema(chooser);

                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 (FileIO.ExistsFile(csv_file) && csv_checkbutton.Active)
                                sources.Add(csv_file);

                            if (FileIO.ExistsFile(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 == ApoIdentCore.Misc.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);
                                FileIO.CopyFile(source_file, destination_file, true);
                            }
                        }

                        this.Control.GuiSettings.LastQueryCopyFolder = chooser.CurrentFolder;
                    }
                    catch (Exception ex)
                    {
                        Plugin.host.ShowInfoMessage(this.query_dialog,
                                                    Catalog.GetString("Cannot copy files: {0}"),
                                                    ex.Message);
                    }
                }

                chooser.Destroy();
            }
        }

        internal void OnCloseButtonClicked(object o, EventArgs e)
        {
            this.close_clicked = true;
            this.Control.ProfileManager.Unstash();
            this.query_dialog.Destroy();
        }

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

        private void ReadQueryMetadata()
        {
            try
            {
                this.Queries = new Dictionary<string, List<ReportInfo>>();
                List<string> directories = new List<string>();

                foreach (ProfileSettings profile in this.Control.ProfileManager.AllProfiles.Values)
                {
                    if (false == FileIO.ExistsDirectory(profile.Archive))
                        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 FileIO.GetDirectories(directory))
            {
                if (folder_pattern.Match(Path.GetFileName(sub_dir)).Success == false)
                    continue;

                string query_dir = Path.Combine(sub_dir, ProfileSettings.QUERY_DIR);
                if (false == FileIO.ExistsDirectory(query_dir))
                    continue;

                foreach (string xml_file in FileIO.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 AppControl Control { get; set; }
        private Dictionary<string, List<ReportInfo>> Queries { get; set; }
    }
}
