// Created with MonoDevelop
//
//    QuickStep: Acquire, view and identify spectra 
//    Copyright (C) 2009 Thomas Klose & Torsten Schache, Hiperscan GmbH Dresden, Germany
//
//    This program is free software: you can redistribute it and/or modify
//    it under the terms of the GNU General Public License as published by
//    the Free Software Foundation, either version 3 of the License, or
//    (at your option) any later version.
//
//    This program is distributed in the hope that it will be useful,
//    but WITHOUT ANY WARRANTY; without even the implied warranty of
//    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//    GNU General Public License for more details.
//
//    You should have received a copy of the GNU General Public License
//    along with this program.  If not, see <http://www.gnu.org/licenses/>.

using System;
using System.IO;
using System.Collections.Generic;
using System.Reflection;
using System.Drawing;
using System.Threading;
using Gtk;

using Hiperscan.Spectroscopy;
using Hiperscan.Spectroscopy.IdentModule;
using Hiperscan.SGS;
using Hiperscan.GUI.Figure;
using Hiperscan.Unix;
using Hiperscan.I18n;


namespace Hiperscan.Extensions.ApoIdent
{
    public partial class FinderWindow
    {

        private class InlineEntryCompletion : Gtk.EntryCompletion
        {
            public InlineEntryCompletion() : base()
            {
                this.InlineSelection = false; //FIXME
            }
        }

        private static FinderWindow current_instance = null;
        public static readonly string ServiceForRepeatedError = Catalog.GetString("\n\n" +
                                               "If the problem persits, please consult an authorized HiperScan " +
                                               "representative:\n" +
                                               "Email: kundenservice@apo-ident.de\n" +
                                                "Telefon: +49 (0)351 212 496 33");

        public static readonly string ServiceForNoRepeatedError = Catalog.GetString("\n\n" +
                                               "Please consult an authorized HiperScan " +
                                               "representative:\n" +
                                               "Email: kundenservice@apo-ident.de\n" +
                                                "Telefon: +49 (0)351 212 496 33");

        internal IHost host;
        private Report.Generic CurrentReport;

        private GreetingWindow greeting_window;

        private volatile bool no_misidentification_yet = true;
        private volatile bool hint_message_already_shown = false;

		public Spectrum CurrentProcessedSpectrum { get; set; }

        public static readonly Gdk.Color HIPERSCAN_RED = new Gdk.Color(192, 30, 30);
        public static readonly Gdk.Color HIPERSCAN_WHITE = new Gdk.Color(255, 255, 255);


        public FinderWindow(IHost host)
        {
            Console.WriteLine("Call FinderWindow.ctor...");

            if (FinderWindow.current_instance != null)
            {
                FinderWindow.current_instance.finder_window.Present();
                return;
            }

            this.host = host;
            this.MainThreadId = System.Threading.Thread.CurrentThread.ManagedThreadId;
            this.DetailSectionState.Reset();

            string path_env = "PATH=" + Environment.GetEnvironmentVariable("PATH");
            string mono_env = "MONO_PATH=" + Environment.GetEnvironmentVariable("MONO_PATH");
            string mono_config = "MONO_CFG_DIR=" + Environment.GetEnvironmentVariable("MONO_CFG_DIR");
            string lang = "LANG=" + Environment.GetEnvironmentVariable("LANG");

            Console.WriteLine("Apo-Ident-Version: {0}", Plugin.version);
            Console.WriteLine(path_env);
            Console.WriteLine(mono_env);
            Console.WriteLine(mono_config);
            Console.WriteLine(lang);

            this.Config = new Configurations(this.host.ConfigurationDirPath);

            this.Config.PrinterSettings.PrinterSettingsChanged += this.OnPrinterSettingsChanged;
			this.Config.AcquisitionSettings.AcquisitionSettingsChanged += this.OnAcquisitionSettingsChanged;           
			this.Config.Profiles.ProfileSettingsChanged += this.OnProfileSettingsChanged;
			this.Config.Profiles.ProfileChanged += this.OnProfileChanged;

            FilesAndDirs.Init(this);
            ReportInfo.ConvertToMetaData2_0(this.Config);
            this.CreateIdentServerClientInstance(this.Config);

            this.InitState = ServerDeviceState.None;
            this.InitSubstanceManagement();

            this.CreateNotMeasurableReport = false;
           
            Console.WriteLine("Initialize components:");

            try
            {
                this.InitializeGreetingWindow();
                this.InitializeFinderWindow();
                this.host.ProcessEvents();
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.ToString());
                string msg = string.Format(Catalog.GetString("A non-recoverable error occured while starting {0}: {1}\n\nThe program start will be canceled."), Plugin.name, ex.Message);
                Plugin.host.ShowInfoMessage(null, msg);
                Environment.Exit(1);
            }

            while (this.host.InfoMessageIsShown == true)
            {
                Thread.Sleep(100);
                this.host.ProcessEvents();
            }

            this.finder_window.DeleteEvent += new DeleteEventHandler(this.OnDeleteEvent);
            this.Scanner = new Scanner(this.GetScanCondition);
            this.Scanner.ScanDetected += this.OnScanDetected;


            this.FinishInit();
            Console.WriteLine("Exit FinderWindow.ctor");
        }

		private void OnProfileSettingsChanged(object sender, EventArgs e)
		{
            this.CreateArchive(this.GtkWindow);

			this.Update(FinderUpdateState.ConfigProfileSettingsChanged);

            if (this.Config.Profiles.NeedCheck == true)
                new Preferences(this);
		}

        private void OnPrinterSettingsChanged(object sender, EventArgs e)
        {
            this.DetailSectionState.Set(DetailSectionStates.HasLabelPrinter, this.Config.PrinterSettings.HasPrinter);
            this.Update(FinderUpdateState.ConfigPrinterSettingsChanged);
        }

		private void OnProfileChanged(object sender, EventArgs e)
		{
            this.CreateArchive(this.GtkWindow);
            this.SubstanceManagement.InitModelExtension();
            this.SubstanceManagement.ResetSelection();
			this.CreateNotMeasurableReport = false;
			this.CurrentProcessedSpectrum = null;
            this.PollFinderButton = false;
            this.IdentServerClient.DiscardResult();
            this.Update(FinderUpdateState.EndConfigProfileChanged);
		}

        private void FinishInit()
        {

            this.ShowGreetingWindow();

            this.finder_window.WindowStateEvent += this.OnWindowStateChanged;

            this.InitState = ServerDeviceState.None;
            this.InitializeDeviceAndIdentModule();
            this.host.DeviceList.Changed += this.OnDeviceListChanged;

            this.SetScrolledWindow();
            this.HideGreetingWindow();

            if (this.Config.Profiles.NeedCheck == true || this.Config.Profiles.Current == null)
				new Preferences(this);				     
        }
      
        private void ShowHintMessage()
        {
            if (this.Config.GuiSettings.DontShowHintMessageAgain && this.hint_message_already_shown)
                return;

            this.hint_message_already_shown = true;

            string message = Catalog.GetString("The identification failed. One reason can be a faulty operation.\n\n" +
                                               "Do you need further information?");
            MessageDialog dialog = new MessageDialog(this.finder_window,
                                                     DialogFlags.Modal,
                                                     Gtk.MessageType.Question,
                                                     ButtonsType.YesNo,
                                                     message);

            CheckButton cb = new CheckButton(Catalog.GetString("Don't show this dialog again during current session"));
            cb.Active = this.Config.GuiSettings.DontShowHintMessageAgain;
            cb.CanFocus = false;
            dialog.VBox.PackStart(cb);
            cb.Show();

            dialog.IconList = ApoIdentStock.FinderIcons;
            ResponseType choice = (ResponseType)dialog.Run();
            this.Config.GuiSettings.DontShowHintMessageAgain = cb.Active;
            dialog.Destroy();

            if (choice == ResponseType.Yes)
            {
                if (this.SubstanceManagement.CurrentModelClass.IsFluid == true)
                    this.ShowPDF("misidentification_liquids.pdf");
                else
                    this.ShowPDF("misidentification_solids.pdf");
            }
        }

        internal void OnResetReferenceButtonClicked(object o, EventArgs e)
        {
            if (this.Finder.FinderExists == true)
                this.Finder.Current.ResetReferences();
            this.Update(FinderUpdateState.DeviceResetReference);
        }

        internal void OnConnectButtonClicked(object o, EventArgs e)
        {
            this.InitState = ServerDeviceState.None;
            this.InitializeDeviceAndIdentModule();
        }

        internal void OnEmptyButtonClicked(object o, EventArgs e)
        {
            if (this.RemindSave() == false)
                return;

            this.IdentServerClient.DiscardResult();
            this.CurrentReport = null;
            this.Update(FinderUpdateState.EmptyDieMeasurementBegin);
            this.host.ProcessEvents();

            bool abort_repeating = false;
            int repeats = 0;
            bool success = true;
            bool lost_connection = false;


            while (abort_repeating == false)
            {
                abort_repeating = true;
                try
                {
                    this.Finder.Current.AcquireExternalTransflectanceReference();
                }
                catch (NullReferenceException ex)
                { /* may happen if device was disconnected */
                    Console.WriteLine("OnEmptyButtonClicked -> NullReferenceException: {0}", ex);
                }
                catch (Finder.InvalidTransflectReferenceException ex)
                {
                    string message = String.Format(Catalog.GetString("<b> Error while Transflectance Reference measurement:</b>\n\n" +
                                                                     "Always start by applying a clean sample cup and the Transflectance Insert(feet down). " +
                                                                     "Please click on 'Information' in order to see how to perform a measurement.\n\n" +
                                                                     "Click Reset and repeat the Transflectance Reference measurement.\n\n" +
                                                                     "In case of permanent problems, please contact your distributor or Hiperscan customer " +
                                                                     "support under info@hiperscan.com or +49 351 212 496 0 for further information."));

                    success = false;
                    //string message = String.Format(Catalog.GetString("<b>Error during empty die referencing:</b>\n\n{0}"), ex.Message);
                    this.host.ShowInfoMessage(this.finder_window, Stock.Info, this.ShowErrorDieReference, true, message);

                    message = Catalog.GetString("Always start by applying a clean sample cup and the Transflectance Insert(feet down). " +
                                                "Information on how to perform the measurement can be found online in our " +
                                                "Service Center under Help.\n\n" +
                                                "Click Reset and repeat the Transflectance Reference measurement.\n\n" +
                                                "In case of permanent problems, please contact your distributor or Hiperscan customer " +
                                                "support under info@hiperscan.com or +49 351 212 496 0 for further information.");

                    this.FinderAnimation.SetError(AnimationErrorType.EmptyDieReferencingFailed, message.Replace("<u>", "").Replace("</u>",""));
                    this.host.ProcessEvents();
                    Console.WriteLine("OnEmptyButtonClicked -> InvalidTransflectReferenceException: {0}", ex);
                }
                catch (Finder.EmptyReferenceException ex)
                {
                    success = false;
                    string message = String.Format(Catalog.GetString("Error during empty referencing: {0}"), ex.Message);
                    this.host.ShowInfoMessage(this.finder_window, Stock.Info, this.ShowErrorEmptyReference, message);
                    this.FinderAnimation.SetError(AnimationErrorType.ExternalEmptyReferencingFailed, message);
                    this.host.ProcessEvents();
                    Console.WriteLine("OnEmptyButtonClicked -> EmptyReferenceException: {0}", ex);
                }
                catch (Finder.WhiteReferenceException ex)
                {
                    success = false;
                    string message = String.Format(Catalog.GetString("Error during white referencing: {0}"), ex.Message);
                    this.host.ShowInfoMessage(this.finder_window, Stock.Info, this.ShowErrorWhiteReference, message);
                    this.FinderAnimation.SetError(AnimationErrorType.ExternalWhiteReferencingFailed, message);
                    this.host.ProcessEvents();
                    Console.WriteLine("OnEmptyButtonClicked -> WhiteReferenceException: {0}", ex);
                }
                catch (LevenbergMarquardt.MaxIterationNumberException ex) //InternalCalibrationException
                {
                    if (repeats == 0)
                        abort_repeating = false;
                    else
                    {
                        success = false;
                        string message = String.Format(Catalog.GetString("Error during internal referencing: {0}"), ex.Message);
                        message += ServiceForNoRepeatedError;
                        this.host.ShowInfoMessage(this.finder_window, message);
                        this.FinderAnimation.SetError(AnimationErrorType.InternalReferencingFailed, message);
                    }
                    this.host.ProcessEvents();
                    repeats++;
                    Console.WriteLine("OnEmptyButtonClicked -> MaxIterationNumberException: {0}", ex);
                }
                catch (InvalidDistanceException ex)
                {
                    success = false;
                    string message = String.Format(Catalog.GetString("Probe specimen is not valid."));
                    message += ServiceForNoRepeatedError;
                    this.host.ShowInfoMessage(this.finder_window, message);
                    this.FinderAnimation.SetError(AnimationErrorType.EmptyDieMeasurementFailed, message);
                    this.host.ProcessEvents();
                    Console.WriteLine("OnEmptyButtonClicked -> InvalidDistanceException: {0}", ex);
                }               
                catch (Exception ex)
                {
                    success = false;
                    string message = String.Format(Catalog.GetString("Empty die executing error: {0}"), ex.Message);
                    message += ServiceForRepeatedError;
                    this.Finder.Current.ResetRecalibrationTimestamp();
                    this.host.ShowInfoMessage(this.finder_window, message);
                    this.FinderAnimation.SetError(AnimationErrorType.EmptyDieMeasurementFailed, message);
                    this.host.ProcessEvents();
                    Console.WriteLine("OnEmptyButtonClicked -> Exception: {0}", ex);
                }
                finally
                {
                    if (abort_repeating == true)
                    {
                        this.Finder.Idle();
                    }
                }

                if (success == true)
                {
                    if (this.SubstanceManagement.CurrentClassification.ClassificationType != ClassificationType.Multiple)
                        this.Update(FinderUpdateState.EmptyDieMeasurementEnd);
                    else
                        this.Update(FinderUpdateState.EmptyDieClassificationMeasurementEnd);
                }
                else
                {
                    if (lost_connection == false)
                        this.Update(FinderUpdateState.EmptyDieMeasurementFailedEnd);
                }
            }
        }

        internal void OnStartButtonClicked(object o, EventArgs e)
        {
            if (this.RemindSave() == false)
                return;

            bool success = true;
            bool lost_connection = false;
            this.IdentServerClient.DiscardResult();
            this.CurrentReport = null;

            this.Update(FinderUpdateState.MeasurementBegin);
            this.host.ProcessEvents();

            bool abort_repeating = false;
            int repeats = 0;
            while (abort_repeating == false)
            {
                abort_repeating = true;
                success = true;
                try
                {
                    this.Finder.Current.Acquire(this.SubstanceManagement.CurrentModelClass.IsFluid);
                    this.Finder.Current.SaveReferenceSpectrum(this.Finder.Current.CurrentSampleSpectrum,
                                                              this.SubstanceManagement.CurrentModelClass.IsFluid ?
                                                              SpectrumType.FluidSpecimen : SpectrumType.SolidSpecimen,
                                                              false, string.Empty);
                    this.CurrentProcessedSpectrum = this.Finder.Current.Process(this.SubstanceManagement.CurrentModelClass.IsFluid);

                    this.CurrentProcessedSpectrum.LimitBandwidth = true;

                    if (Math.Abs(this.CurrentProcessedSpectrum.Absorbance.YMax()) < 0.02 &&
                        Math.Abs(this.CurrentProcessedSpectrum.Absorbance.YMean()) < 0.01)
                        throw new Exception(Catalog.GetString("Invalid specimen spectrum."));

                    this.IdentServerClient.Classify(this.CurrentProcessedSpectrum);
                    this.DetailSectionState.Set(DetailSectionStates.ReportNotSaved, true);
                }
                catch (NullReferenceException ex) 
                { /* may happen if device was disconnected */
                    Console.WriteLine("OnStartButtonClicked -> NullReferenceException: {0}", ex);
                }
                catch (Finder.EmptyReferenceException ex)
                {
                    success = false;
                    string message = String.Format(Catalog.GetString("Error during empty referencing: {0}"), ex.Message);
                    this.host.ShowInfoMessage(this.finder_window, Stock.Info, this.ShowErrorEmptyReference, message);
                    this.FinderAnimation.SetError(AnimationErrorType.ExternalEmptyReferencingFailed, message); //TODO design error
                    this.host.ProcessEvents();
                    Console.WriteLine("OnStartButtonClicked -> EmptyReferenceException: {0}", ex);
                }
                catch (Finder.WhiteReferenceException ex)
                {
                    success = false;
                    string message = String.Format(Catalog.GetString("Error during white referencing: {0}"), ex.Message);
                    this.host.ShowInfoMessage(this.finder_window, Stock.Info, this.ShowErrorWhiteReference, message);
                    this.FinderAnimation.SetError(AnimationErrorType.ExternalWhiteReferencingFailed, message); //TODO design error
                    this.host.ProcessEvents();
                    Console.WriteLine("OnStartButtonClicked -> WhiteReferenceException: {0}", ex);
                }
                catch (LevenbergMarquardt.MaxIterationNumberException ex) //InternalCalibrationException
                {
                    success = false;
                    if (repeats == 0)
                        abort_repeating = false;
                    else
                    {
                        string message = String.Format(Catalog.GetString("Error during internal referencing: {0}"), ex.Message);
                        message += ServiceForNoRepeatedError;
                        this.host.ShowInfoMessage(this.finder_window, message);
                        this.FinderAnimation.SetError(AnimationErrorType.InternalReferencingFailed, message); //TODO design error
                    }
                    this.host.ProcessEvents();
                    repeats++;
                    Console.WriteLine("OnStartButtonClicked -> MaxIterationNumberException: {0}", ex);
                }
                catch (NotFiniteNumberException ex)
                {
                    success = false;
                    string message = String.Format(Catalog.GetString("Probe specimen is not valid."));
                    message += ServiceForNoRepeatedError;
                    this.host.ShowInfoMessage(this.finder_window, message);
                    this.FinderAnimation.SetError(AnimationErrorType.MeasurementFailed, message); //TODO design error
                    Console.WriteLine("OnStartButtonClicked -> NotFiniteNumberException: {0}", ex);
                }
                catch (InvalidDistanceException ex)
                {
                    success = false;
                    string message = String.Format(Catalog.GetString("Probe specimen is not valid."));
                    message += ServiceForNoRepeatedError;
                    this.host.ShowInfoMessage(this.finder_window, message);
                    this.FinderAnimation.SetError(AnimationErrorType.MeasurementFailed, message); //TODO design error
                    Console.WriteLine("OnStartButtonClicked -> NotFiniteNumberException: {0}", ex);
                }
                catch (System.Runtime.Remoting.RemotingException ex)
                {
                    success = false;
                    lost_connection = true;
                    this.CurrentProcessedSpectrum = null;
                    this.IdentServerClient.Reset();
                    this.Finder.Reset();
                    this.InitState = ServerDeviceState.None;

                    string message = String.Format(Catalog.GetString("Measurement executing error: {0}"), ex.Message);
                    message += ServiceForRepeatedError;

                    this.host.ShowInfoMessage(this.finder_window, message);
                    this.Update(FinderUpdateState.ConnectionLost, message);
                    Console.WriteLine("OnStartButtonClicked -> RemotingException: {0}", ex);
                }
                catch (Exception ex)
                {
                    success = false;
                    this.CurrentProcessedSpectrum = null;
                    string message = String.Format(Catalog.GetString("Measurement executing error: {0}"), ex.Message);
                    message += ServiceForRepeatedError;
                    this.host.ShowInfoMessage(this.finder_window, message);
                    this.FinderAnimation.SetError(AnimationErrorType.MeasurementFailed, message); //TODO design error

                    try
                    {
                        this.Finder.Current.SaveReferenceSpectrum(this.Finder.Current.CurrentSampleSpectrum,
                                                                  this.SubstanceManagement.CurrentModelClass.IsFluid ? SpectrumType.FluidSpecimen : SpectrumType.SolidSpecimen,
                                                                  true, string.Empty);
                    }
                    catch { }
                    this.host.ProcessEvents();
                    Console.WriteLine("OnStartButtonClicked -> Exception: {0}", ex);
                }
                finally
                {
                    if (abort_repeating == true)
                    {
                        try
                        {
                            this.Finder.Idle();
                        }
                        catch (Exception)
                        {
                        }
                    }
                }
            }

            this.DetailSectionState.Set(DetailSectionStates.HasResult, 
                                        this.SubstanceManagement.CurrentSubstance != null && this.IdentServerClient.HasValidResult);
            if (success == true)
            {
                this.Update(FinderUpdateState.MeasurementEnd);
                this.SubstanceManagement.ResetScannerMode();
            }
            else
            {
                if (lost_connection == false)
                    this.Update(FinderUpdateState.MeasurementFailedEnd);
            }
        }

        internal void OnDetailsButtonClicked(object o, EventArgs e)
        {
            if (this.CreateNotMeasurableReport == true)
            {
                string message = String.Format(Catalog.GetString("Spectrum details are not available when a NIR inactive substance ist selected."));
                this.host.ShowInfoMessage(this.finder_window, message);
                return;
            }

            if (this.IdentServerClient.HasValidResult == false)
            {
                string message = String.Format(Catalog.GetString("Spectrum details are not available because of an analyzing error by IdentModule."));
                this.host.ShowInfoMessage(this.finder_window, message);
                return;
            }

            this.FinderAnimation.ResetErrorsWithoutMisIdentification();

            bool identified = false;
            Hiperscan.Spectroscopy.DataSet original = null;
            Hiperscan.Spectroscopy.DataSet backproj = null;
            Hiperscan.Spectroscopy.DataSet matchbp = null;
            Hiperscan.Spectroscopy.DataSet difference = null;

            try
            {
                original = this.CurrentProcessedSpectrum.Absorbance;
                backproj = this.IdentServerClient.Result.SpectrumBackProjection;

                if (this.IdentServerClient.IsTruePositive)
                {
                    matchbp = this.IdentServerClient.Result.MatchBackProjection;
                    difference = backproj - matchbp;
                    identified = true;
                }
            }
            catch (Exception ex)
            {
                string message = String.Format(Catalog.GetString("Error during calculating the spectra: {0}"),
                                          ex.Message);
                message += ServiceForRepeatedError;
                this.host.ShowInfoMessage(this.finder_window, message);
                this.FinderAnimation.SetError(AnimationErrorType.CalculationFailed, message);
                return;
            }


            original.Label = Catalog.GetString("Acquired spectrum");
            original.Color = Color.Blue;
            backproj.Label = Catalog.GetString("Back projection of acquired spectrum");
            backproj.Color = Color.Blue;
            backproj.Id = "0_backprojection";

            if (identified == true)
            {
                matchbp.Label = Catalog.GetString("Back projection of reference spectrum");
                matchbp.Color = Color.Red;
                matchbp.Id = "1_match";
                difference.Label = Catalog.GetString("Difference of back projections*");
                difference.Color = Color.Green;
                difference.LineStyle = LineStyle.Dash;
                difference.Id = "2_difference";
            }

            Dialog dialog = new Dialog(Catalog.GetString("Absorbance Spectra"), this.finder_window, DialogFlags.Modal);

            dialog.IconList = ApoIdentStock.FinderIcons;

            dialog.AddButton(Stock.Close, ResponseType.Close);
            dialog.WindowPosition = WindowPosition.CenterAlways;
            dialog.HasSeparator = true;
            dialog.WidthRequest = (int) (780 * 1.4);
            dialog.HeightRequest = (int)(400 * 1.4);

            IPlotEngine plot1 = new NPlotEngine(PlotEngineType.Synchronous);
            IPlotEngine plot2 = new NPlotEngine(PlotEngineType.Synchronous);

            plot1.BackgroundColor = this.host.BackgroundColor;
            Hiperscan.Spectroscopy.DataSet ds = new Hiperscan.Spectroscopy.DataSet(original);
            //          List<int> indices = ds.Find('x', '<', backproj.XMin());
            //          ds.RemoveAt(indices);
            //          indices = ds.Find('x', '>', backproj.XMax());
            //          ds.RemoveAt(indices);
            ds.Label = original.Label;
            ds.Color = original.Color;
            plot1.AddDataSet(ds);
            plot1.Title = Catalog.GetString("Rawdata of the spectrum");
            plot1.XLabel = Catalog.GetString("Wavelength / nm");
            plot1.YLabel = Catalog.GetString("Absorbance");
            plot1.FitView();
            plot1.ProcessChanges();

            plot2.BackgroundColor = this.host.BackgroundColor;
            plot2.AddDataSet(backproj);
            plot2.Title = Catalog.GetString("Comparison of processed spectrum and database entry");
            if (identified == true)
                plot2.AddDataSet(matchbp);
            plot2.XLabel = Catalog.GetString("Wavelength / nm");
            plot2.YLabel = Catalog.GetString("f(Absorbance)");
            plot2.FitView();
            plot2.ProcessChanges();

            if (this.Config.Profiles.Current.ShowDifference == true && identified == true)
            {
                plot2.UseSecondAxes = true;
                plot2.AddDataSet(difference);
                double min = difference.YMin();
                double max = difference.YMax();
                plot2.YMin = min - 2 * (max - min);
                plot2.YMax = max + 2 * (max - min);
                plot2.ProcessChanges();
                plot2.YLabel = Catalog.GetString("Difference*");
                plot2.ProcessChanges();
            }

            HBox hbox = new HBox(true, 5);
            hbox.PackStart(plot1.GtkWidget, true, true, 0);
            hbox.PackStart(plot2.GtkWidget, true, true, 0);

            dialog.VBox.PackStart(hbox);
            hbox.ShowAll();

            dialog.IconList = ApoIdentStock.FinderIcons;

            dialog.Run();

            dialog.Destroy();

            ((IDisposable)plot1).Dispose();
            ((IDisposable)plot2).Dispose();
        }


        internal void OnResultListButtonClicked(object o, EventArgs e)
        {
            if (this.CreateNotMeasurableReport == true)
            {
                string message = String.Format(Catalog.GetString("Ranking list is not available when a NIR inactive substance ist selected."));
                this.host.ShowInfoMessage(this.finder_window, message);
                return;
            }

            if (this.IdentServerClient.HasValidResult == false)
            {
                string message = String.Format(Catalog.GetString("Ranking list is not available because of an analyzing error by IdentModule."));
                this.host.ShowInfoMessage(this.finder_window, message);
                return;
            }

            new ResultListDialog(this.GtkWindow, 
                                 this.Config.Profiles.Current.ReportCountry, 
                                 this.IdentServerClient.Result, 
                                 this.GetTestId());
        }



        internal void OnConfigComboChanged(object o, EventArgs e)
        {         
            if (this.IsUpdating == true)
                return;

            this.Update(FinderUpdateState.BeginConfigComboChanged);
            
            if (this.RemindSave() == false)
            {
                this.config_combo.Active = this.Config.Profiles.CurrentIndex(this.Config.Profiles.Current);
                return;
            }

            Profile profile = this.Config.Profiles[this.config_combo.ActiveText];           
			this.Config.Profiles.Current = profile;
 
			this.OnProfileChanged(this.Config.Profiles, EventArgs.Empty);

			if (this.Config.Profiles.NeedCheck == true)
				new Preferences(this);
        }



        private void OnSubstanceCategoryButtonClicked(object sender, EventArgs e)
        {
            try
            {
                if (this.SubstanceManagement.CurrentClassification.IsNirMeasurable == false)
                {
                    string message = String.Format(Catalog.GetString("Substance '{0}' cannot be measured by Apo-Ident. " +
                                                                     "Please try another identification method for this substance."),
                                                   this.SubstanceManagement.CurrentSubstance.SelectedDefaultName);
                    Gtk.MessageDialog mdlg = new Gtk.MessageDialog(this.finder_window,
                                                                          Gtk.DialogFlags.Modal,
                                                                          Gtk.MessageType.Info,
                                                                          Gtk.ButtonsType.Ok,
                                                                          message);
                    
                    mdlg.Title = Catalog.GetString("Substance Not Measurable");
                    mdlg.IconList = ApoIdentStock.FinderIcons;
                    mdlg.Run();
                    mdlg.Destroy();
                }
                else if (this.SubstanceManagement.CurrentClassification.ClassificationType == ClassificationType.Multiple)
                {
                    new ClassificationInfoWindow(this.finder_window, this.SubstanceManagement, false);
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine("INFO - " + ex.ToString());
            }
        }


        private void OnShowSummarizedGroupLinkbuttonClicked(object sender, EventArgs e)
        {
            new ClassificationInfoWindow(this.finder_window, this.SubstanceManagement, true);
        }


        private void OnUserReportInfoChanged(object sender, EventArgs e)
        {
            this.UpdateDetailSectionState(DetailSectionStates.UserFieldCorrect, this.user_regexentry.IsTextValid);
        }

        private void OnSupplierReportInfoChanged(object sender, EventArgs e)
        {
            this.UpdateDetailSectionState(DetailSectionStates.SupplierFieldCorrect, this.supplier_regexentry.IsTextValid);
        }

        private void OnBatchReportInfoChanged(object sender, EventArgs e)
        {
            this.UpdateDetailSectionState(DetailSectionStates.BatchNumberFieldCorrect, this.batch_regexentry.IsTextValid);
        }

        private void OnPznReportInfoChanged(object sender, EventArgs e)
        {
            this.UpdateDetailSectionState(DetailSectionStates.PznFieldCorrect, this.pzn_regexentry.IsTextValid);
        }

        private void OnCorrFacReportInfoChanged(object sender, EventArgs e)
        {
            this.UpdateDetailSectionState();
        }

        private void OnCommentReportInfoChanged(object sender, EventArgs e)
        {
            this.UpdateDetailSectionState();
        }
      
        private void OnEditLinkButtonClicked(object sender, EventArgs e)
        {
            new AdditionalTestWindow(this);
        }
      
        private void OnUseByDateButtonClicked(object sender, EventArgs e)
        {
            if (this.IsUpdating == true)
                return;
            
            this.use_by_date_linkbutton.Label = BatchDeadLine.GetNextDeadLineType();

            this.DetailSectionState.Set(DetailSectionStates.ReportNotSaved, true);
            this.Update(FinderUpdateState.ExpiryDateChanged);
        }

        private void OnExpiryDateMonthChanged(object sender, EventArgs e)
        {
            if (this.IsUpdating == true)
                return;

            this.DetailSectionState.Set(DetailSectionStates.ExpiryDateMonthlyFieldCorrect, this.expiry_date_month_combo.Active > 0);
            this.DetailSectionState.Set(DetailSectionStates.ReportNotSaved, true);
            this.Update(FinderUpdateState.ExpiryDateChanged);
        }

        private void OnExpiryDateYearChanged(object sender, EventArgs e)
        {
            if (this.IsUpdating == true)
                return;

            this.DetailSectionState.Set(DetailSectionStates.ExpiryDateYearFieldCorrect, this.expiry_date_year_combo.Active > 0);
            this.DetailSectionState.Set(DetailSectionStates.ReportNotSaved, true);
            this.Update(FinderUpdateState.ExpiryDateChanged);
        }


        private void UpdateDetailSectionState()
        {
            this.UpdateDetailSectionState(DetailSectionStates.None, false);
        }

        private void UpdateDetailSectionState(DetailSectionStates  state, bool value)
        {
            if (this.IsUpdating == true)
                return;

            DetailSectionStates before_state = this.DetailSectionState;
            if (state != DetailSectionStates.None)
                this.DetailSectionState.Set(state, value);
            this.DetailSectionState.Set(DetailSectionStates.ReportNotSaved, true);

            if (this.DetailSectionState != before_state)
                this.Update(FinderUpdateState.ReportChanged);
        }



        internal void OnQuickStepButtonClicked(object o, EventArgs e)
        {
            if (this.host.MainWindow == null)
                return;

            if (!this.host.MainWindow.Visible)
            {
                bool dont_show_again = this.Config.GuiSettings.DontShowQuickStepMessageAgain;
                this.host.ShowInfoMessage(this.finder_window, ref dont_show_again,
                                          Catalog.GetString("In Quickstep-mode (advanced mode) parameters of the spectrometer can be modified. " +
                                                            "This possibly leads to incorrect measurements or identifications.")
                                          );
                this.Config.GuiSettings.DontShowQuickStepMessageAgain = dont_show_again;
            }

            this.host.MainWindow.ShowAll();
            this.host.MainWindow.Present();
        }

        internal void OnQueryButtonClicked(object o, EventArgs e)
        {
            new QueryDialog(this.Config, this.GtkWindow);
        }

        internal void OnQuitButtonClicked(object o, EventArgs e)
        {
            try
            {
                if (this.RemindSave() == false)
                    return;
            }
            catch (Exception ex)
            {
                Console.WriteLine("Cannot call RemindSave(): " + ex.ToString());
            }

            if (Plugin.replace_gui)
            {
                if (Plugin.host.CheckForUnsavedSpectra() == false)
                    return;
            }

            WaitWindow wait_win = new WaitWindow(this.finder_window,
                                                     Catalog.GetString("The application is closing ..."),
                                                     500);
            this.Scanner.Exit();
            this.PollFinderButton = false;
            this.RequestQuit();

            if (this.Config.GuiSettings.WindowMaximized == false)
            {
                int width, height;
                this.finder_window.GetSize(out width, out height);
                this.Config.GuiSettings.WindowWidth = width;
                this.Config.GuiSettings.WindowHeight = height;
            }

            this.Config.GuiSettings.LastUser = this.GtkUsernameValue;
            this.Config.GuiSettings.LastSupplier = this.GtkSupplierValue;

            try
            {
                this.Config.WriteToXmlFile();
            }
            catch (Exception ex) 
            { 
                Console.WriteLine("Error while saving apo-ident configuration file: {0}", ex);
            }
         
            try
            {
                this.IdentServerClient.StopIdentModule();
            }
            catch (Exception ex)
            {
                Console.WriteLine("Failed to terminate IdentServer: " + ex.Message);
            }


            if (Plugin.replace_gui)
            {
                this.host.MainWindow.Destroy();
                this.host.Quit(true);
            }
            else
            {
                FinderWindow.current_instance = null;
                this.finder_window.Destroy();
            }
            wait_win.Destroy();
        }

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

        private void OnPreferencesButtonClicked(object o, EventArgs e)
        {            
            this.SubstanceManagement.ReselectSubstance();
            new Preferences(this);
        }

        private void OnPreferencesClosed(object sender, EventArgs e)
        {
            this.Update(FinderUpdateState.PreferencesClosed);
        }

        internal void OnAboutButtonClicked(object o, EventArgs e)
        {
            AboutDialog dialog = new AboutDialog();
            dialog.TransientFor = this.finder_window;

            dialog.ProgramName = "QuickStep " + Plugin.name;

            dialog.Version = Plugin.version;
            dialog.Comments = string.Format(Catalog.GetString("Chemometrics Plugin for QuickStep"));

            if (String.IsNullOrEmpty(this.Config.AcquisitionSettings.IdentServerVersion) == false)
            {
                string version = this.Config.AcquisitionSettings.IdentServerVersion + "-" +
                    this.Config.AcquisitionSettings.IdentServerSuffix;

                Gtk.VBox vbox_tmp = ((Gtk.VBox)dialog.VBox.Children[0]);
                List<Widget> widgets = new List<Widget>(vbox_tmp.Children);
                foreach (Widget widget in widgets)
                    vbox_tmp.Remove(widget);

                LinkButton ident_server_lb = new LinkButton("", String.Format(
                    Catalog.GetString("Ident-Modul Version: ") + version));
                ident_server_lb.TooltipText = Catalog.GetString("IdentModule license text");

                Gtk.Label comment_extenison_label = new Gtk.Label(Catalog.GetString("White reference: ") + "Zenith");
                comment_extenison_label.Text += "\n\n" + Catalog.GetString("kundenservice@apo-ident.de\n+49 (0)351 212 496 33");

                Gtk.Alignment add_align = new Alignment(0.5f, 0.5f, 0f, 0f);
                add_align.Add(ident_server_lb);
                VBox add_vbox = new VBox();
                add_vbox.Add(add_align);
                add_vbox.Add(comment_extenison_label);

                widgets.Insert(3, add_vbox);

                foreach (Widget widget in widgets)
                    vbox_tmp.Add(widget);

                ident_server_lb.Clicked += (sender, evnt) =>
                {
                    Glade.XML gui = new Glade.XML(null, "ApoIdent.Resources.Glade.FinderWindow.glade", "license_window", "Hiperscan");
                    gui.Autoconnect(this);

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

                    this.license_window.TransientFor = dialog;
 
                    if (this.IdentServerClient != null && this.IdentServerClient.License != null)
                        this.license_textview.Buffer.Text = Catalog.GetString(this.IdentServerClient.License);
                    else
                        this.license_textview.Buffer.Text = Catalog.GetString("License is currently not available.");

                    this.license_window.ShowAll();
                    this.license_window.WidthRequest = this.license_window.Allocation.Width + 50;
                };
            }
            else
            {
                dialog.Comments += Catalog.GetString("\nWhite reference: ") + "Zenith";
                dialog.Comments += "\n\n" + Catalog.GetString("kundenservice@apo-ident.de\n+49 (0)351 212 496 33");
            }

            dialog.Copyright = String.Format("2010-{0}, HiperScan GmbH", Plugin.copyright_year);

            dialog.WrapLicense = true;
            dialog.License = Catalog.GetString(Plugin.license);
            dialog.Authors = Plugin.authors;

            dialog.Website = Urls.OnlineHelp(ReferrerId.TeamViewer, true);
            dialog.WebsiteLabel = Catalog.GetString("Service Center");
            dialog.Logo = new Gdk.Pixbuf(Assembly.GetExecutingAssembly(), "ApoIdent.Resources.Images.logo.png");

            try
            {
                if (File.Exists(FilesAndDirs.UpdateCertificateFile) == true)
                {
                    Widget w = dialog.ActionArea.Children[2];
                    dialog.ActionArea.Remove(w);

                    LinkButton lb = new LinkButton("file:///usr/lib/identserver/update-certificate.pdf", Catalog.GetString("Certificate"));
                    lb.Clicked += delegate (object sender, EventArgs ee) {
                        try
                        {
                            System.Diagnostics.Process.Start(new Uri(FilesAndDirs.UpdateCertificateFile).ToString());
                        }
                        catch (Exception ex)
                        {
                            this.host.ShowInfoMessage(this.finder_window,
                                                      Catalog.GetString("Cannot open ULR '{0}': {1}"),
                                                      FilesAndDirs.UpdateCertificateFile, ex.Message);
                        }
                    };
                    lb.TooltipText = Catalog.GetString("Open update certificate");
                    dialog.ActionArea.Add(lb);
                    dialog.ActionArea.Add(w);

                    dialog.ShowAll();
                }
            }
            catch (Exception)
            {

            }

            dialog.IconName = Stock.About;
            dialog.Run();
            dialog.Destroy();
        }

        private void OnLicenseCloseButtonClicked(object sender, EventArgs e)
        {
            this.license_window.Destroy();
        }

        private void OnSubstanceManagementMenuButtonClicked(object sender, EventArgs e)
        {
            if (this.RemindSave() == false)
                return;

			new SubstanceExtensionDialog(this);

            this.SubstanceManagement.InitModelExtension();         
            this.SubstanceManagement.ResetSelection();
            this.IdentServerClient.DiscardResult();
			this.CreateNotMeasurableReport = false;
            this.Update(FinderUpdateState.AfterEditingUserDefinedSubstances);
        }

        public void ShowPDF(string file_name)
        {
            string path = Path.Combine("Resources", "Pdfs");
            path = Locale.ResourceFileRelativeToAssembly(Assembly.GetExecutingAssembly(), path, file_name, false);

            string url = "file://" + path.Replace("\\", "/");
            Console.WriteLine("Open URL: " + url);

            try
            {
                if (File.Exists(path) == false)
                    throw new Exception(String.Format(Catalog.GetString("Cannot find file '{0}'."), path));
                System.Diagnostics.Process.Start(url);
            }
            catch (Exception ex)
            {
                this.host.ShowInfoMessage(this.finder_window,
                                          Catalog.GetString("Cannot open ULR '{0}': {1}"),
                                          url, ex.Message);
            }
        }

        public void OnValidationReportButtonClicked(object sender, EventArgs e)
        {
            if (this.CreateNotMeasurableReport == true)
            {
                string message = String.Format(Catalog.GetString("Validation is not available when a NIR inactive substance ist selected."));
                this.host.ShowInfoMessage(this.finder_window, message);
                return;
            }

            if (this.IdentServerClient.HasValidResult == false)
            {
                string message = String.Format(Catalog.GetString("Validation is not available because of an analyzing error by IdentModule."));
                this.host.ShowInfoMessage(this.finder_window, message);
                return;
            }

            if (this.SubstanceManagement.CurrentModelClass.ValidationDocument == false)
            {
                string message = String.Format(Catalog.GetString("Validation is not available for this substance class."));
                this.host.ShowInfoMessage(this.finder_window, message);
                return;
            }

            if (this.Config.AcquisitionSettings.TestVersion == true)
            {
                string message = String.Format(Catalog.GetString("Validation is not available for Test-IdentModule."));
                this.host.ShowInfoMessage(this.finder_window, message);
                return;
            }


            try
            {
                string classification_id = this.IdentServerClient.Result.ClassificationId;
                string class_pdf_file = FilesAndDirs.ValidationFile(this.SubstanceManagement.CurrentModelClass.ModelClassName);
                Hiperscan.PdfLib.Pdf.OpenValidationDocumentation(class_pdf_file, "sect:classification" + classification_id);
            }
            catch (Exception ex)
            {
                string message = String.Format(Catalog.GetString("Cannot open validation document '{0}': {1}"),
                                               this.SubstanceManagement.CurrentSubstance.SelectedDefaultName,
                                               ex.Message);

                Gtk.MessageDialog mdlg = new Gtk.MessageDialog(this.finder_window,
                                                               Gtk.DialogFlags.Modal,
                                                               Gtk.MessageType.Info,
                                                               Gtk.ButtonsType.Ok,
                                                               message);

                mdlg.Title = Catalog.GetString("Error");

                mdlg.IconList = ApoIdentStock.FinderIcons;
                mdlg.Run();
                mdlg.Destroy();
            }
        }

        private void OnValidationExplanationLinkbuttonClicked(object sender, EventArgs e)
        {
            try
            {
                string class_pdf_file = FilesAndDirs.ValidationFile(this.SubstanceManagement.CurrentModelClass.ModelClassName);

                if (this.IdentServerClient.IsTruePositive == true)
                    Hiperscan.PdfLib.Pdf.OpenValidationDocumentation(class_pdf_file, "sect:spezifitaet");
                else
                    Hiperscan.PdfLib.Pdf.OpenValidationDocumentation(class_pdf_file, "sect:erkennungsrate");
            }
            catch (Exception ex)
            {
                string message = String.Format(Catalog.GetString("Cannot open validation document '{0}': {1}"),
                                               this.SubstanceManagement.CurrentSubstance.SelectedDefaultName,
                                               ex.Message);

                Gtk.MessageDialog mdlg = new Gtk.MessageDialog(this.finder_window,
                                                               Gtk.DialogFlags.Modal,
                                                               Gtk.MessageType.Info,
                                                               Gtk.ButtonsType.Ok,
                                                               message);

                mdlg.Title = Catalog.GetString("Error");
                mdlg.IconList = ApoIdentStock.FinderIcons;
                mdlg.Run();
                mdlg.Destroy();
            }
        }
      
        public void ShowErrorEmptyReference(object o, EventArgs e)
        {
            this.ShowPDF("error_black_reference.pdf");
        }

        public void ShowErrorWhiteReference(object o, EventArgs e)
        {
            this.ShowPDF("error_white_reference.pdf");
        }

        public void ShowErrorDieReference(object o, EventArgs e)
        {
            this.ShowPDF("error_transflectance_reference.pdf");
        }

        public void RequestQuit()
        {
            if (this.Finder != null)
                this.Finder.QuitRequested = true;
        }

        public static string GdkColorToPango(Gdk.Color color)
        {
            return color.ToString().Replace("rgb:", "#").Replace("/", string.Empty);
        }

        private void SetToolButtonTextColor(ToolButton tb, Gdk.Color c)
        {
            tb.Realized += delegate {
                try
                {
                    Button b = tb.Child as Button;
                    VBox vbox = b.Child as VBox;

                    foreach (Widget w in vbox.Children)
                    {
                        if (w is Gtk.Label)
                        {
                            Gtk.Label l = w as Gtk.Label;
                            l.ModifyFg(StateType.Normal, c);
                            l.ModifyFg(StateType.Prelight, c);
                            l.ModifyFg(StateType.Active, c);
                            l.ModifyFg(StateType.Selected, c);
                        }
                    }
                }
                catch { }
            };
        }

        private void OnScanDetected(object sender, EventArgs e)
        {
            Console.WriteLine("Scanner read: " + this.Scanner.ScannerInfo);
            string messagge = String.Empty;
            MessageDialog mdlg = null;

            if (this.Scanner.ScannerInfo.InfoType != ApoIdentCommon.ScannerInfoType.Complete)
            {
                if (String.IsNullOrEmpty(this.Scanner.ScannerInfo.PZN?.Trim()) == false)
                    messagge = String.Format(Catalog.GetString("Cannot find the PPN {0} in the Apo-Ident substance database."), this.Scanner.ScannerInfo.PZN.Trim());
                else                                                             
                    messagge = Catalog.GetString("The scan is not valid or incomplete. Please close this window and retry.");
                
                mdlg = new MessageDialog(this.finder_window,
                                                       DialogFlags.Modal,
                                                       Gtk.MessageType.Error,
                                                       ButtonsType.Ok,
                                                       messagge);

                mdlg.IconList = ApoIdentStock.FinderIcons;
                mdlg.Run();
                mdlg.Destroy();
                if (this.SubstanceManagement.ProtocolCompletion == ProtocolAutoCompletionType.Scanner)
                    this.Scanner.ScannerInfo = this.Scanner.PreviewScanInfo;
                return;
            }

            if (this.SubstanceManagement.GetModelClasses(this.Scanner.ScannerInfo.SubstanceName).Count == 0)
            {
                messagge = String.Format(Catalog.GetString("Can not find scanned substance '{0}' in our substance database."),
                                         this.Scanner.ScannerInfo.SubstanceName);
                mdlg = new MessageDialog(this.finder_window,
                                                       DialogFlags.Modal,
                                         Gtk.MessageType.Error,
                                                       ButtonsType.Ok,
                                                       messagge);

                mdlg.IconList = ApoIdentStock.FinderIcons;
                mdlg.Run();
                mdlg.Destroy();
                if (this.SubstanceManagement.ProtocolCompletion == ProtocolAutoCompletionType.Scanner)
                    this.Scanner.ScannerInfo = this.Scanner.PreviewScanInfo;
                return;
            }

            this.IsUpdating = true;
            this.Scanner.PreviewScanInfo = this.Scanner.ScannerInfo;
            this.substance_search_entry.Text = this.Scanner.ScannerInfo.SubstanceName;
            this.SubstanceManagement.SelectNewSubstance(this.Scanner.ScannerInfo);
            this.IsUpdating = false;
            this.Update(FinderUpdateState.SubstanceChangedFromExtern);
        }

        private bool GetScanCondition()
        {
            return this.finder_window.IsActive && this.busy == false && this.InitState == ServerDeviceState.Success;
        }

        public GreetingWindow GreetingWin
        {
            set
            {
                this.greeting_window = value;
            }
        }

        public Configurations Config { get; set; }
        public int MainThreadId { get; private set; }
    }
}
