// 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/>.
//
/// \file CorrectionDialog.cs
/// \brief short description
///
/// detailed description

using System;
using System.Text;
using System.Text.RegularExpressions;
using System.IO;
using System.Collections.Generic;
using System.Threading;
using System.Globalization;
using System.Drawing;
using Gtk;
using Glade;

using Hiperscan.SGS;
using Hiperscan.FileIO;
using Hiperscan.Spectroscopy;
using Hiperscan.GUI.Figure;
using Hiperscan.Extensions;
using Hiperscan.Unix;

namespace Hiperscan.GUI
{
	
	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 ScrolledPlot scrolled_plot1;
		private ScrolledPlot scrolled_plot2;
		
		private bool deny_correction_record = true;
		
		Device device;
		ServiceDialog hw_dialog;
		UserInterface ui;

		private Spectrum current_spectrum;
		
		private DataSet reference_extrema_10nm;
		private DataSet reference_extrema_15nm;
		private DataSet reference_extrema;
		private DataSet current_extrema;
		
		private WavelengthCorrection correction;
		
		private static Dictionary<Device,CorrectionDialog> Instances = new Dictionary<Device,CorrectionDialog>();
		
		private int    window_size {get { return (int)this.window_size_spin1.Value;} }
		private double noise_limit {get { return this.noise_limit_spin1.Value;     } }
		private double cutoff      {get { return this.cutoff_limit_spin1.Value;    } }
		private int    epsilon     {get { return (int)this.epsilon_spin1.Value;    } }

		private int reference_offset {get { return (int)this.reference_offset_spin1.Value;} }
		private int current_offset   {get { return (int)this.current_offset_spin1.Value;  } }
		private int max_count        {get { return (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(Device 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, "GUI.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.hzb.Changed += new ZoomBar.ZoomBar.ChangedHandler(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 reference wavelength references:") +
				                    " " + ex.Message);
			}
			
			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.hzb.Visible = false;
			
			this.correction_dialog.DeleteEvent += new DeleteEventHandler(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.HasBackgroundSpectrum && 
			    (!this.deny_correction_record || !device.IsFinder))
				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.ProcessEvents_();
			
			// update absorbance plots
			this.scrolled_plot1.Plot.Clear();
			this.scrolled_plot2.Plot.Clear();
			
			this.scrolled_plot1.vzb.Sensitive = false;
			this.scrolled_plot2.vzb.Sensitive = false;
			this.scrolled_plot2.hzb.Sensitive = false;

			IPlotEngine plot = this.scrolled_plot1.Plot;
			
			WavelengthCorrection lm_wlc = new WavelengthCorrection(new double[3] {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.reference_offset);
				
				int diff = this.reference_extrema.Count - this.max_count;
				if (diff > 0)
					this.reference_extrema.RemoveRange(this.max_count, 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());
					absorbance.Id    = "2_current_spectrum";
					absorbance.Label = this.current_spectrum.Label;
					
					try
					{
						DataSet.Extrema extrema;
						switch (this.algorithm_combo1.Active)
						{
							
						case (int)Algorithm.ParabolicFit:
							extrema = absorbance.FindExtrema(this.window_size, 
							                                 this.noise_limit,
							                                 this.cutoff,
							                                 this.epsilon);
							break;
							
						case (int)Algorithm.SGCubicSpline:
							extrema = absorbance.FindExtremaSG(this.epsilon,
							                                   this.window_size,
							                                   this.noise_limit,
							                                   this.cutoff);
							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);
						this.current_extrema.IsContinuous = false;
						this.current_extrema.RemoveRange(0, this.current_offset);

						diff = this.current_extrema.Count - this.max_count;
						if (diff > 0)
							this.current_extrema.RemoveRange(this.max_count, 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)
					{
						new InfoMessage(this.correction_dialog, 
						                Catalog.GetString("Cannot calculate calibration extrema:") +
						                " " + ex.Message);
						this.current_extrema  = null;
					}
				}
				else
				{
					new InfoMessage(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)");
				//plot.DoPlot();
				//this.scrolled_plot1.ReplacePlot(plot);
				//this.scrolled_plot1.Plot.StartRefreshing();

				this.scrolled_plot1.vzb.Sensitive = true;
				this.scrolled_plot2.hzb.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);
				deviation.IsContinuous = false;
				deviation.IsDropPoint  = true;
				deviation.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(),
				                                          10f);
				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(),
				                                    10f);
				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);

//				plot.DoPlot();
//				this.scrolled_plot2.ReplacePlot(plot);
//				this.scrolled_plot2.Plot.StartRefreshing();

//				this.calibration_result_label.Text =
//					"Mean deviation: "   + deviation.YMean().ToString("f3") + " nm,\t" +
//					"RMS of deviation: " + deviation.YRms().ToString("f3")  + " nm";
				
				this.scrolled_plot2.vzb.Sensitive = true;
			}
			else if (!called_from_clear && 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 && 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)
				return;
			
			this.correction_dialog.Sensitive = false;
			this.correction_dialog.GdkWindow.Cursor = new Gdk.Cursor(Gdk.CursorType.Watch);
			UserInterface.ProcessEvents_();
		}
		
		private void SetArrowCursor()
		{
			if (!this.correction_dialog.IsRealized)
				return;
			
			this.correction_dialog.Sensitive = true;
			this.correction_dialog.GdkWindow.Cursor = new Gdk.Cursor(Gdk.CursorType.Arrow);
			UserInterface.ProcessEvents_();
		}

		private void OnHzbChanged(ZoomBar.ZoomBar zb, bool released)
		{
			this.scrolled_plot1.hzb.Pan  = zb.Pan;
			this.scrolled_plot1.hzb.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()
		{
			QSMessageDialog dialog = new QSMessageDialog(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.ProcessEvents_();

			if (choice == ResponseType.Yes)
			{
				WavelengthCorrection sav_wlc = this.device.CurrentConfig.WavelengthCorrection;
				try
				{
					Finder finder = new Finder(this.device, ReferenceWheelVersion.Auto, ExternalWhiteReferenceType.Unknown, "QuickStep");
					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)
				return;
			
			this.SetWatchCursor();
			
			if (!open_ref)
			{
				this.current_extrema  = null;
				this.current_spectrum = null;
				this.correction = null;
				apply_sensitive = this.correction_apply_button.Sensitive;
			}
			
			try
			{
				// dark intensity
				this.device.SetFinderWheel(FinderWheelPosition.White, UserInterface.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 = Device.DarkIntensityType.Record;
				
				// background
				if (open_ref)
					this.device.SetFinderWheel(FinderWheelPosition.Sample, UserInterface.ProcessEvents_);
				else
					this.device.SetFinderWheel(FinderWheelPosition.White, UserInterface.ProcessEvents_);
				Spectrum bg_spectrum = this.device.Single(true);
				if (bg_spectrum == null)
					return;
				bg_spectrum.LimitBandwidth = this.lowpass_checkbutton.Active;
				this.device.CurrentConfig.BackgroundSpectrum     = bg_spectrum;
				this.device.CurrentConfig.BackgroundSpectrumType = Device.BackgroundSpectrumType.Record;
				
				this.ui.UpdatePropertiesSpectrometerControls();
				this.Update();
				
				// plots
				this.scrolled_plot1.Plot.Clear();
				this.scrolled_plot2.Plot.Clear();
				
				this.scrolled_plot1.vzb.Sensitive = false;
				this.scrolled_plot2.vzb.Sensitive = false;
				this.scrolled_plot2.hzb.Sensitive = false;
				
				IPlotEngine plot = this.scrolled_plot1.Plot;
				
				bg_spectrum.Label = Catalog.GetString("Raw background");
				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.vzb.Sensitive = true;
				this.scrolled_plot2.hzb.Sensitive = true;
				
				this.deny_correction_record = open_ref;
				this.UpdateButtons();
			}
			catch (Exception ex)
			{
				this.current_extrema  = null;
				this.current_spectrum = null;
				this.correction = null;
				new InfoMessage(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 QSFileChooserDialog(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)
			{
				new InfoMessage(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.SetFinderWheel(FinderWheelPosition.Reference, UserInterface.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;
				new InfoMessage(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)
			{
				new InfoMessage(this.correction_dialog, 
				                Catalog.GetString("Cannot save reference spectrum: {0}"),
				                ex.Message);
			}
		}
		
		internal void OnLowpassCheckbuttonToggled(object o, EventArgs e)
		{
			this.Update();
		}

		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 OnWindowSizeSpinChanged(object o, System.EventArgs e)
		{
			this.Update();
		}

		internal void OnNoiseLimitSpinChanged(object o, System.EventArgs e)
		{
			this.Update();
		}

		internal void OnCutoffLimitSpinChanged(object o, System.EventArgs e)
		{
			this.Update();
		}

		internal void OnEpsilonSpinChanged(object o, System.EventArgs e)
		{
			this.Update();
		}

		internal void OnReferenceOffsetSpinChanged(object o, System.EventArgs e)
		{
			this.Update();
		}

		internal void OnCurrentOffsetSpinChanged(object o, System.EventArgs e)
		{
			this.Update();
		}

		internal void OnMaxCountSpinChanged(object o, System.EventArgs e)
		{
			this.Update();
		}
		
		internal void OnAlgorithmComboChanged(object o, EventArgs e)
		{
			this.Update();
		}
	}
}
