// CorrectionDialog.cs created with MonoDevelop
// User: klose at 12:58 21.08.2009
// CVS release: $Id: CorrectionDialog.cs,v 1.18 2011-01-06 16:36:27 klose Exp $
//
//    QuickStep: Acquire, view and identify spectra 
//    Copyright (C) 2009  Thomas Klose, 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.Drawing;
using System.IO;

using Glade;

using Gtk;

using Hiperscan.Extensions;
using Hiperscan.NPlotEngine;
using Hiperscan.QuickStep.Figure;
using Hiperscan.SGS;
using Hiperscan.SGS.Benchtop;
using Hiperscan.SGS.Common;
using Hiperscan.Spectroscopy;
using Hiperscan.Unix;


namespace Hiperscan.QuickStep.Dialog
{

    public class CorrectionDialog
    {
        private enum Algorithm
        {
            ParabolicFit,
            SGCubicSpline
        }
            
        [Widget] Gtk.Dialog correction_dialog  = null;
        [Widget] Gtk.ComboBox algorithm_combo1 = null;
        
        [Widget] Gtk.SpinButton window_size_spin1  = null;
        [Widget] Gtk.SpinButton noise_limit_spin1  = null;
        [Widget] Gtk.SpinButton cutoff_limit_spin1 = null;
        [Widget] Gtk.SpinButton epsilon_spin1      = null;
        
        [Widget] Gtk.SpinButton reference_offset_spin1 = null;
        [Widget] Gtk.SpinButton current_offset_spin1   = null;
        [Widget] Gtk.SpinButton max_count_spin1        = null;
        
        [Widget] Gtk.VPaned correction_plot_vpaned = null;
        
        [Widget] Gtk.Button correction_delete_button = null;
        [Widget] Gtk.Button reference_button         = null;
        [Widget] Gtk.Button open_reference_button    = null;
        [Widget] Gtk.Button correction_record_button = null;
        [Widget] Gtk.Button correction_apply_button  = null;
        [Widget] Gtk.Button correction_save_button   = null;
        
        [Widget] Gtk.CheckButton lowpass_checkbutton = null;
        
        private readonly ScrolledPlot scrolled_plot1;
        private readonly ScrolledPlot scrolled_plot2;
        
        private bool deny_correction_record = true;

        IDevice device;

        readonly ServiceDialog hw_dialog;
        readonly UserInterface ui;

        private Spectrum current_spectrum;
        
        private readonly DataSet reference_extrema_10nm;
        private readonly DataSet reference_extrema_15nm;

        private DataSet reference_extrema;
        private DataSet current_extrema;
        
        private WavelengthCorrection correction;
        
        private static Dictionary<IDevice, CorrectionDialog> Instances = new Dictionary<IDevice, CorrectionDialog>();

        private int WindowSize     => (int)this.window_size_spin1.Value;
        private double NoiseLimit  => this.noise_limit_spin1.Value;
        private double CutoffLimit => this.cutoff_limit_spin1.Value;
        private int Epsilon        => (int)this.epsilon_spin1.Value;

        private int ReferenceOffset => (int)this.reference_offset_spin1.Value;
        private int CurrentOffset   => (int)this.current_offset_spin1.Value;
        private int MaxCount        => (int)this.max_count_spin1.Value;

        private bool updating = false;

        private delegate DataSet.Extrema FindExtrema(int window_size, double noise_limit, double cutoff, int epsilon);
        
        
        public CorrectionDialog(IDevice device, ServiceDialog hw_dialog, UserInterface ui)
        {
            if (CorrectionDialog.Instances.ContainsKey(device))
            {
                CorrectionDialog.Instances[device].correction_dialog.Present();
                return;
            }

            CorrectionDialog.Instances.Add(device, this);
            this.device    = device;
            this.hw_dialog = hw_dialog;
            this.ui        = ui;

            Glade.XML gui;
            try
            {
                gui = new Glade.XML(null, "UserInterface.glade", "correction_dialog", MainClass.ApplicationDomain);
            }
            catch
            {
                Console.WriteLine("UserInterface: No embedded resource for GUI found. Trying to read external Glade file."); 
                gui = new Glade.XML("UserInterface.glade", "correction_dialog", null);
            }
            gui.Autoconnect(this);

            this.correction_dialog.DefaultWidth = 800;
            
            this.scrolled_plot1 = new ScrolledPlot(null);
            this.scrolled_plot1.Plot.BackgroundColor = this.ui.BackgroundColor;
            this.scrolled_plot1.Plot.XLabel = Catalog.GetString("Wavelength / nm");
            this.scrolled_plot1.Plot.YLabel = Catalog.GetString("Absorbance");
            this.scrolled_plot1.Plot.DisableSelections = true;
            this.correction_plot_vpaned.Pack1(this.scrolled_plot1, true, false);

            this.scrolled_plot2 = new ScrolledPlot(null);
            this.scrolled_plot2.Plot.BackgroundColor = this.ui.BackgroundColor;
            this.scrolled_plot2.Plot.XLabel = Catalog.GetString("Wavelength / nm");
            this.scrolled_plot2.Plot.YLabel = Catalog.GetString("Deviation / nm");
            this.scrolled_plot2.Plot.DisableSelections = true;
            this.correction_plot_vpaned.Pack2(this.scrolled_plot2, true, false);

            this.scrolled_plot2.HZoombar.Changed += this.OnHzbChanged;
            
            try
            {
                this.reference_extrema_10nm = WavelengthCorrection.GetWavelengthReferences(WavelengthCorrection.ReferenceType.NIST10nm);
                this.reference_extrema_15nm = WavelengthCorrection.GetWavelengthReferences(WavelengthCorrection.ReferenceType.NIST15nm);
            }
            catch (Exception ex)
            {
                throw new Exception(Catalog.GetString("Cannot read wavelength references:") +
                                    " " + ex.Message, ex);
            }
            
            this.Update();
            
            this.algorithm_combo1.Active = (int)Algorithm.SGCubicSpline;
            this.window_size_spin1.Value = 15.0;
            this.noise_limit_spin1.Value = 0.002;
            this.current_offset_spin1.Value = -2.0;
            this.epsilon_spin1.Value = 5.0;
            
            this.correction_dialog.ShowAll();
            this.scrolled_plot1.HZoombar.Visible = false;
            
            this.correction_dialog.DeleteEvent += this.OnDeleteEvent;                        
            
            this.UpdateButtons();

            if (this.device.CurrentConfig.HasWavelengthCorrection)
                this.correction_delete_button.Sensitive = true;

            this.correction_apply_button.Sensitive = false;
        }
        
        private void UpdateButtons()
        {
            if (this.device != null     && 
                this.device.IsIdle      && 
                this.device.IsConnected && 
                this.device.CurrentConfig.HasWavelengthCorrection)
            {
                this.correction_delete_button.Sensitive = true;
            }
            else
            {
                this.correction_delete_button.Sensitive = false;
            }

            if (this.device != null && this.device.IsFinder)
            {
                this.reference_button.Sensitive      = true;
                this.open_reference_button.Sensitive = true;
            }
            else
            {
                this.reference_button.Sensitive      = false;
                this.open_reference_button.Sensitive = false;
            }
            
            if (this.device != null     && 
                this.device.IsIdle      && 
                this.device.IsConnected &&
                this.device.CurrentConfig.HasReferenceSpectrum && 
                (this.deny_correction_record == false || device.IsFinder == false))
            {
                this.correction_record_button.Sensitive = true;
            }
            else
            {
                this.correction_record_button.Sensitive = false;
            }

            if (this.device != null     &&
                this.device.IsIdle      &&
                this.device.IsConnected && 
                (this.correction != null || (this.current_extrema == null && this.device.CurrentConfig.HasWavelengthCorrection)))
            {
                this.correction_apply_button.Sensitive = true;
            }
            else
            {
                this.correction_apply_button.Sensitive = false;
            }

            if (this.current_spectrum != null)
                this.correction_save_button.Sensitive = true;
            else
                this.correction_save_button.Sensitive = false;
        }
        
        private void Update()
        {
            this.Update(false);
        }
        
        private void Update(bool called_from_clear)
        {
            if (this.updating)
                return;
            
            this.updating = true;
            if (this.correction_dialog.GdkWindow != null)
                this.correction_dialog.GdkWindow.Cursor = new Gdk.Cursor(Gdk.CursorType.Watch);

            UserInterface.Instance.ProcessEvents();
            
            // update absorbance plots
            this.scrolled_plot1.Plot.Clear();
            this.scrolled_plot2.Plot.Clear();
            
            this.scrolled_plot1.VZoombar.Sensitive = false;
            this.scrolled_plot2.VZoombar.Sensitive = false;
            this.scrolled_plot2.HZoombar.Sensitive = false;

            IPlotEngine plot = this.scrolled_plot1.Plot;
            
            WavelengthCorrection lm_wlc = new WavelengthCorrection(new double[] { 0.0, 0.0, 0.0});
            
            if (this.current_spectrum != null)
            {

                if (this.lowpass_checkbutton.Active)
                    this.reference_extrema = new DataSet(this.reference_extrema_15nm);
                else
                    this.reference_extrema = new DataSet(this.reference_extrema_10nm);

                this.reference_extrema.RemoveRange(0, this.ReferenceOffset);
                
                int diff = this.reference_extrema.Count - this.MaxCount;
                if (diff > 0)
                    this.reference_extrema.RemoveRange(this.MaxCount, diff);
                
                this.reference_extrema.Label = Catalog.GetString("Reference maxima");
                this.reference_extrema.IsContinuous = false;
                this.reference_extrema.IsDropPoint  = true;
                this.reference_extrema.Id = "1_reference_maxima";
                
                plot.AddDataSet(this.reference_extrema);

                if (this.current_spectrum.HasAbsorbance)
                {
                    DataSet absorbance = new DataSet(this.current_spectrum.Absorbance.SNV())
                    {
                        Id = "2_current_spectrum",
                        Label = this.current_spectrum.Label
                    };

                    try
                    {
                        DataSet.Extrema extrema;
                        switch (this.algorithm_combo1.Active)
                        {
                            
                        case (int)Algorithm.ParabolicFit:
                            extrema = absorbance.FindExtrema(this.WindowSize, 
                                                             this.NoiseLimit,
                                                             this.CutoffLimit,
                                                             this.Epsilon);
                            break;
                            
                        case (int)Algorithm.SGCubicSpline:
                            extrema = absorbance.FindExtremaSG(this.Epsilon,
                                                               this.WindowSize,
                                                               this.NoiseLimit,
                                                               this.CutoffLimit);
                            break;
                            
                        default:
                            throw new Exception(Catalog.GetString("Unknown algorithm:") + 
                                                " " + 
                                                ((Algorithm)this.algorithm_combo1.Active).ToString());

                        }

                        List<double> x_max = new List<double>();
                        List<double> y_max = new List<double>();
                        for (int ix=0; ix < this.reference_extrema.Count; ++ix)
                        {
                            double[] max = extrema.Maxima.FindNearest(this.reference_extrema.X[ix]);
                            x_max.Add(max[0]);
                            y_max.Add(max[1]);
                        }

                        this.current_extrema = new DataSet(x_max, y_max)
                        {
                            IsContinuous = false
                        };
                        this.current_extrema.RemoveRange(0, this.CurrentOffset);

                        diff = this.current_extrema.Count - this.MaxCount;
                        if (diff > 0)
                            this.current_extrema.RemoveRange(this.MaxCount, diff);

                        this.current_extrema.Id    = "3_current_maxima";
                        this.current_extrema.Label = $"{this.current_spectrum.Label} {Catalog.GetString("maxima")}";
                        this.current_extrema.Color = Color.Red;
                        
                        // LM-fit for absorbance
                        Spectrum ref_spectrum = Finder.GetWavelengthReference(ReferenceWheelVersion.TitaniumDioxide);
                        ref_spectrum.LimitBandwidth = this.lowpass_checkbutton.Active;
                        lm_wlc = new WavelengthCorrection(ref_spectrum, this.current_spectrum, null, true);
                        DataSet lm_absorbance = lm_wlc.CorrectedSpectrum(absorbance);
                        lm_absorbance.Label = Catalog.GetString("Absorbance (LM)");
                        DataSet ref_absorbance = ref_spectrum.Absorbance.SNV();
                        ref_absorbance.Label = Catalog.GetString("Reference Absorbance");

                        absorbance.Color = Color.Green;
                        ref_absorbance.Color = Color.Black;
                        lm_absorbance.Color = Color.DarkOrange;
                        
                        plot.AddDataSet(absorbance);
                        plot.AddDataSet(ref_absorbance);
                        plot.AddDataSet(lm_absorbance);
                        plot.AddDataSet(this.current_extrema);
                    }
                    catch (Exception ex)
                    {
                        InfoMessageDialog.Show(this.correction_dialog, 
                                         Catalog.GetString("Cannot calculate calibration extrema:") +
                                         " " +
                                         ex.Message);
                        this.current_extrema  = null;
                    }
                }
                else
                {
                    InfoMessageDialog.Show(this.correction_dialog,
                                     Catalog.GetString("Cannot display calibration extrema: Spectrum has no absorbance."));
                }
            }
            
            if (this.current_spectrum != null)
            {
                plot.XLabel = Catalog.GetString("Wavelength / nm");
                plot.YLabel = Catalog.GetString("SNV(Absorbance)");

                this.scrolled_plot1.VZoombar.Sensitive = true;
                this.scrolled_plot2.HZoombar.Sensitive = true;
            }
            else
            {
                this.scrolled_plot1.Plot.Clear();
            }

            // update difference plot
            if (this.current_extrema != null)
            {
                List<double> x = new List<double>();
                List<double> y = new List<double>();

                plot = this.scrolled_plot2.Plot;

                int ix = 0;
                while (ix < this.reference_extrema.Count &&
                       ix < this.current_extrema.Count)
                {
                    x.Add(this.reference_extrema.X[ix]);
                    y.Add(this.current_extrema.X[ix] - this.reference_extrema.X[ix]);
                    ++ix;
                }

                DataSet deviation = new DataSet(x, y)
                {
                    IsContinuous = false,
                    IsDropPoint = true,
                    Label = Catalog.GetString("Deviation")
                };

                this.correction = new WavelengthCorrection(x, y);
                
                DataSet fit = this.correction.FitFunction(this.current_spectrum.Absorbance.XMin(),
                                                          this.current_spectrum.Absorbance.XMax(),
                                                          10.0);
                fit.Label = Catalog.GetString("Correction fit");
                fit.Color = Color.Green;
                
                DataSet lm_fit = lm_wlc.FitFunction(this.current_spectrum.Absorbance.XMin(),
                                                    this.current_spectrum.Absorbance.XMax(),
                                                    10.0);
                lm_fit.Label = Catalog.GetString("Correction fit (LM)");
                lm_fit.Color = Color.DarkOrange;
                
                plot.AddDataSet(deviation);
                plot.AddDataSet(fit);
                plot.AddDataSet(lm_fit);
                plot.XLabel = Catalog.GetString("Wavelength / nm");
                plot.YLabel = Catalog.GetString("Deviation / nm");
                plot.SetGlobalXMinMax(this.scrolled_plot1.Plot.GlobalXMin,
                                      this.scrolled_plot1.Plot.GlobalXMax);

                this.scrolled_plot2.VZoombar.Sensitive = true;
            }
            else if (called_from_clear == false && 
                     this.device.CurrentConfig.HasWavelengthCorrection)
            {
                plot = this.scrolled_plot2.Plot;
                
                DataSet fit = this.device.CurrentConfig.WavelengthCorrection.FitFunction(1000.0, 1900.0, 10.0);
                fit.Label = Catalog.GetString("Correction fit");
                
                plot.AddDataSet(fit);
                plot.XLabel = Catalog.GetString("Wavelength / nm");
                plot.YLabel = Catalog.GetString("Deviation / nm");
            }
            else if (called_from_clear == false && this.correction != null)
            {
                plot = this.scrolled_plot2.Plot;
                
                DataSet fit = this.correction.FitFunction(1000.0, 1900.0, 10.0);
                fit.Label = Catalog.GetString("Correction fit");
                
                plot.AddDataSet(fit);
                plot.XLabel = Catalog.GetString("Wavelength / nm");
                plot.YLabel = Catalog.GetString("Deviation / nm");
            }
            else
            {
                this.scrolled_plot2.Plot.Clear();
            }
                    
            if (this.correction_dialog.GdkWindow != null)
                this.correction_dialog.GdkWindow.Cursor = new Gdk.Cursor(Gdk.CursorType.Arrow);
            
            this.updating = false;
            
            this.UpdateButtons();
        }
        
        private void SetWatchCursor()
        {
            if (this.correction_dialog.IsRealized == false)
                return;
            
            this.correction_dialog.Sensitive = false;
            this.correction_dialog.GdkWindow.Cursor = new Gdk.Cursor(Gdk.CursorType.Watch);
            UserInterface.Instance.ProcessEvents();
        }
        
        private void SetArrowCursor()
        {
            if (this.correction_dialog.IsRealized == false)
                return;
            
            this.correction_dialog.Sensitive = true;
            this.correction_dialog.GdkWindow.Cursor = new Gdk.Cursor(Gdk.CursorType.Arrow);
            UserInterface.Instance.ProcessEvents();
        }

        private void OnHzbChanged(Zoombar.Zoombar zb, bool released)
        {
            this.scrolled_plot1.HZoombar.Pan  = zb.Pan;
            this.scrolled_plot1.HZoombar.Zoom = zb.Zoom;
        }
        
        internal void OnDeleteButtonClicked(object o, EventArgs e)
        {
            this.current_extrema  = null;
            this.current_spectrum = null;
            this.correction = null;
            this.Update(true);
            this.correction_delete_button.Sensitive = false;
        }
        
        internal void OnReferenceButtonClicked(object o, EventArgs e)
        {
            if (this.device.IsFinder && Finder.HasZenithRecalibrationReference(this.device))
                this.AutoRecalibrate();
            else
                this.Reference(false);
        }
        
        internal void OnOpenReferenceButtonClicked(object o, EventArgs e)
        {
            this.Reference(true);
        }

        private void AutoRecalibrate()
        {
            MessageDialog dialog = new MessageDialog(this.correction_dialog, 
                                                     DialogFlags.Modal, 
                                                     MessageType.Question,
                                                     ButtonsType.YesNo,
                                                     Catalog.GetString("This wavelength correction procedure is only available for internal TiO2 or BaSO4 standards. Would you like to perform a standard wavelength correction?"));
            ResponseType choice = (ResponseType)dialog.Run();
            dialog.Destroy();
            UserInterface.Instance.ProcessEvents();

            if (choice == ResponseType.Yes)
            {
                WavelengthCorrection sav_wlc = this.device.CurrentConfig.WavelengthCorrection;
                try
                {
                    Finder finder = new Finder(this.device, ReferenceWheelVersion.Auto,
                                               ExternalWhiteReferenceType.Unknown,
                                               MainClass.ApplicationName);
                    finder.RecalibrateInternal(false);
                    this.correction = finder.Device.CurrentConfig.WavelengthCorrection;
                }
                catch (Exception ex)
                {
                    this.ui.ShowInfoMessage(this.correction_dialog, 
                                            Catalog.GetString("Cannot perform standard wavelength correction: {0}"),
                                            ex.Message);
                }
                finally
                {
                    this.device.CurrentConfig.WavelengthCorrection = sav_wlc;
                }
            }

            this.Update();
            return;
        }
        
        private void Reference(bool open_ref)
        {
            bool apply_sensitive = false;
            
            if (this.device.IsFinder == false)
                return;
            
            this.SetWatchCursor();
            
            if (open_ref == false)
            {
                this.current_extrema  = null;
                this.current_spectrum = null;
                this.correction = null;
                apply_sensitive = this.correction_apply_button.Sensitive;
            }
            
            try
            {
                // dark intensity
                this.device.SetFinderPosition(FinderWheelPosition.White, UserInterface.Instance.ProcessEvents);
                Spectrum di_spectrum = this.device.Single(false);
                if (di_spectrum == null)
                    return;
                this.device.CurrentConfig.DarkIntensity = new DarkIntensity(di_spectrum.RawIntensity.YMean(),
                                                                            di_spectrum.RawIntensity.YSigma());
                this.device.CurrentConfig.DarkIntensityType = Spectrometer.DarkIntensityType.Record;
                
                // reference
                if (open_ref)
                    this.device.SetFinderPosition(FinderWheelPosition.Sample, UserInterface.Instance.ProcessEvents);
                else
                    this.device.SetFinderPosition(FinderWheelPosition.White, UserInterface.Instance.ProcessEvents);
                Spectrum bg_spectrum = this.device.Single(true);
                if (bg_spectrum == null)
                    return;
                bg_spectrum.LimitBandwidth = this.lowpass_checkbutton.Active;
                this.device.CurrentConfig.ReferenceSpectrum     = bg_spectrum;
                this.device.CurrentConfig.ReferenceSpectrumType = Spectrometer.ReferenceSpectrumType.Record;
                
                this.ui.UpdatePropertiesSpectrometerControls();
                this.Update();
                
                // plots
                this.scrolled_plot1.Plot.Clear();
                this.scrolled_plot2.Plot.Clear();
                
                this.scrolled_plot1.VZoombar.Sensitive = false;
                this.scrolled_plot2.VZoombar.Sensitive = false;
                this.scrolled_plot2.HZoombar.Sensitive = false;
                
                IPlotEngine plot = this.scrolled_plot1.Plot;
                
                bg_spectrum.Label = Catalog.GetString("Raw reference");
                bg_spectrum.Color = Color.Blue;
                bg_spectrum.Id = "bg";
                di_spectrum.Label = Catalog.GetString("Raw dark intensity");
                di_spectrum.Color = Color.Green;
                di_spectrum.Id = "di";
                
                plot.AddDataSet(bg_spectrum.RawIntensity);
                plot.AddDataSet(di_spectrum.RawIntensity);
                
                this.scrolled_plot1.VZoombar.Sensitive = true;
                this.scrolled_plot2.HZoombar.Sensitive = true;
                
                this.deny_correction_record = open_ref;
                this.UpdateButtons();
            }
            catch (Exception ex)
            {
                this.current_extrema  = null;
                this.current_spectrum = null;
                this.correction = null;
                InfoMessageDialog.Show(this.correction_dialog, 
                                 Catalog.GetString("Cannot reference spectrometer:") +
                                 " " + ex);
                return;
            }
            finally
            {
                this.SetArrowCursor();
                this.correction_apply_button.Sensitive = apply_sensitive;
            }
        }
        
        internal void OnOpenButtonClicked(object o, EventArgs e)
        {
            FileChooserDialog chooser = new FileChooserDialog(Catalog.GetString("Choose spectrum"), 
                                                              this.correction_dialog, 
                                                              FileChooserAction.Open,
                                                              Stock.Open,
                                                              ResponseType.Ok,
                                                              Stock.Cancel,
                                                              ResponseType.Cancel);
            foreach (FileTypeInfo file_type in this.ui.FileTypes)
            {
                if (file_type.RwType != FileRwType.WriteOnly && !file_type.IsContainer)
                    chooser.AddFilter(this.ui.GetFileFilter(file_type));
            }
            chooser.SelectMultiple = false;
            
            try
            {
                ResponseType choice = (ResponseType)chooser.Run();
                if (choice == ResponseType.Ok)
                {
                    chooser.Hide();
                    
                    FileTypeInfo file_type = this.ui.GetFileTypeInfo(chooser.Filter, true, false);
                    
                    Spectrum spectrum = file_type.Read(chooser.Filename);
                    
                    if (string.IsNullOrEmpty(spectrum.Label))
                        spectrum.Label = Path.GetFileName(chooser.Filename);

                    spectrum.WavelengthCorrection = null;
                    this.current_spectrum = spectrum;
                    this.current_spectrum.LimitBandwidth = this.lowpass_checkbutton.Active;
                }
            }
            catch (Exception ex)
            {
                InfoMessageDialog.Show(this.correction_dialog, 
                                 Catalog.GetString("Error while reading file: {0}"),
                                 ex.Message);
            }
            finally
            {
                chooser.Destroy();
            }
            
            this.Update();
        }
        
        internal void OnRecordButtonClicked(object o, EventArgs e)
        {
            this.SetWatchCursor();
            WavelengthCorrection save = this.device.CurrentConfig.WavelengthCorrection;
            this.device.CurrentConfig.WavelengthCorrection = null;
            try
            {
                this.device.SetFinderPosition(FinderWheelPosition.Reference, UserInterface.Instance.ProcessEvents);
                this.current_spectrum = this.device.Single(true);
                this.current_spectrum.LimitBandwidth = this.lowpass_checkbutton.Active;
                this.device.SetAbsorbance(this.current_spectrum);
                this.device.CurrentConfig.WavelengthCorrection = save;
            }
            catch (Exception ex)
            {
                this.current_extrema  = null;
                this.current_spectrum = null;
                this.correction = null;
                InfoMessageDialog.Show(this.correction_dialog, 
                                 Catalog.GetString("Cannot acquire spectrum:") +
                                 " " + ex.Message);
                return;
            }
            finally
            {
                this.SetArrowCursor(); 
            }

            this.Update();
        }
        
        internal void OnApplyButtonClicked(object o, EventArgs e)
        {
            this.device.CurrentConfig.WavelengthCorrection = this.correction;
            this.hw_dialog.Update();
            this.ui.UpdatePropertiesSpectrometerControls();
            
            this.UpdateButtons();
            this.correction_apply_button.Sensitive = false;
        }
        
        internal void OnCorrectionSaveButtonClicked(object o, EventArgs e)
        {
            if (this.current_spectrum == null)
                return;
            
            try
            {
                Spectrum spectrum = this.current_spectrum.Clone();
                this.device.SetWavelengthCorrection(spectrum);
                string fname = this.ui.GenerateFilename(spectrum);
                this.ui.SaveNewSpectrum(spectrum, fname + "_oxid");
            }
            catch (Exception ex)
            {
                InfoMessageDialog.Show(this.correction_dialog, 
                                 Catalog.GetString("Cannot save reference spectrum: {0}"),
                                 ex.Message);
            }
        }


        internal void OnCloseButtonClicked(object o, System.EventArgs e)
        {
            CorrectionDialog.Instances.Remove(this.device);
            this.correction_dialog.Destroy();
        }
        
        private void OnDeleteEvent(System.Object o, DeleteEventArgs e)
        {
            this.OnCloseButtonClicked(o, e);
            e.RetVal = true;
        }

        internal void OnLowpassCheckbuttonToggled(object o, EventArgs e)  => this.Update();
        internal void OnWindowSizeSpinChanged(object o, EventArgs e)      => this.Update();
        internal void OnNoiseLimitSpinChanged(object o, EventArgs e)      => this.Update();
        internal void OnCutoffLimitSpinChanged(object o, EventArgs e)     => this.Update();
        internal void OnEpsilonSpinChanged(object o, EventArgs e)         => this.Update();
        internal void OnReferenceOffsetSpinChanged(object o, EventArgs e) => this.Update();
        internal void OnCurrentOffsetSpinChanged(object o, EventArgs e)   => this.Update();
        internal void OnMaxCountSpinChanged(object o, EventArgs e)        => this.Update();
        internal void OnAlgorithmComboChanged(object o, EventArgs e)      => this.Update();
    }
}
