// Created with MonoDevelop
//
//    QuickStep: Acquire, view and identify spectra 
//    Copyright (C) 2018 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 Hiperscan.Unix;
using Gtk;
using Gdk;
using Glade;
using Hiperscan.Extensions.ApoIdent.SubstanceManagementExtension;
using Hiperscan.GtkExtensions.Widgets;
using Hiperscan.Spectroscopy.IdentModule;
using GLib;
using Hiperscan.Extensions.ApoIdent.Widgets;

namespace Hiperscan.Extensions.ApoIdent
{
    public class SubstanceExtensionDialog
    {
        private enum Columns
        {
            Selectable,
            Info,
            SubstanceCategoryPic,
            SubstanceName,
            LatinName,
            SubstanceInfo,
            SubstanceCategory,
            Search,
        }

        [Widget] Gtk.Dialog substance_extension_dialog = null;
        [Widget] Gtk.Entry search_entry = null;

        [Widget] Gtk.TreeView substance_treeview = null;
        [Widget] Gtk.Label explanation_label = null;
        [Widget] Gtk.ComboBox config_combobox = null;

        Gtk.TreeModelFilter FilterModel = null;
        Gtk.ListStore ListStoreModel = null;

        public static SubstanceExtensionDialog Start(ApoIdentApplication apo_ident_app)
        {
            return new SubstanceExtensionDialog(apo_ident_app);
        }

        private SubstanceExtensionDialog(ApoIdentApplication apo_ident_app)
        {
            this.ApoIdentApp = apo_ident_app;

            lock (this.ApoIdentApp.Control.Transaction)
            {
                this.Config = apo_ident_app.Config;
                this.Config.StashAll();
                this.SubstanceDB = apo_ident_app.SubstanceManagement;

                Glade.XML gui = new Glade.XML(null, "substance_extension_dialog.glade", "substance_extension_dialog", "Hiperscan");
                gui.Autoconnect(this);

                this.substance_extension_dialog.SetWmclass(Plugin.name.ToLower(), Plugin.name.ToLower());
                this.substance_extension_dialog.TransientFor = apo_ident_app.MainWindow;

                this.RedPoint = new Pixbuf(null, "red_point.png", 18, 18);
                this.GreyPoint = new Pixbuf(null, "black_point.png", 18, 18);

                this.InitConfigCombo();
                this.InitSubstanceList();
                this.UpdateSubstanceList();

                this.substance_treeview.ButtonPressEvent += this.OnButtonPressEvent;
                this.substance_treeview.MotionNotifyEvent += this.OnMotion;

                this.explanation_label.Text = this.ExplanationText();

                int width = substance_treeview.SizeRequest().Width + 20;
                if (width > WindowSize.RESOLUTION_WIDTH)
                    width = WindowSize.RESOLUTION_WIDTH - 100;

                this.explanation_label.WidthRequest = width;
                this.substance_extension_dialog.HeightRequest = 500;
                this.search_entry.GrabFocus();
                this.search_entry.Activated += (sender, e) => this.ShowAddDialog();

                this.substance_extension_dialog.IconList = ApoIdentStock.FinderIcons;
                while (this.substance_extension_dialog.Run() == (int)ResponseType.Accept)
                {
                    this.ShowAddDialog();
                }

                WaitWindow wait_window = new WaitWindow(this.substance_extension_dialog,
                                                 Catalog.GetString("Please wait while saving changes ..."),
                                                 600);

                if (this.ApoIdentApp.Control.IsDisconnecting == false)
                    this.SubstanceDB.Extension.Save(false);

                this.Config.UnstashAll();

                wait_window.Destroy();
                this.substance_extension_dialog.Destroy();
            }
        }

        private string ExplanationText()
        {
            return Catalog.GetString("Here you can manage substances. You can create new substance entries and " +
                                           "you can control which substance should be shown at the application surface.");
        }

        public void ShowAddDialog()
        {
            AddSubstanceDialog dialog = new AddSubstanceDialog(this);
            if (dialog.ClassificationInfo != null)
                this.AddSubstance(dialog.ClassificationInfo);
        }

        private void InitConfigCombo()
        {
            if (this.Config.Profiles.AllProfiles.Count > 0)
            {
                ListStore config_store = new ListStore(typeof(string));
                CellRendererText config_text = new CellRendererText();
                config_text.Scale = Pango.Scale.Large;
                this.config_combobox.PackStart(config_text, false);
                this.config_combobox.SetAttributes(config_text, "text", 0);
                this.config_combobox.Model = config_store;
                foreach (Configurations.ProfilePreferences item in this.Config.Profiles.AllProfiles.Values)
                    this.config_combobox.AppendText(item.Name);

                this.config_combobox.Active = this.Config.Profiles.CurrentIndex(this.Config.Profiles.Current);
            }

            this.config_combobox.Sensitive = this.Config.Profiles.AllProfiles.Count > 1;
            this.config_combobox.Changed += this.OnConfigComboChanged;
        }

        private void InitSubstanceList()
        {
            CellRendererToggle toggle = new CellRendererToggle();
            toggle.Activatable = true;
            toggle.Toggled += this.OnSelectableToggled;

            CellRendererInfo info = new CellRendererInfo();
            info.Activatable = true;
            info.Toggled += this.OnSubstanceInfoToggled;


            this.substance_treeview.AppendColumn(Catalog.GetString("Selectable"), toggle, "active", Columns.Selectable);
            this.substance_treeview.AppendColumn("   ", info, "active", Columns.Info);
            this.substance_treeview.AppendColumn(Catalog.GetString("Category"), new CellRendererPixbuf(), "pixbuf", Columns.SubstanceCategoryPic);
            this.substance_treeview.AppendColumn(Catalog.GetString("Substance Name"), new CellRendererText(), "text", Columns.SubstanceName);
            this.substance_treeview.AppendColumn(Catalog.GetString("Latin Name"), new CellRendererText(), "text", Columns.LatinName);

            foreach (TreeViewColumn col in substance_treeview.Columns)
            {
                col.Clickable = true;
                col.SortOrder = SortType.Ascending;
                col.Clicked += this.OnTVColumnClicked;
            }

            this.ListStoreModel = new ListStore(typeof(bool),
                                                 typeof(bool),
                                                 typeof(Gdk.Pixbuf),
                                                 typeof(string),
                                                 typeof(string),
                                                 typeof(SubstanceInfo),
                                                 typeof(SubstanceCategory),
                                                 typeof(HashSet<string>));


            this.ListStoreModel.SetSortFunc((int)Columns.Selectable, this.CompareSelectables);
            this.ListStoreModel.SetSortFunc((int)Columns.Info, this.CompareInfos);
            this.ListStoreModel.SetSortFunc((int)Columns.SubstanceCategoryPic, this.CompareCategories);
            this.ListStoreModel.SetSortFunc((int)Columns.SubstanceName, this.CompareSubstanceNames);
            this.ListStoreModel.SetSortFunc((int)Columns.LatinName, this.CompareLatinNames);

            this.LastActiveColumnId = (int)Columns.Selectable;
            this.substance_treeview.Columns[(int)Columns.SubstanceName].SortOrder = SortType.Ascending;
            this.ListStoreModel.SetSortColumnId((int)Columns.Selectable, SortType.Ascending);
            this.substance_treeview.Columns[(int)Columns.Selectable].SortIndicator = true;

            this.FilterModel = new TreeModelFilter(this.ListStoreModel, null);
            this.FilterModel.VisibleFunc = new TreeModelFilterVisibleFunc(this.SubstanceFilter);
            this.substance_treeview.Model = this.FilterModel;
        }

        private void UpdateSubstanceList()
        {
            this.IgnoreFilter = true;

            this.ListStoreModel.Clear();

            bool show_info_column = false;


            foreach (ClassificationInfo classification_info in this.SubstanceDB.Extension.GetModelClassExtension().Classifications)
            {
                foreach (SubstanceInfo substance_info in classification_info.Substances)
                {
                    string substance_name = substance_info.PrimaryDefaultName;
                    string latin_name = substance_info.PrimaryLatinName;
                    if (String.IsNullOrEmpty(latin_name) == true)
                        latin_name = LocalizedSubstanceExtensions.NotAvailable;

                    SubstanceCategory category = SubstanceListAutoCompletionEntry.ConvertToSubstanceCategory(classification_info);

                    bool is_selectable = substance_info.IsVisible;
                    bool has_info = substance_info.HasAlternativeNames;
                    Pixbuf buf = category == SubstanceCategory.NotMeasurable ? this.RedPoint : this.GreyPoint;
                    HashSet<string> search = new HashSet<string>();
                    foreach (string item in substance_info.AllNames)
                        search.Add(item.xCleanForSubstanceSearch());

                    if (has_info == true)
                        show_info_column = true;

                    this.ListStoreModel.AppendValues(is_selectable, has_info,
                                                     buf,
                                                     substance_name,
                                                     latin_name,
                                                     substance_info,
                                                     category,
                                                     search);

                }
            }

            this.substance_treeview.Columns[(int)Columns.Info].Visible = show_info_column;

            this.IgnoreFilter = false;
            this.FilterModel.Refilter();
        }

        [ConnectBefore]
        private void OnMotion(object o, MotionNotifyEventArgs e)
        {
            try
            {
                TreePath path;
                TreeIter iter;
                bool is_in_range = false;

                if (this.substance_treeview.GetPathAtPos((int)e.Event.X, (int)e.Event.Y, out path))
                {
                    Rectangle rect = this.substance_treeview.GetCellArea(path, this.substance_treeview.Columns[
                        (int)Columns.SubstanceCategoryPic]);
                    this.FilterModel.GetIter(out iter, path);

                    if (rect.X <= e.Event.X && e.Event.X <= rect.X + rect.Width)
                        is_in_range = true;

                    bool? entered_range = null;
                    bool changed_row = path.ToString() != this.LastTreePath.ToString();

                    if (is_in_range == true && this.IsPictureColumn == false)
                        entered_range = true;
                    else if (is_in_range == false && this.IsPictureColumn == true)
                        entered_range = false;

                    this.IsPictureColumn = is_in_range;
                    this.LastTreePath = path.ToString();

                    SubstanceCategory category = (SubstanceCategory)this.FilterModel.GetValue(iter, (int)Columns.SubstanceCategory);

                    int x = 0;
                    int y = 0;

                    this.substance_treeview.TranslateCoordinates(this.substance_extension_dialog, 0, 0, out x, out y);
                    double mouse_x = x + rect.X + rect.Width / 2;
                    double mouse_y = y + rect.Y + rect.Height / 2;

                    this.substance_extension_dialog.GdkWindow.GetPosition(out x, out y);
                    mouse_x += x;
                    mouse_y += y;

                    switch (entered_range)
                    {
                        case true:
                            this.ToolTip = new SubstanceCategoryToolTip(mouse_x, mouse_y, category);
                            break;

                        case false:
                            try
                            {
                                this.ToolTip.Destroy();
                            }
                            catch (Exception) { }
                            break;

                        default:
                            if (this.IsPictureColumn == true && changed_row == true)
                            {
                                try
                                {
                                    this.ToolTip.Destroy();
                                }
                                catch (Exception) { }
                                this.ToolTip = new SubstanceCategoryToolTip(mouse_x, mouse_y, category);
                            }

                            break;
                    }
                }
            }
            catch (Exception)
            {
                this.LastTreePath = "-1";
                this.IsPictureColumn = false;
                try { this.ToolTip.Destroy(); } catch { };
            }
        }

        private int CompareCategories(TreeModel model, TreeIter a, TreeIter b)
        {
            try
            {
                SubstanceCategory c1 = (SubstanceCategory)model.GetValue(a, (int)Columns.SubstanceCategory);
                SubstanceCategory c2 = (SubstanceCategory)model.GetValue(b, (int)Columns.SubstanceCategory);

                if (c1 == SubstanceCategory.NotMeasurable && c2 == SubstanceCategory.UserDefined)
                    return -1;

                if (c1 == SubstanceCategory.UserDefined && c2 == SubstanceCategory.NotMeasurable)
                    return 1;

                return this.CompareSubstanceNames(model, a, b);
            }
            catch (Exception)
            {
                return 0;
            }
        }

        private int CompareInfos(TreeModel model, TreeIter a, TreeIter b)
        {
            try
            {
                bool b1 = (bool)model.GetValue(a, (int)Columns.Info);
                bool b2 = (bool)model.GetValue(b, (int)Columns.Info);

                if (b1 == true && b2 == false)
                    return -1;

                if (b1 == false && b2 == true)
                    return 1;

                return this.CompareSubstanceNames(model, a, b);
            }
            catch (Exception)
            {
                return 0;
            }
        }

        private int CompareSelectables(TreeModel model, TreeIter a, TreeIter b)
        {
            if (this.SortActive == false)
                return 0;

            try
            {
                bool b1 = (bool)model.GetValue(a, (int)Columns.Selectable);
                bool b2 = (bool)model.GetValue(b, (int)Columns.Selectable);

                if (b1 == true && b2 == false)
                    return -1;

                if (b1 == false && b2 == true)
                    return 1;

                return this.CompareSubstanceNames(model, a, b);
            }
            catch (Exception)
            {
                return 0;
            }
        }

        private int CompareSubstanceNames(TreeModel model, TreeIter a, TreeIter b)
        {
            try
            {
                string s1 = (string)model.GetValue(a, (int)Columns.SubstanceName);
                string s2 = (string)model.GetValue(b, (int)Columns.SubstanceName);

                return String.Compare(s1, s2);
            }
            catch (Exception)
            {
                return 0;
            }
        }

        private int CompareLatinNames(TreeModel model, TreeIter a, TreeIter b)
        {
            try
            {
                string s1 = (string)model.GetValue(a, (int)Columns.LatinName);
                string s2 = (string)model.GetValue(b, (int)Columns.LatinName);

                if (s1 != LocalizedSubstanceExtensions.NotAvailable && s2 == LocalizedSubstanceExtensions.NotAvailable)
                    return -1;

                if (s1 == LocalizedSubstanceExtensions.NotAvailable && s2 != LocalizedSubstanceExtensions.NotAvailable)
                    return 1;

                int compare = String.Compare(s1, s2);
                if (compare != 0)
                    return compare;

                return this.CompareSubstanceNames(model, a, b);
            }
            catch (Exception)
            {
                return 0;
            }
        }

        private void OnConfigComboChanged(object sender, EventArgs e)
        {
            if (this.substance_extension_dialog.GdkWindow != null)
                this.substance_extension_dialog.GdkWindow.Cursor = new Gdk.Cursor(Gdk.CursorType.Watch);

            this.substance_extension_dialog.Sensitive = false;

            string profile = this.config_combobox.ActiveText;

            System.Threading.Tasks.Task.Run(() =>
            {
                this.Config.Profiles.Current.SubstanceExtension?.Save(false);
                this.Config.Profiles.Current = this.Config.Profiles.AllProfiles[profile];
                this.ApoIdentApp.CheckArchive();
                this.SubstanceDB.InitModelExtension();

                Application.Invoke((sender1, e1) =>
                {
                    this.UpdateSubstanceList();
                    this.substance_extension_dialog.Sensitive = true;

                    if (this.substance_extension_dialog.GdkWindow != null)
                        this.substance_extension_dialog.GdkWindow.Cursor = new Gdk.Cursor(Gdk.CursorType.Arrow);
                });
            });
        }

        private void OnTVColumnClicked(object sender, EventArgs e)
        {
            Gtk.TreeViewColumn col = sender as Gtk.TreeViewColumn;
            if (col == null)
                return;

            this.SortActive = true;

            int id = 0;
            foreach (Gtk.TreeViewColumn item in this.substance_treeview.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.substance_treeview.Columns[this.LastActiveColumnId].SortIndicator = false;
                this.LastActiveColumnId = id;
            }

            this.ListStoreModel.SetSortColumnId(id, col.SortOrder);
            col.SortIndicator = true;

            TreeIter iter;
            if (this.substance_treeview.Selection.GetSelected(out iter) == true)
            {
                TreePath path = this.ListStoreModel.GetPath(iter);
                this.substance_treeview.ScrollToCell(path, this.substance_treeview.Columns[0], false, 0.5f, 0.5f);
            }
        }

        private bool SubstanceFilter(Gtk.TreeModel model, Gtk.TreeIter iter)
        {
            if (this.IgnoreFilter == true)
                return true;

            string substance_pattern = this.search_entry.Text.xCleanForSubstanceSearch();

            if (String.IsNullOrEmpty(substance_pattern) == true)
                return true;

            HashSet<string> names = (HashSet<string>)model.GetValue(iter, (int)Columns.Search);
            if (names == null)
                return true;

            foreach (string item in names)
            {
                if (String.IsNullOrEmpty(substance_pattern) == false && item.StartsWith(substance_pattern) == true)
                    return true;
            }

            return false;
        }

        private void OnSelectableToggled(object sender, ToggledArgs args)
        {
            this.SortActive = false;
            TreeIter iter;
            Gtk.TreeModelFilter model = this.substance_treeview.Model as Gtk.TreeModelFilter;

            if (model.GetIter(out iter, new TreePath(args.Path)))
            {
                iter = model.ConvertIterToChildIter(iter);

                SubstanceInfo substance_info = (SubstanceInfo)this.ListStoreModel.GetValue(iter, (int)Columns.SubstanceInfo);
                if (substance_info.IsVisible == false)
                    substance_info.IsVisible = this.CheckForUniqueSubstanceNames(substance_info, false);
                else
                    substance_info.IsVisible = !substance_info.IsVisible;

                this.ListStoreModel.SetValue(iter, (int)Columns.Selectable, substance_info.IsVisible);
                this.SubstanceDB.RefreshExtension();
            }
        }

        private void OnSubstanceInfoToggled(object sender, ToggledArgs args)
        {
            TreeIter iter;
            Gtk.TreeModelFilter model = this.substance_treeview.Model as Gtk.TreeModelFilter;

            if (model.GetIter(out iter, new TreePath(args.Path)))
            {
                iter = model.ConvertIterToChildIter(iter);

                bool has_info = (bool)this.ListStoreModel.GetValue(iter, (int)Columns.Info);
                if (has_info == false)
                    return;

                SubstanceInfo substance_info = (SubstanceInfo)this.ListStoreModel.GetValue(iter, (int)Columns.SubstanceInfo);

                string alternative_substance_names = LocalizedSubstanceExtensions.NotAvailable;
                if (substance_info.OtherDefaultNames.Count > 0)
                    alternative_substance_names = String.Join("\n\t- ", substance_info.OtherDefaultNames.ToArray());

                string alternavtive_latin_names = LocalizedSubstanceExtensions.NotAvailable;
                if (substance_info.OtherLatinNames.Count > 0)
                    alternavtive_latin_names = String.Join("\n\t- ", substance_info.OtherLatinNames.ToArray());


                string message = String.Format(Catalog.GetString("Alternative substance names:\n\t- {0}\n\n" +
                                                                 "Alternative latin names:\n\t- {1}"),
                                               alternative_substance_names,
                                               alternavtive_latin_names);


                ExtToolTip.SetDialog(this.substance_treeview,
                                      Stock.DialogInfo,
                                             substance_info.PrimaryDefaultName,
                                             message);
            }
        }

        private void SelectTreeIterItem(string primary_name)
        {
            TreeIter iter;
            this.search_entry.Text = String.Empty;

            this.substance_treeview.GrabFocus();

            Gtk.TreeModelFilter model = this.substance_treeview.Model as Gtk.TreeModelFilter;

            for (int i = 0; i < model.IterNChildren(); i++)
            {
                if (model.IterNthChild(out iter, i) == true)
                {
                    string name = (string)model.GetValue(iter, (int)Columns.SubstanceName);
                    if (name == primary_name)
                    {
                        this.substance_treeview.Selection.SelectIter(iter);
                        TreePath path = model.GetPath(iter);
                        this.substance_treeview.ScrollToCell(path, this.substance_treeview.Columns[0], true, 0.5f, 0.0f);
                        break;
                    }
                }
            }
        }

        private void OnSearchEntryChanged(object sender, EventArgs e)
        {
            this.FilterModel.Refilter();
        }

        private void AddSubstance(ClassificationInfo classification_info)
        {
            string message = String.Empty;
            SubstanceInfo substance_info = classification_info.Substances[0];

            if (this.IsInputValid(substance_info.PrimaryDefaultName) == false)
            {
                message = String.Format(Catalog.GetString("The substance name '{0}' is not valid."),
                                        substance_info.PrimaryDefaultName);

                this.ShowMessage(message, MessageType.Error);
                return;
            }

            if (this.IsInputValid(substance_info.PrimaryLatinName) == false)
            {
                message = String.Format(Catalog.GetString("The latin name '{0}' is not valid."),
                                        substance_info.PrimaryLatinName);

                this.ShowMessage(message, MessageType.Error);
                return;
            }

            string substance_name = substance_info.PrimaryDefaultName.xCleanForSubstanceSearch();
            string latin_name = substance_info.PrimaryLatinName.xCleanForSubstanceSearch();

            if (substance_name == latin_name)
            {
                message = Catalog.GetString("The substance name and the latin name must not be equal.");
                this.ShowMessage(message, MessageType.Error);
                return;
            }

            if (this.CheckForUniqueSubstanceNames(substance_info, true) == false)
                return;

            this.SubstanceDB.Extension.GetModelClassExtension().Classifications.Add(classification_info);
            this.SubstanceDB.Extension.GetModelClassExtension().InitResolver();
            this.SubstanceDB.RefreshExtension();
            this.UpdateSubstanceList();
            this.search_entry.Text = String.Empty;
            this.FilterModel.Refilter();
            this.SelectTreeIterItem(substance_info.PrimaryDefaultName);
        }

        private bool IsInputValid(string substance_name)
        {
            substance_name = substance_name.ToLower().Trim(new char[] { ' ', ',', '\t', ';' });
            if (substance_name == LocalizedSubstanceExtensions.NotAvailable.ToLower())
                return false;

            string batch = LocalizedSubstanceExtensions.NotAvailable.Trim(new char[] { ' ', ',' }).ToLower();
            if (substance_name.EndsWith(batch, StringComparison.InvariantCulture) == true)
                return false;

            if (substance_name.StartsWith(batch, StringComparison.InvariantCulture) == true)
                return false;

            return true;
        }

        private bool CheckForUniqueSubstanceNames(SubstanceInfo substance_info, bool refresh_treeview)
        {
            string message = String.Empty;

            string substance_name = substance_info.PrimaryDefaultName.xCleanForSubstanceSearch();
            string latin_name = substance_info.PrimaryLatinName.xCleanForSubstanceSearch();

            Dictionary<string, string> substance_name_classifiers = this.SubstanceDB.CheckUniqueSubstanceName(substance_name);
            Dictionary<string, string> latin_name_classifiers = this.SubstanceDB.CheckUniqueSubstanceName(latin_name);

            if (substance_name_classifiers.ContainsKey(LocalizedSubstanceExtensions.UserDefinedModelClassName) == true)
            {
                string primary_name = substance_name_classifiers[LocalizedSubstanceExtensions.UserDefinedModelClassName];
                message = String.Format(Catalog.GetString("The substance '{0}' already exists in this classifier '{2}'. " +
                                                          "Please look at the entry with the primary name '{1}'. " +
                                                         "Before adding/activating this substance, you have to deactivate the selectable property " +
                                                         "of the existing substance."),
                                        substance_info.PrimaryDefaultName,
                                        primary_name,
                                        LocalizedSubstanceExtensions.UserDefinedModelClassName);

                this.ShowMessage(message, MessageType.Error);
                if (refresh_treeview == true)
                {
                    this.search_entry.Text = String.Empty;
                    this.FilterModel.Refilter();
                    this.SelectTreeIterItem(primary_name);
                }
                return false;
            }

            if (latin_name_classifiers.ContainsKey(LocalizedSubstanceExtensions.UserDefinedModelClassName) == true)
            {
                string primary_name = latin_name_classifiers[LocalizedSubstanceExtensions.UserDefinedModelClassName];

                message = String.Format(Catalog.GetString("The substance '{0}' already exists in this classifier '{2}'. " +
                                                          "Please look at the entry with the primary name '{1}'. " +
                                                         "Before adding/activating this substance, you have to deactivate the selectable property " +
                                                         "of the existing substance."),
                                        substance_info.PrimaryLatinName,
                                        primary_name,
                                        LocalizedSubstanceExtensions.UserDefinedModelClassName);

                this.ShowMessage(message, MessageType.Error);
                if (refresh_treeview == true)
                {
                    this.search_entry.Text = String.Empty;
                    this.FilterModel.Refilter();
                    this.SelectTreeIterItem(primary_name);
                }
                return false;
            }

            if (substance_name_classifiers.Count > 0 || latin_name_classifiers.Count > 0)
            {
                string name = substance_name_classifiers.Count > 0 ? substance_info.PrimaryDefaultName : substance_info.PrimaryLatinName;

                message = String.Format(Catalog.GetString("The substance '{0}' already exists as a NIR measurable substance. " +
                                                          "Do you want to add/activate this substance anyway?." +
                                                          "Press the YES buttun for continuing."), name);

                if (this.ShowMessage(message, MessageType.Warning) == false)
                    return false;
            }

            return true;
        }

        [ConnectBefore]
        private void OnButtonPressEvent(object sender, ButtonPressEventArgs e)
        {
            if (e.Event.Button == 3)
            {
                Menu menu = new Menu();
                MenuItem item = new MenuItem(Catalog.GetString("Deselect all substances"));
                item.Activated += this.OnDeselectAllSubstances;
                menu.Append(item);
                menu.ShowAll();
                menu.Popup();
            }
        }

        private void OnDeselectAllSubstances(object sender, EventArgs e)
        {
            TreeIter iter;
            this.SortActive = false;

            Gtk.TreeModelFilter model = this.substance_treeview.Model as Gtk.TreeModelFilter;

            for (int i = 0; i < model.IterNChildren(); i++)
            {
                if (model.IterNthChild(out iter, i) == true)
                {
                    iter = model.ConvertIterToChildIter(iter);
                    this.ListStoreModel.SetValue(iter, (int)Columns.Selectable, false);
                    SubstanceInfo substance_info = (SubstanceInfo)this.ListStoreModel.GetValue(iter, (int)Columns.SubstanceInfo);
                    substance_info.IsVisible = false;
                }
            }

            this.SubstanceDB.RefreshExtension();
        }

        public string SearchText
        {
            get
            {
                return this.search_entry.Text.Trim();
            }
        }

        private bool ShowMessage(string message, Gtk.MessageType message_type)
        {
            MessageDialog mdlg = new MessageDialog(this.substance_extension_dialog,
                                                   Gtk.DialogFlags.Modal, message_type,
                                                   message_type == MessageType.Warning ? ButtonsType.YesNo : ButtonsType.Ok,
                                                   message);

            mdlg.IconList = ApoIdentStock.FinderIcons;
            ResponseType answer = (ResponseType)mdlg.Run();
            mdlg.Destroy();
            if (answer == ResponseType.Yes)
                return true;

            return false;
        }

        public Gtk.Window Window
        {
            get
            {
                return this.substance_extension_dialog;
            }
        }

        private Gdk.Pixbuf RedPoint { get; set; }
        private Gdk.Pixbuf GreyPoint { get; set; }
        private int LastActiveColumnId { get; set; }

        private SubstanceManagement SubstanceDB { get; set; }
        private bool SortActive { get; set; } = true;
        private bool IgnoreFilter { get; set; } = true;
        private Configurations Config { get; set; }
        private ApoIdentApplication ApoIdentApp { get; set; }

        public bool IsPictureColumn { get; set; } = false;
        public string LastTreePath { get; set; } = "-1";
        public SubstanceCategoryToolTip ToolTip { get; set; }
    }
}
