//
//  SampleAcquisitionDialog.cs
//
// QuickStep: Acquire, view and identify spectra 
// Copyright (C) 2010-2017  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/>.

//  SampleAcquisitionDialog.cs  created with MonoDevelop



using System;
using System.IO;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using System.Reflection;
using Glade;
using Gtk;

using Hiperscan.Common;
using Hiperscan.SGS;
using Hiperscan.FileIO;
using Hiperscan.Spectroscopy;
using Hiperscan.Unix;


namespace Hiperscan.Extensions.Acquisition
{
	
	public enum AcquisitionState
	{
		Init,
		Warmup,
		Ready,
		Acquisition,
		Close,
		Unknown
	}
	
	public enum AcquisitionType
	{
		ExtWhite,
		ExtEmpty,
		Labsphere,
		Specimen,
		ExtEmptyDie,
		None
	}

	public enum SpecialModeType : int
	{
		None                          = 0,
		TransflectanceInsertGold      = 999001,
		WhiteReferenceSampleInsert    = 999002,
		ReferenceWheelZenithGlass     = 999003,
		ReferenceWheelOxideNoGlass    = 999004,
		ReferenceWheelEloxNoGlass     = 999005,
		TransflectanceInsertAlu       = 999006,
		TransflectanceInsertAluWater  = 999007,
		TransflectanceInsertGoldWater = 999008,
		WhiteReferenceZenith          = 9990011,
		BlackReference                = 999009,
		BlackReferenceInsert          = 999010,
		SampleJar                     = 999011
	}

	public class SampleAcquisitionDialog
	{
		[Widget] Gtk.Window sample_acquisition_window = null;
		[Widget] Gtk.Label status_label = null;
		[Widget] Gtk.Label sgs_temperature_label = null;
		[Widget] Gtk.Label sgs_gradient_label = null;
		[Widget] Gtk.Label finder_temperature_label = null;
		[Widget] Gtk.Label finder_gradient_label = null;
		[Widget] Gtk.Label sgs_temperature_info_label = null;
		[Widget] Gtk.Label sgs_gradient_info_label = null;
		[Widget] Gtk.Label remarks_optional_label = null;
		[Widget] Gtk.Label finder_gradient_info_label = null;
		[Widget] Gtk.FileChooserButton filechooser = null;
		[Widget] Gtk.Entry series_entry = null;
		[Widget] Gtk.Entry probe_serial_entry = null;
		[Widget] Gtk.Entry white_reference_serial_entry = null;
		[Widget] Gtk.Entry black_reference_serial_entry = null;
		[Widget] Gtk.Entry remarks_entry = null;
		[Widget] Gtk.SpinButton acquisition_count_spin = null;
		[Widget] Gtk.SpinButton average_count_spin = null;
		[Widget] Gtk.Table acquisition_table = null;
		[Widget] Gtk.CheckButton temperatur_stabilization_checkbutton = null;
		[Widget] Gtk.CheckButton lightsource_stabilization_checkbutton = null;
		[Widget] Gtk.CheckButton plausibility_checkbutton = null;
		[Widget] Gtk.CheckButton random_delay_checkbutton = null;
		[Widget] Gtk.CheckButton fluid_mode_checkbutton = null;
		[Widget] Gtk.CheckButton paranoia_checkbutton = null;
		[Widget] Gtk.HBox series_hbox = null;
		[Widget] Gtk.Table series_table = null;
		[Widget] Gtk.Image sgs_temperature_image = null;
		[Widget] Gtk.Image sgs_gradient_image = null;
		[Widget] Gtk.Image series_image = null;
		[Widget] Gtk.Image t1_image = null;
		[Widget] Gtk.Image g2_image = null;
		[Widget] Gtk.Image s4_image = null;
		[Widget] Gtk.Image logo_image = null;
		[Widget] Gtk.Image remarks_image = null;
		
		[Widget] Gtk.Button t1_button = null;
		[Widget] Gtk.Button g2_button = null;
		[Widget] Gtk.Button t3_button = null;
		[Widget] Gtk.Button s4_button = null;
		[Widget] Gtk.Button acquisition_button = null;
		
		[Widget] Gtk.Label t1_label = null;
		[Widget] Gtk.Label g2_label = null;
		[Widget] Gtk.Label l3_label = null;
		[Widget] Gtk.Label s4_label = null;
		[Widget] Gtk.Label series_label = null;
		[Widget] Gtk.Label example_label = null;
		
		[Widget] Gtk.Alignment empty_die_align = null;

		[Widget] Gtk.Expander preferences_expander = null;
		
		private AcquisitionState state;
		private AcquisitionType  acquisition_type = AcquisitionType.None;
		private Finder finder;
		private double di0 = 0.0;
		private double sigma0 = 0.0;
		private double add_data_di0 = 0.0;
		private Spectrum white_master, dark_master;
		
		private DateTime then = DateTime.Now;
		private List<double> times = new List<double>();
		private List<double> sgs_temperatures = new List<double>();
		private List<double> finder_temperatures = new List<double>();
		
		private DateTime extwhite_timestamp = DateTime.MinValue;
		private DateTime extempty_timestamp = DateTime.MinValue;
		private DateTime transflectance_reference_timestamp = DateTime.MinValue;
		
		private readonly TimeSpan extwhite_timespan = new TimeSpan(0, 20, 0);
		private readonly TimeSpan extempty_timespan = new TimeSpan(0, 20, 0);
		private readonly TimeSpan emptydie_timespan = new TimeSpan(0, 20, 0);
		
		private Spectrum current_transflectance_reference_spectrum = null;
		
		private volatile bool autonomous_mode = false;
		private double autonomous_target_temperature = double.MaxValue;

		private static SpecialModeType special_mode = SpecialModeType.None;
		private static readonly bool euro_otc_mode = Environment.GetEnvironmentVariable(Env.Plugin.EURO_OTC) == "1";

		private static string series_name_regex = @"^[0-9]+_[A-Z]+(_G[0-9])*$";
		private static DateTime last_recalibration_time = DateTime.MinValue;
		private static TimeSpan recalibration_time_limit = new TimeSpan(0, 3, 0);

		private static Spectrum current_white, current_dark, current_oxid;
		private static double current_di, current_sigma;

		public SampleAcquisitionDialog()
		{
			Glade.XML gui = new Glade.XML(null, "Acquisition.Acquisition.glade", "sample_acquisition_window", "Hiperscan");
			gui.Autoconnect(this);

			if (!string.IsNullOrEmpty(Settings.Current.TargetDirectory))
				this.filechooser.SetCurrentFolder(Settings.Current.TargetDirectory);
			else
				this.filechooser.SetCurrentFolder(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments));
			
			if (!string.IsNullOrEmpty(Settings.Current.ProbeSerial))
				this.probe_serial_entry.Text = Settings.Current.ProbeSerial;

			if (!string.IsNullOrEmpty(Settings.Current.WhiteReferenceSerial))
				this.white_reference_serial_entry.Text = Settings.Current.WhiteReferenceSerial;

			if (!string.IsNullOrEmpty(Settings.Current.BlackReferenceSerial))
				this.black_reference_serial_entry.Text = Settings.Current.BlackReferenceSerial;

			this.filechooser.CurrentFolderChanged += delegate(object sender, EventArgs e) {
				Settings.Current.TargetDirectory = this.filechooser.Filename;
				this.extwhite_timestamp = DateTime.MinValue;
				this.extempty_timestamp = DateTime.MinValue;
				this.transflectance_reference_timestamp = DateTime.MinValue;
			};
			
			this.series_entry.Changed += delegate {
				this.UpdateControls();
			};
			
			this.probe_serial_entry.Changed += delegate {
				this.extwhite_timestamp = DateTime.MinValue;
				this.extempty_timestamp = DateTime.MinValue;
				this.transflectance_reference_timestamp = DateTime.MinValue;
				this.UpdateControls();
			};
			
			this.white_reference_serial_entry.Changed += delegate {
				this.extwhite_timestamp = DateTime.MinValue;
				this.extempty_timestamp = DateTime.MinValue;
				this.transflectance_reference_timestamp = DateTime.MinValue;
				this.UpdateControls();
			};
			
			this.black_reference_serial_entry.Changed += delegate {
				this.extwhite_timestamp = DateTime.MinValue;
				this.extempty_timestamp = DateTime.MinValue;
				this.transflectance_reference_timestamp = DateTime.MinValue;
				this.UpdateControls();
			};

			this.average_count_spin.ValueChanged += delegate(object sender, EventArgs e) {
				this.extwhite_timestamp = DateTime.MinValue;
				this.extempty_timestamp = DateTime.MinValue;
				this.transflectance_reference_timestamp = DateTime.MinValue;
			};
			
			this.logo_image.Pixbuf = new Gdk.Pixbuf(Assembly.GetExecutingAssembly(), "Acquisition.logo.png");
			
			this.sample_acquisition_window.DeleteEvent += this.OnDeleteEvent;
			
			this.finder = new Finder(Plugin.host.ActiveDevices.Idle[0], ReferenceWheelVersion.Auto, ExternalWhiteReferenceType.Unknown, Plugin.name);
			this.finder.Device.SetFinderFanSpeed(true);
			this.finder.AutoSaveReferences = false;
			this.finder.NoReferenceResetOnFailedCheck = true;
			this.finder.CorrectionMask = CorrectionMask.All;
			
			try 
			{
				byte version, control_value;
				double target_temperature;
				this.finder.Device.ReadTemperatureControlInfo(out version, out control_value, out target_temperature);
				this.autonomous_mode = true;
				this.autonomous_target_temperature = target_temperature;
				this.sgs_temperature_info_label.Text = string.Format(Catalog.GetString("°C\t(value > {0:f1}°C)"), target_temperature);
				this.finder_gradient_info_label.Text = "K/h";
				this.sgs_temperature_image.Show();
				this.sgs_gradient_image.Show();
			}
			catch (NotImplementedException) 
			{
				this.sgs_temperature_image.Hide();
			}

			if (SampleAcquisitionDialog.euro_otc_mode)
			{
				this.preferences_expander.Sensitive = false;

				this.white_reference_serial_entry.Sensitive = false;
				this.black_reference_serial_entry.Sensitive = false;
				this.probe_serial_entry.Sensitive = false;

				this.example_label.NoShowAll = true;
				this.example_label.Visible = false;
				this.remarks_optional_label.NoShowAll = true;
				this.remarks_optional_label.Visible = false;

				this.average_count_spin.Value = 500.0;
				this.average_count_spin.Sensitive = false;
				this.acquisition_count_spin.Value = 10.0;
				this.acquisition_count_spin.Sensitive = false;

				this.t3_button.Sensitive = false;

				SampleAcquisitionDialog.series_name_regex = @"^[0-9A-Z-]{6,15}(_([0-9]{1,2}|[0-9]{1,2}[OMU]|[OMU])){0,1}$";
			}
			else
			{
				this.average_count_spin.Value = 2000.0;
				this.acquisition_count_spin.Value = 5.0;
			}

			
			this.sample_acquisition_window.ShowAll();

			Plugin.host.ProcessEvents();

			this.state = AcquisitionState.Init;
			Hiperscan.Unix.Timeout.Add(0, delegate {
				try
				{
					this.RunLoop();
				}
				catch (Exception ex)
				{
					Console.WriteLine(ex);
					Plugin.host.ShowInfoMessage(this.sample_acquisition_window,
					                            Catalog.GetString("Error while running Aquisition plugin: {0}"),
					                            ex.Message);
					this.sample_acquisition_window.Destroy();
				}
				return false;
			});
		}
		
		internal void OnAcquisitionButtonClicked(object o, EventArgs e)
		{
			this.acquisition_table.Sensitive = false;
			this.fluid_mode_checkbutton.Sensitive = false;
			this.acquisition_type = AcquisitionType.Specimen;
		}
		
		internal void On1WButtonClicked(object o, EventArgs e)
		{
			this.acquisition_table.Sensitive = false;
			this.fluid_mode_checkbutton.Sensitive = false;
			this.acquisition_type = AcquisitionType.ExtWhite;
		}
		
		internal void On2GButtonClicked(object o, EventArgs e)
		{
			this.acquisition_table.Sensitive = false;
			this.fluid_mode_checkbutton.Sensitive = false;
			this.acquisition_type = AcquisitionType.ExtEmpty;
		}
		
		internal void On3TButtonClicked(object o, EventArgs e)
		{
			this.acquisition_table.Sensitive = false;
			this.fluid_mode_checkbutton.Sensitive = false;
			this.acquisition_type = AcquisitionType.Labsphere;
		}
		
		internal void On4SButtonClicked(object o, EventArgs e)
		{
			this.acquisition_table.Sensitive = false;
			this.fluid_mode_checkbutton.Sensitive = false;
			this.acquisition_type = AcquisitionType.ExtEmptyDie;
		}
		
		private void RunLoop()
		{
			WavelengthCorrection wlc = null;
					
			for (;;)
			{
				if (this.finder.Device.GetState() == Device.State.Unknown)
					this.state = AcquisitionState.Unknown;
				
				this.UpdateControls();
				
				switch (this.state)
				{
				
				case AcquisitionState.Init:
					this.status_label.Markup = Catalog.GetString("<b>Initializing ...</b>");
					this.finder.Device.SetFinderWheel(FinderWheelPosition.Standby, Plugin.host.ProcessEvents);
					this.finder.Device.SetFinderLight(this.StabilizedLightSource, Plugin.host.ProcessEvents);
					this.state = AcquisitionState.Warmup;
					this.then = DateTime.Now;
					times.Clear();
					sgs_temperatures.Clear();
					finder_temperatures.Clear();
					this.series_hbox.Sensitive = true;
					this.series_table.Sensitive = true;
					this.filechooser.Sensitive  = true;
					this.acquisition_table.Sensitive = false;

					if (SampleAcquisitionDialog.euro_otc_mode)
					{
						this.temperatur_stabilization_checkbutton.Active  = false;
						this.lightsource_stabilization_checkbutton.Active = false;
						this.paranoia_checkbutton.Active = false;

						this.average_count_spin.Sensitive = false;
						this.acquisition_count_spin.Sensitive = false;
					}
					else
					{
						this.average_count_spin.Sensitive = true;
						this.acquisition_count_spin.Sensitive = true;
					}

					break;
					
				case AcquisitionState.Warmup:
				case AcquisitionState.Ready:
					this.UpdateTemperatures();
					for (int ix=0; ix < 10; ++ix)
					{
						System.Threading.Thread.Sleep(100);
						Plugin.host.ProcessEvents();
					}
					const int regression_count = 200;
					double sgs_gradient, finder_gradient;
					if (times.Count >= regression_count)
					{
						DataSet ds = new DataSet(times.GetRange(times.Count-regression_count, regression_count), 
						                         sgs_temperatures.GetRange(sgs_temperatures.Count-regression_count, regression_count));
						sgs_gradient = ds.LinearRegression().Slope;
						this.sgs_gradient_label.Text = sgs_gradient.ToString("f2");
						ds = new DataSet(times.GetRange(times.Count-regression_count, regression_count), 
						                 finder_temperatures.GetRange(finder_temperatures.Count-regression_count, regression_count));
						finder_gradient = ds.LinearRegression().Slope;
						this.finder_gradient_label.Text = finder_gradient.ToString("f2");
					}
					else
					{
						sgs_gradient = double.MaxValue;
						finder_gradient = double.MaxValue;
					}
					
					if (this.times.Count > 1000)
					{
						this.times               = new List<double>(times.GetRange(times.Count-regression_count, regression_count));
						this.sgs_temperatures    = new List<double>(sgs_temperatures.GetRange(sgs_temperatures.Count-regression_count, regression_count));
						this.finder_temperatures = new List<double>(finder_temperatures.GetRange(finder_temperatures.Count-regression_count, regression_count));
					}
					
					if (this.state == AcquisitionState.Close)
						break;
					
					if (this.autonomous_mode)
					{
						if ((this.state == AcquisitionState.Warmup && Math.Abs(sgs_gradient) < 1.5 && sgs_temperatures[sgs_temperatures.Count-1] > this.autonomous_target_temperature) ||
						    (this.state == AcquisitionState.Ready  && Math.Abs(sgs_gradient) < 2.0 && sgs_temperatures[sgs_temperatures.Count-1] > this.autonomous_target_temperature) ||
						    !this.temperatur_stabilization_checkbutton.Active)
						{
							if (this.acquisition_type != AcquisitionType.None)
							{
								this.state = AcquisitionState.Acquisition;
							}
							else if (this.state != AcquisitionState.Unknown)
							{
								this.state = AcquisitionState.Ready;
								this.status_label.Markup = Catalog.GetString("<b>Ready for measurement.</b>");
								this.series_hbox.Sensitive = true;
								this.series_table.Sensitive = true;
								this.filechooser.Sensitive = true;
								this.acquisition_table.Sensitive = true;
								this.fluid_mode_checkbutton.Sensitive = true;

								if (SampleAcquisitionDialog.euro_otc_mode)
								{
									this.acquisition_count_spin.Sensitive = false;
									this.average_count_spin.Sensitive = false;
								}
								else
								{
									this.acquisition_count_spin.Sensitive = true;
									this.average_count_spin.Sensitive = true;
								}
								this.sgs_gradient_image.Hide();
								this.sgs_temperature_image.Hide();
							}
						}
						else if (this.state != AcquisitionState.Unknown)
						{
							this.state = AcquisitionState.Warmup;
							this.status_label.Markup = Catalog.GetString("<b>Warming up ...</b>");
							this.series_hbox.Sensitive = true;
							this.series_table.Sensitive = true;
							this.filechooser.Sensitive = true;
							this.acquisition_table.Sensitive = false;
							this.fluid_mode_checkbutton.Sensitive = true;

							this.extwhite_timestamp = DateTime.MinValue;
							this.extempty_timestamp = DateTime.MinValue;
							this.transflectance_reference_timestamp = DateTime.MinValue;

							if ((this.state == AcquisitionState.Warmup && Math.Abs(sgs_gradient) < 1.5) ||
							    (this.state == AcquisitionState.Ready  && Math.Abs(sgs_gradient) < 2.0))
								this.sgs_gradient_image.Hide();
							else
								this.sgs_gradient_image.Show();
							
							if (sgs_temperatures.Count > 0 && sgs_temperatures[sgs_temperatures.Count-1] > this.autonomous_target_temperature)
								this.sgs_temperature_image.Hide();
							else
								this.sgs_temperature_image.Show();
						}
					}
					else
					{
						if ((this.state == AcquisitionState.Warmup && Math.Abs(sgs_gradient) < 1.5 && Math.Abs(finder_gradient) < 1.5) ||
						    (this.state == AcquisitionState.Ready  && Math.Abs(sgs_gradient) < 2.0 && Math.Abs(finder_gradient) < 2.0) ||
						    !this.temperatur_stabilization_checkbutton.Active)
						{
							if (this.acquisition_type != AcquisitionType.None)
							{
								this.state = AcquisitionState.Acquisition;
							}
							else
							{
								this.state = AcquisitionState.Ready;
								this.status_label.Markup = Catalog.GetString("<b>Ready for measurement.</b>");
								this.series_hbox.Sensitive = true;
								this.series_table.Sensitive = true;
								this.filechooser.Sensitive = true;
								this.acquisition_table.Sensitive = true;
								this.fluid_mode_checkbutton.Sensitive = true;
								if (SampleAcquisitionDialog.euro_otc_mode)
								{
									this.acquisition_count_spin.Sensitive = false;
									this.average_count_spin.Sensitive = false;
								}
								else
								{
									this.acquisition_count_spin.Sensitive = true;
									this.average_count_spin.Sensitive = true;
								}
								this.sgs_gradient_image.Hide();
							}
						}
						else
						{
							this.state = AcquisitionState.Warmup;
							this.status_label.Markup = Catalog.GetString("<b>Warming up ...</b>");
							this.series_hbox.Sensitive = true;
							this.series_table.Sensitive = true;
							this.filechooser.Sensitive = true;
							this.acquisition_table.Sensitive = false;
							this.fluid_mode_checkbutton.Sensitive = true;
							this.extwhite_timestamp = DateTime.MinValue;
							this.extempty_timestamp = DateTime.MinValue;
							this.transflectance_reference_timestamp = DateTime.MinValue;
						}
					}
					break;
					
				case AcquisitionState.Acquisition:
					this.series_hbox.Sensitive = false;
					this.series_table.Sensitive = false;
					this.filechooser.Sensitive  = false;
					this.average_count_spin.Sensitive = false;
					this.acquisition_count_spin.Sensitive = false;
					this.acquisition_table.Sensitive = false;
					this.fluid_mode_checkbutton.Sensitive = false;
					
					ushort average = (ushort)this.average_count_spin.Value;
					if (average != this.finder.Device.CurrentConfig.Average)
					{
						di0 = 0.0;
						sigma0 = 0.0;
						this.add_data_di0 = 0.0;
						this.white_master = null;
						this.dark_master = null;
						
						this.finder.Device.CurrentConfig.Average = average;
						this.finder.Device.WriteConfig();
					}
					
					if (di0 == 0.0 || sigma0 == 0.0 || this.add_data_di0 == 0.0 || this.white_master == null || this.dark_master == null)
					{
						this.status_label.Markup = Catalog.GetString("<b>Acquire dark intensity ...</b>");
						this.finder.Device.SetFinderWheel(FinderWheelPosition.White, Plugin.host.ProcessEvents);
						this.white_master = this.finder.Device.Single(!this.StabilizedLightSource, Plugin.host.ProcessEvents);
						this.white_master.ProbeType = this.finder.ProbeType;

						if (this.StabilizedLightSource)
							this.finder.Device.SetFinderLight(false, null);
						System.Threading.Thread.Sleep(2000);
						this.dark_master = this.finder.Device.Single(false, Plugin.host.ProcessEvents);
						this.dark_master.ProbeType = this.finder.ProbeType;
						if (this.StabilizedLightSource)
							this.finder.Device.SetFinderLight(true, null);
						this.finder.Device.SetFinderWheel(FinderWheelPosition.Standby, Plugin.host.ProcessEvents);

						if (this.lightsource_stabilization_checkbutton.Active)
						{
							List<int> indices = this.dark_master.RawIntensity.Find('x', '<', 1450.0);
							this.dark_master.RawIntensity = this.dark_master.RawIntensity[indices];
						}

						this.dark_master.SpectrumType = SpectrumType.InternalDark;
						this.SaveReferenceSpectrum(this.dark_master, "intdark");
						this.di0 = this.dark_master.RawIntensity.YMean();
						this.sigma0 = this.dark_master.RawIntensity.YSigma();
						this.add_data_di0 = (double)(this.white_master.AddData[2]) * 1e-4;

						this.white_master.DarkIntensity = new DarkIntensity(this.di0, this.sigma0);
						this.white_master.SpectrumType = SpectrumType.InternalWhite;
						this.SaveReferenceSpectrum(this.white_master, "intwhite");
						
						DateTime then2 = DateTime.Now;
						
						const double wait_minutes = 5.0;
						while (this.StabilizedLightSource && (DateTime.Now - then2).TotalMinutes < wait_minutes)
						{
							this.status_label.Markup = string.Format(Catalog.GetString("<b>Waiting for light source ({0}s) ...</b>"),
							                                       (wait_minutes*60 - (DateTime.Now - then2).TotalSeconds).ToString("f0"));
							if (this.state == AcquisitionState.Close)
								break;
							this.UpdateTemperatures();
							for (int ix=0; ix < 5; ++ix)
							{
								System.Threading.Thread.Sleep(200);
								Plugin.host.ProcessEvents();
							}
						}
					}

					if (this.state == AcquisitionState.Close)
						break;
					
					if (this.acquisition_type != AcquisitionType.Specimen)
					{
						if (this.paranoia_checkbutton.Active || DateTime.Now - SampleAcquisitionDialog.last_recalibration_time > SampleAcquisitionDialog.recalibration_time_limit)
						{
							this.status_label.Markup = Catalog.GetString("<b>Acquire internal wavelength standard ...</b>");
							this.finder.Device.SetFinderWheel(FinderWheelPosition.Reference, Plugin.host.ProcessEvents);
							Spectrum oxid = this.finder.Device.Single(!this.StabilizedLightSource, Plugin.host.ProcessEvents);
							oxid.ProbeType = this.finder.ProbeType;

							this.status_label.Markup = Catalog.GetString("<b>Acquire internal white reference ...</b>");
							this.finder.Device.SetFinderWheel(FinderWheelPosition.White, Plugin.host.ProcessEvents);
							Spectrum white = this.finder.Device.Single(!this.StabilizedLightSource, Plugin.host.ProcessEvents);
							white.ProbeType = this.finder.ProbeType;

							double di, sigma;
							Spectrum dark;
							if (this.lightsource_stabilization_checkbutton.Active)
							{
								di = ((double)(white.AddData[2]) * 1e-4) - this.add_data_di0 + this.di0;
								sigma = this.sigma0;
								dark = this.dark_master.Clone();
								dark.Id = "1_" + DateTime.Now.ToString("yyyy-MM-dd_HH-mm-ss_") + this.finder.Device.Info.SerialNumber;
								dark.RawIntensity *= 0.0;
								dark.RawIntensity += di;
								dark.Timestamp = DateTime.Now;
							}
							else
							{
								this.status_label.Markup = Catalog.GetString("<b>Acquire internal dark spectum ...</b>");
								this.finder.Device.SetFinderWheel(FinderWheelPosition.White, Plugin.host.ProcessEvents);
								dark = this.finder.Device.Single(false, Plugin.host.ProcessEvents);
								dark.ProbeType = this.finder.ProbeType;
								di = dark.RawIntensity.YMean();
								sigma = dark.RawIntensity.YSigma();
							}
							dark.SpectrumType = SpectrumType.InternalDark;
							this.SaveReferenceSpectrum(dark, "intdark");

							white.DarkIntensity = new DarkIntensity(di, sigma);
							white.SpectrumType = SpectrumType.InternalWhite;
							this.SaveReferenceSpectrum(white, "intwhite");

							oxid.Background = white.Clone();
							oxid.DarkIntensity = new DarkIntensity(di, sigma);
							oxid.SpectrumType = SpectrumType.InternalOxid;

							try
							{
								if (Finder.HasZenithRecalibrationReference(this.finder))
									wlc = new WavelengthCorrection(Finder.GetWavelengthReference(this.finder.ReferenceWheelVersion), oxid, Finder.ZenithRecalibrationWavelengths, false);
								else
									wlc = new WavelengthCorrection(Finder.GetWavelengthReference(this.finder.ReferenceWheelVersion), oxid, null, true);
							}
							catch (LevenbergMarquardt.MaxIterationNumberException)
							{
								this.ShowInfoMessage(Catalog.GetString("Invalid internal wavelength standard measurement. Try again."));
								break;
							}

							this.SaveReferenceSpectrum(oxid, "intoxid");

							white.WavelengthCorrection = wlc;
							dark.WavelengthCorrection  = wlc;
							oxid.WavelengthCorrection  = wlc;
							oxid.Background = white;
							oxid.DarkIntensity = new DarkIntensity(dark);
							this.finder.__SetSpectra(null, white, dark, oxid, null, null, null, oxid, white, dark);

							this.finder.Device.CurrentConfig.DarkIntensity = new DarkIntensity(di, this.sigma0);
							this.finder.Device.CurrentConfig.BackgroundSpectrum = white;

							SampleAcquisitionDialog.current_dark  = dark;
							SampleAcquisitionDialog.current_di    = di;
							SampleAcquisitionDialog.current_oxid  = oxid;
							SampleAcquisitionDialog.current_sigma = sigma;
							SampleAcquisitionDialog.current_white = white;

							SampleAcquisitionDialog.last_recalibration_time = DateTime.Now;
						}

						this.status_label.Markup = Catalog.GetString("<b>Acquire external spectrum ...</b>");
						this.finder.Device.SetFinderWheel(FinderWheelPosition.Sample, Plugin.host.ProcessEvents);
					}

					switch (this.acquisition_type)
					{

					case AcquisitionType.ExtWhite:
						ushort sav_average = this.finder.Device.CurrentConfig.Average;
						Spectrum extwhite = null;
						try
						{
							ushort new_average = (ushort)(this.average_count_spin.Value * 4);
							if (new_average > this.finder.Device.HardwareProperties.MaxAverage)
								new_average = (ushort)this.finder.Device.HardwareProperties.MaxAverage;

							if (new_average != sav_average)
							{
								this.finder.Device.CurrentConfig.Average = new_average;
								this.finder.Device.WriteConfig();
							}

							extwhite = this.finder.Device.Single(!this.StabilizedLightSource, Plugin.host.ProcessEvents);
							extwhite.ProbeType = this.finder.ProbeType;
						}
						catch (Exception ex)
						{
							this.ShowInfoMessage(ex.Message);
							this.extwhite_timestamp = DateTime.MinValue;
						}
						finally
						{
							if (this.finder.Device.CurrentConfig.Average != sav_average)
							{
								this.finder.Device.CurrentConfig.Average = sav_average;
								this.finder.Device.WriteConfig();
							}
						}
						extwhite.DarkIntensity = new DarkIntensity(this.finder.CurrentInternalDarkSpectrum);
						try
						{
							if (this.plausibility_checkbutton.Active)
								this.finder.CheckWhiteReference(extwhite);
						}
						catch (Finder.WhiteReferenceException ex)
						{
							this.ShowInfoMessage(ex.Message);
							this.extwhite_timestamp = DateTime.MinValue;
							break;
						}
						extwhite.SpectrumType = SpectrumType.ExternalWhite;
						extwhite.WavelengthCorrection = wlc;
						this.finder.__SetSpectra(null, null, null, null, extwhite, null, null, null, null, null);
						this.SaveReferenceSpectrum(extwhite, "zenith");
						this.extwhite_timestamp = DateTime.Now;
						break;
						
					case AcquisitionType.ExtEmpty:
						sav_average = this.finder.Device.CurrentConfig.Average;
						Spectrum extblack = null;
						try
						{
							ushort new_average = (ushort)(this.average_count_spin.Value * 4);
							if (new_average > this.finder.Device.HardwareProperties.MaxAverage)
								new_average = (ushort)this.finder.Device.HardwareProperties.MaxAverage;

							if (new_average != sav_average)
							{
								this.finder.Device.CurrentConfig.Average = new_average;
								this.finder.Device.WriteConfig();
							}

							extblack = this.finder.Device.Single(!this.StabilizedLightSource, Plugin.host.ProcessEvents);
							extblack.ProbeType = this.finder.ProbeType;
						}
						catch (Exception ex)
						{
							this.ShowInfoMessage(ex.Message);
							this.extempty_timestamp = DateTime.MinValue;
						}
						finally
						{
							if (this.finder.Device.CurrentConfig.Average != sav_average)
							{
								this.finder.Device.CurrentConfig.Average = sav_average;
								this.finder.Device.WriteConfig();
							}
						}
						extblack.DarkIntensity = new DarkIntensity(this.finder.CurrentInternalDarkSpectrum);
						try
						{
							if (this.plausibility_checkbutton.Active)
                                this.finder.CheckBlackReference(extblack);
						}
						catch (Finder.EmptyReferenceException ex)
						{
							this.ShowInfoMessage(ex.Message);
							this.extempty_timestamp = DateTime.MinValue;
							break;
						}
						extblack.SpectrumType = SpectrumType.ExternalEmpty;
						extblack.WavelengthCorrection = wlc;
						this.finder.__SetSpectra(null, null, null, null, null, null, extblack, null, null, null);
						this.SaveReferenceSpectrum(extblack, "extempty");
						this.extempty_timestamp = DateTime.Now;
						break;
						
					case AcquisitionType.Labsphere:
						sav_average = this.finder.Device.CurrentConfig.Average;
						Spectrum labsphere = null;
						try
						{
							ushort new_average = (ushort)(this.average_count_spin.Value * 4);
							if (new_average > this.finder.Device.HardwareProperties.MaxAverage)
								new_average = (ushort)this.finder.Device.HardwareProperties.MaxAverage;

							if (new_average != sav_average)
							{
								this.finder.Device.CurrentConfig.Average = new_average;
								this.finder.Device.WriteConfig();
							}

							labsphere = this.finder.Device.Single(!this.StabilizedLightSource, Plugin.host.ProcessEvents);
							labsphere.ProbeType = this.finder.ProbeType;
						}
						catch (Exception ex)
						{
							this.ShowInfoMessage(ex.Message);
						}
						finally
						{
							if (this.finder.Device.CurrentConfig.Average != sav_average)
							{
								this.finder.Device.CurrentConfig.Average = sav_average;
								this.finder.Device.WriteConfig();
							}
						}
						labsphere.DarkIntensity = new DarkIntensity(this.finder.CurrentInternalDarkSpectrum);
						try
						{
							if (this.plausibility_checkbutton.Active)
								this.finder.CheckWhiteReference(labsphere);
						}
						catch (Finder.WhiteReferenceException ex)
						{
							this.ShowInfoMessage(ex.Message);
							break;
						}
						labsphere.SpectrumType = SpectrumType.ExternalWhite;
						this.SaveReferenceSpectrum(labsphere, "extwhite");
						break;
						
					case AcquisitionType.ExtEmptyDie:
						sav_average = this.finder.Device.CurrentConfig.Average;
						try
						{
							ushort new_average = (ushort)(this.average_count_spin.Value * 4);
							if (new_average > this.finder.Device.HardwareProperties.MaxAverage)
								new_average = (ushort)this.finder.Device.HardwareProperties.MaxAverage;

							if (new_average != sav_average)
							{
								this.finder.Device.CurrentConfig.Average = new_average;
								this.finder.Device.WriteConfig();
							}

							this.current_transflectance_reference_spectrum = this.finder.Device.Single(!this.StabilizedLightSource, Plugin.host.ProcessEvents);
							this.current_transflectance_reference_spectrum.ProbeType = this.finder.ProbeType;
						}
						catch (Exception ex)
						{
							this.ShowInfoMessage(ex.Message);
							this.transflectance_reference_timestamp = DateTime.MinValue;
						}
						finally
						{
							if (this.finder.Device.CurrentConfig.Average != sav_average)
							{
								this.finder.Device.CurrentConfig.Average = sav_average;
								this.finder.Device.WriteConfig();
							}
						}
                        this.current_transflectance_reference_spectrum.DarkIntensity = new DarkIntensity(this.finder.CurrentInternalDarkSpectrum);
						try
						{
                            if (this.plausibility_checkbutton.Active && this.finder.CurrentExternalWhiteSpectrum != null)
                                this.finder.CheckTransflectanceInsert(this.current_transflectance_reference_spectrum,
										this.finder.CurrentExternalWhiteSpectrum,
										this.finder.Device.CurrentConfig.DarkIntensity);
						}
						catch (Finder.InvalidTransflectReferenceException ex)
						{
							this.ShowInfoMessage(ex.Message);
							this.current_transflectance_reference_spectrum = null;
                            this.transflectance_reference_timestamp = DateTime.MinValue;
							break;
						}
						this.current_transflectance_reference_spectrum.SpectrumType = SpectrumType.ExternalFluidWhite;
						this.current_transflectance_reference_spectrum.WavelengthCorrection = wlc;
						this.finder.__SetSpectra(null, null, null, null, null, this.current_transflectance_reference_spectrum, null, null, null, null);
						this.SaveReferenceSpectrum(this.current_transflectance_reference_spectrum, "emptydie");
						this.transflectance_reference_timestamp = DateTime.Now;
						break;

					case AcquisitionType.Specimen:
						Plugin.host.ClearPlot();
						if (!string.IsNullOrEmpty(this.remarks_entry.Text))
						    this.WriteInfo();
						for (int ix=0; ix < this.acquisition_count_spin.Value; ++ix)
						{
							if (this.state == AcquisitionState.Close)
								break;

							this.UpdateTemperatures();

							Spectrum oxid, white, dark;
							double di, sigma;

							if (this.paranoia_checkbutton.Active || DateTime.Now - SampleAcquisitionDialog.last_recalibration_time > SampleAcquisitionDialog.recalibration_time_limit)
							{
								this.status_label.Markup = string.Format(Catalog.GetString("<b>Acquire internal wavelength standard ({0}/{1}) ...</b>"),
								                                         ix+1, this.acquisition_count_spin.Value);
								this.finder.Device.SetFinderWheel(FinderWheelPosition.Reference, Plugin.host.ProcessEvents);
								oxid = this.finder.Device.Single(!this.StabilizedLightSource, Plugin.host.ProcessEvents);
								oxid.ProbeType = this.finder.ProbeType;

								this.status_label.Markup = string.Format(Catalog.GetString("<b>Acquire internal white reference ({0}/{1}) ...</b>"),
								                                         ix+1, this.acquisition_count_spin.Value);
								this.finder.Device.SetFinderWheel(FinderWheelPosition.White, Plugin.host.ProcessEvents);
								white = this.finder.Device.Single(!this.StabilizedLightSource, Plugin.host.ProcessEvents);
								white.ProbeType = this.finder.ProbeType;

								if (this.lightsource_stabilization_checkbutton.Active)
								{
									di = ((double)(white.AddData[2]) * 1e-4) - this.add_data_di0 + this.di0;
									sigma = this.sigma0;
									dark = this.dark_master.Clone();
									dark.Id = "1_" + DateTime.Now.ToString("yyyy-MM-dd_HH-mm-ss_") + this.finder.Device.Info.SerialNumber;
									dark.RawIntensity *= 0.0;
									dark.RawIntensity += di;
									dark.Timestamp = DateTime.Now;
								}
								else
								{
									this.status_label.Markup = Catalog.GetString("<b>Acquire internal dark reference ...</b>");
									this.finder.Device.SetFinderWheel(FinderWheelPosition.White, Plugin.host.ProcessEvents);
									dark = this.finder.Device.Single(false, Plugin.host.ProcessEvents);
									dark.ProbeType = this.finder.ProbeType;
									di = dark.RawIntensity.YMean();
									sigma = dark.RawIntensity.YSigma();
								}
								dark.SpectrumType = SpectrumType.InternalDark;
								this.SaveReferenceSpectrum(dark, "intdark");

								white.DarkIntensity = new DarkIntensity(di, sigma);
								white.SpectrumType = SpectrumType.InternalWhite;
								this.SaveReferenceSpectrum(white, "intwhite");

								this.finder.Device.CurrentConfig.DarkIntensity = new DarkIntensity(di, sigma);

								if (this.fluid_mode_checkbutton.Active)
									this.finder.Device.CurrentConfig.BackgroundSpectrum = this.current_transflectance_reference_spectrum.Clone();
								else
									this.finder.Device.CurrentConfig.BackgroundSpectrum = white;

								SampleAcquisitionDialog.current_dark  = dark;
								SampleAcquisitionDialog.current_white = white;
								SampleAcquisitionDialog.current_oxid  = oxid;
								SampleAcquisitionDialog.current_di    = di;
								SampleAcquisitionDialog.current_sigma = sigma;

								SampleAcquisitionDialog.last_recalibration_time = DateTime.Now;
							}
							else
							{
								dark  = SampleAcquisitionDialog.current_dark;
								white = SampleAcquisitionDialog.current_white;
								oxid  = SampleAcquisitionDialog.current_oxid;
								di    = SampleAcquisitionDialog.current_di;
								sigma = SampleAcquisitionDialog.current_sigma;
							}

							if (this.random_delay_checkbutton.Active)
							{
								Random random = new Random(DateTime.Now.GetHashCode());
								double delay_mseconds = random.NextDouble() * 20000.0;
								this.status_label.Markup = string.Format(Catalog.GetString("<b>Random delay: {2:f1}s ({0}/{1}) ...</b>"),
								                                         ix+1, this.acquisition_count_spin.Value, delay_mseconds/1000.0);
								TimeSpan span = new TimeSpan(0, 0, 0, 0, (int)delay_mseconds);
								DateTime then = DateTime.Now;
								while (DateTime.Now - then < span)
								{
									Plugin.host.ProcessEvents();
									System.Threading.Thread.Sleep(100);
								}
							}

							this.status_label.Markup = string.Format(Catalog.GetString("<b>Acquire external spectrum ({0}/{1}) ...</b>"),
							                                       ix+1, this.acquisition_count_spin.Value);
							this.finder.Device.SetFinderWheel(FinderWheelPosition.Sample, Plugin.host.ProcessEvents);							
							Spectrum specimen = this.finder.Device.Single(!this.StabilizedLightSource, Plugin.host.ProcessEvents);
							specimen.ProbeType = this.finder.ProbeType;
							this.finder.Device.SetAbsorbance(specimen);
							string fname = this.series_entry.Text;
							if (!string.IsNullOrEmpty(fname))
								fname += "_";
							this.AddComment(specimen);
							specimen.SpectrumType = this.fluid_mode_checkbutton.Active ? SpectrumType.FluidSpecimen : SpectrumType.SolidSpecimen;

//							this.device.SetAbsorbance(oxid);
							oxid.Background = white.Clone();
							oxid.DarkIntensity = new DarkIntensity(di, sigma);
							oxid.SpectrumType = SpectrumType.InternalOxid;

							try
							{
								if (Finder.HasZenithRecalibrationReference(this.finder))
									wlc = new WavelengthCorrection(Finder.GetWavelengthReference(this.finder.ReferenceWheelVersion), oxid, Finder.ZenithRecalibrationWavelengths, false);
								else
									wlc = new WavelengthCorrection(Finder.GetWavelengthReference(this.finder.ReferenceWheelVersion), oxid, null, true);
							}
							catch (LevenbergMarquardt.MaxIterationNumberException)
							{
								--ix;
								this.ShowInfoMessage(Catalog.GetString("Invalid internal wavelength standard measurement. Try again."));
								continue;
							}

							if (SampleAcquisitionDialog.special_mode != SpecialModeType.None)
							{
								string[] s = this.series_entry.Text.Split('-');
								int number = int.Parse(s[1]);
								number = (number + ix) % 1000;
								fname = ((int)SampleAcquisitionDialog.special_mode).ToString() + "_" + s[0] + "-" + number.ToString("d3");
								specimen.Label = fname;
								CSV.Write(specimen, Path.Combine(this.filechooser.Filename, fname + "_specimen"));
							}
							else if (SampleAcquisitionDialog.euro_otc_mode)
							{
								specimen.Label = fname + "Euro-OTC_" + (ix+1).ToString("d4");
								CSV.Write(specimen, Path.Combine(this.filechooser.Filename, fname + "Euro-OTC_" + (ix+1).ToString("d4")));
							}
							else
							{
								specimen.Label = fname + (ix+1).ToString("d4");
								CSV.Write(specimen, Path.Combine(this.filechooser.Filename, fname + (ix+1).ToString("d4")));
							}

							this.SaveReferenceSpectrum(oxid, "intoxid");

							white.WavelengthCorrection    = wlc;
							dark.WavelengthCorrection     = wlc;
							oxid.WavelengthCorrection     = wlc;
							specimen.WavelengthCorrection = wlc;
							this.finder.__SetSpectra(specimen, white, dark, oxid, null, null, null, null, null, null);
							
							Console.WriteLine(this.finder.ToString());
							Spectrum processed = this.finder.Process(this.fluid_mode_checkbutton.Active);
							processed.SpectrumType = this.fluid_mode_checkbutton.Active ? SpectrumType.ProcessedFluidSpecimen : SpectrumType.ProcessedSolidSpecimen;

							if (Environment.GetEnvironmentVariable(Env.Plugin.ACQUISITION_DISABLE_PLOT) != "1")
								Plugin.host.AddToCurrentPlot(processed);

							if (SampleAcquisitionDialog.special_mode != SpecialModeType.None)
							{
								if (Plugin.host.GetCurrentPlotType() != "Reflectance" && Plugin.host.GetCurrentPlotType() != Catalog.GetString("Reflectance"))
									Plugin.host.TrySetCurrentPlotType("Reflectance");
								this.SaveProcessedSpectrum(processed, fname);
							}
							else
							{
								if (Plugin.host.GetCurrentPlotType() != "Absorbance" && Plugin.host.GetCurrentPlotType() != Catalog.GetString("Absorbance"))
									Plugin.host.TrySetCurrentPlotType("Absorbance");
								this.SaveProcessedSpectrum(processed, fname + (ix+1).ToString("d4"));
							}
						}
						if (this.paranoia_checkbutton.Active)
						{
							this.extwhite_timestamp = DateTime.MinValue;
							this.extempty_timestamp = DateTime.MinValue;
							this.transflectance_reference_timestamp = DateTime.MinValue;
						}
						break;
						
					}
					if (this.state == AcquisitionState.Close)
						break;
					
					this.finder.Device.SetFinderWheel(FinderWheelPosition.Standby, Plugin.host.ProcessEvents);
					
					this.acquisition_type = AcquisitionType.None;
					this.state = AcquisitionState.Ready;
					break;
					
				case AcquisitionState.Close:
					this.finder.Device.SetFinderLight(false, Plugin.host.ProcessEvents);
					this.sample_acquisition_window.Destroy();
					return;
					
				case AcquisitionState.Unknown:
					break;
					
				}
				
				Plugin.host.ProcessEvents();
				System.Threading.Thread.Sleep(200);
			}
		}
		
		private void WriteInfo()
		{
			using (StreamWriter writer = new StreamWriter(Path.Combine(this.filechooser.Filename, "series_info.txt"), true))
			{
				writer.WriteLine("{0}{3}{1}{3}{2}", this.series_entry.Text, DateTime.Now.ToString(), this.remarks_entry.Text,
				                 System.Globalization.CultureInfo.CurrentCulture.TextInfo.ListSeparator);
			}
		}
		
		private void AddComment(Spectrum spectrum)
		{
			if (!string.IsNullOrEmpty(this.white_reference_serial_entry.Text))
				spectrum.Comment += "white reference serial: " + this.white_reference_serial_entry.Text + "\n";
			
			if (!string.IsNullOrEmpty(this.black_reference_serial_entry.Text))
				spectrum.Comment += "black reference serial: " + this.black_reference_serial_entry.Text + "\n";
			
			if (!string.IsNullOrEmpty(this.probe_serial_entry.Text))
				spectrum.Comment += "probe serial: " + this.probe_serial_entry.Text + "\n";

			if (!string.IsNullOrEmpty(this.remarks_entry.Text))
				spectrum.Comment += "remarks: " + this.remarks_entry.Text + "\n";
		}
		
		private void UpdateTemperatures()
		{
			double sgs_temperature = this.finder.Device.ReadStatus();
			double finder_temperature = this.finder.Device.ReadFinderTemperature();
			this.sgs_temperature_label.Text = sgs_temperature.ToString("f1");
			this.finder_temperature_label.Text = finder_temperature.ToString("f1");
			TimeSpan span = DateTime.Now - this.then;
			this.times.Add(span.TotalHours);
			this.sgs_temperatures.Add(sgs_temperature);
			this.finder_temperatures.Add(finder_temperature);
		}
		
		private void UpdateControls()
		{
			if (this.state == AcquisitionState.Unknown)
			{
				this.sample_acquisition_window.Sensitive = false;
				return;
			}
			
			bool enable_sample = true;
			
			TimeSpan span = DateTime.Now - this.extwhite_timestamp;
			if (span < this.extwhite_timespan)
			{
				this.t1_image.Visible = false;
				this.t1_label.Text = (this.extwhite_timespan - span).TotalMinutes.ToString("f0") + "min";
			}
			else
			{
				this.t1_image.Visible = true;
				this.t1_label.Text = Catalog.GetString("expired");
				enable_sample = false;
			}
			
			span = DateTime.Now - this.extempty_timestamp;
			if (span < this.extempty_timespan)
			{
				this.g2_image.Visible = false;
				this.g2_label.Text = (this.extempty_timespan - span).TotalMinutes.ToString("f0") + "min";
			}
			else
			{
				this.g2_image.Visible = true;
				this.g2_label.Text = Catalog.GetString("expired");
				enable_sample = false;
			}

			if (this.fluid_mode_checkbutton.Active)
			{
				this.s4_button.Sensitive = true;
				this.s4_label.Sensitive  = true;
				this.empty_die_align.Visible = true;
				
				span = DateTime.Now - this.transflectance_reference_timestamp;
				if (span < this.emptydie_timespan)
				{
					this.s4_image.Visible = false;
					this.s4_label.Text = (this.emptydie_timespan - span).TotalMinutes.ToString("f0") + "min";
				}
				else
				{
					this.s4_image.Visible = true;
					this.s4_label.Text = Catalog.GetString("expired");
					enable_sample = false;
				}
			}
			else
			{
				this.s4_button.Sensitive = false;
				this.s4_label.Sensitive  = false;
				this.empty_die_align.Visible = false;
			}

			if (Regex.IsMatch(this.series_entry.Text, "^[0-9]{4}W-[0-9]{3}") && !this.fluid_mode_checkbutton.Active)
				SampleAcquisitionDialog.special_mode = SpecialModeType.WhiteReferenceZenith;
			else if (Regex.IsMatch(this.series_entry.Text, "^[0-9]{4}G-[0-9]{3}") && !this.fluid_mode_checkbutton.Active)
				SampleAcquisitionDialog.special_mode = SpecialModeType.TransflectanceInsertGold;
			else if (Regex.IsMatch(this.series_entry.Text, "^[0-9]{4}P-[0-9]{3}") && !this.fluid_mode_checkbutton.Active)
				SampleAcquisitionDialog.special_mode = SpecialModeType.WhiteReferenceSampleInsert;
			else if (Regex.IsMatch(this.series_entry.Text, "^[0-9]{4}RZ-[0-9]{3}") && !this.fluid_mode_checkbutton.Active)
				SampleAcquisitionDialog.special_mode = SpecialModeType.ReferenceWheelZenithGlass;
			else if (Regex.IsMatch(this.series_entry.Text, "^[0-9]{4}RO-[0-9]{3}") && !this.fluid_mode_checkbutton.Active)
				SampleAcquisitionDialog.special_mode = SpecialModeType.ReferenceWheelOxideNoGlass;
			else if (Regex.IsMatch(this.series_entry.Text, "^[0-9]{4}RE-[0-9]{3}") && !this.fluid_mode_checkbutton.Active)
				SampleAcquisitionDialog.special_mode = SpecialModeType.ReferenceWheelEloxNoGlass;
			else if (Regex.IsMatch(this.series_entry.Text, "^[0-9]{4}A-[0-9]{3}") && !this.fluid_mode_checkbutton.Active)
				SampleAcquisitionDialog.special_mode = SpecialModeType.TransflectanceInsertAlu;
			else if (Regex.IsMatch(this.series_entry.Text, "^[0-9]{4}AW-[0-9]{3}") && !this.fluid_mode_checkbutton.Active)
				SampleAcquisitionDialog.special_mode = SpecialModeType.TransflectanceInsertAluWater;
			else if (Regex.IsMatch(this.series_entry.Text, "^[0-9]{4}GW-[0-9]{3}") && !this.fluid_mode_checkbutton.Active)
				SampleAcquisitionDialog.special_mode = SpecialModeType.TransflectanceInsertGoldWater;
			else if (Regex.IsMatch(this.series_entry.Text, "^[0-9]{4}B-[0-9]{3}") && !this.fluid_mode_checkbutton.Active)
				SampleAcquisitionDialog.special_mode = SpecialModeType.BlackReference;
			else if (Regex.IsMatch(this.series_entry.Text, "^[0-9]{4}BI-[0-9]{3}") && !this.fluid_mode_checkbutton.Active)
				SampleAcquisitionDialog.special_mode = SpecialModeType.BlackReferenceInsert;
			else if (Regex.IsMatch(this.series_entry.Text, "^[0-9]{4}SJ-[0-9]{3}") && !this.fluid_mode_checkbutton.Active)
				SampleAcquisitionDialog.special_mode = SpecialModeType.SampleJar;
			else
				SampleAcquisitionDialog.special_mode = SpecialModeType.None;
			
			bool series_name_valid = true;
            if (Regex.IsMatch(this.series_entry.Text, SampleAcquisitionDialog.series_name_regex) == false &&
				SampleAcquisitionDialog.special_mode == SpecialModeType.None)
			{
				enable_sample = false;
				series_name_valid = false;
				this.series_image.Visible = true;
				this.series_label.Text = Catalog.GetString("invalid format");
			}

            try
            {
                if (SampleAcquisitionDialog.special_mode != SpecialModeType.None)
                {
                    if  (!string.IsNullOrEmpty(this.filechooser.Filename) && 
                         File.Exists(Path.Combine(this.filechooser.Filename, ((int)SampleAcquisitionDialog.special_mode).ToString() + "_" + this.series_entry.Text + "_specimen.csv")))
                    {
                        enable_sample = false;
                        series_name_valid = false;
                        this.series_image.Visible = true;
                        this.series_label.Text = Catalog.GetString("already exists");
                    }
                }
                else if (SampleAcquisitionDialog.euro_otc_mode)
                {
                    if  (!string.IsNullOrEmpty(this.filechooser.Filename) && File.Exists(Path.Combine(this.filechooser.Filename, this.series_entry.Text + "_Euro-OTC_0001.csv")))
                    {
                        enable_sample = false;
                        series_name_valid = false;
                        this.series_image.Visible = true;
                        this.series_label.Text = Catalog.GetString("already exists");
                    }
                }
                else
                {
                    if  (!string.IsNullOrEmpty(this.filechooser.Filename) && File.Exists(Path.Combine(this.filechooser.Filename, this.series_entry.Text + "_0001.csv")))
                    {
                        enable_sample = false;
                        series_name_valid = false;
                        this.series_image.Visible = true;
                        this.series_label.Text = Catalog.GetString("already exists");
                    }
                }
            }
            catch (ArgumentException ex)
            {
                enable_sample = false;
                series_name_valid = false;
                this.series_image.Visible = true;
                this.series_label.Text = ex.Message;
            }

            if (series_name_valid)
			{
				this.series_image.Visible = false;
				this.series_label.Text = string.Empty;
			}

			if (SampleAcquisitionDialog.euro_otc_mode)
			{
				if (this.remarks_entry.Text.Length < 5)
				{
					this.remarks_image.NoShowAll = false;
					this.remarks_image.Visible = true;
					enable_sample = false;
				}
				else
				{
					this.remarks_image.Visible = false;
					this.remarks_image.NoShowAll = true;
				}
			}

			if (this.temperatur_stabilization_checkbutton.Active)
			{
				if (!string.IsNullOrEmpty(this.sav_sgs_gradient_string))
				    this.sgs_gradient_info_label.Text += this.sav_sgs_gradient_string;
				if (!string.IsNullOrEmpty(this.sav_sgs_temperature_string))
					this.sgs_temperature_info_label.Text += this.sav_sgs_temperature_string;
				this.sav_sgs_gradient_string = null;
				this.sav_sgs_temperature_string = null;
			}
			else if (string.IsNullOrEmpty(this.sav_sgs_gradient_string) && string.IsNullOrEmpty(this.sav_sgs_temperature_string))
			{
				int ix = this.sgs_gradient_info_label.Text.IndexOf('\t');
				this.sav_sgs_gradient_string = this.sgs_gradient_info_label.Text.Substring(ix);
				this.sgs_gradient_info_label.Text = this.sgs_gradient_info_label.Text.Remove(ix);

				ix = this.sgs_temperature_info_label.Text.IndexOf('\t');
				this.sav_sgs_temperature_string = this.sgs_temperature_info_label.Text.Substring(ix);
				this.sgs_temperature_info_label.Text = this.sgs_temperature_info_label.Text.Remove(ix);
			}
			
			this.acquisition_button.Sensitive = enable_sample;
		}

		private string sav_sgs_gradient_string;
		private string sav_sgs_temperature_string;

		private void SaveReferenceSpectrum(Spectrum spectrum, string type)
		{
			if (this.state == AcquisitionState.Close)
				return;
			
			string dir = Path.Combine(this.filechooser.Filename, type);
			if (!Directory.Exists(dir))
			    Directory.CreateDirectory(dir);
			string fname = spectrum.Id.Remove(0, 2) + "_" + type;
			this.AddComment(spectrum);
			CSV.Write(spectrum, Path.Combine(dir, fname));
		}
		
		private void SaveProcessedSpectrum(Spectrum spectrum, string fname)
		{
			if (this.state == AcquisitionState.Close)
				return;
			
			string dir = Path.Combine(this.filechooser.Filename, "processed");
			if (!Directory.Exists(dir))
			    Directory.CreateDirectory(dir);
			this.AddComment(spectrum);
			spectrum.Label = fname;
			CSV.Write(spectrum, Path.Combine(dir, fname));
		}
		
		private void OnDeleteEvent(object o, DeleteEventArgs e)
		{
			Settings.Current.ProbeSerial = this.probe_serial_entry.Text;
			Settings.Current.WhiteReferenceSerial = this.white_reference_serial_entry.Text;
			Settings.Current.BlackReferenceSerial = this.black_reference_serial_entry.Text;
			
			e.RetVal = true;
			this.state = AcquisitionState.Close;
			if (!this.sample_acquisition_window.Title.EndsWith(Catalog.GetString("(Quitting...)")))
				this.sample_acquisition_window.Title += " " + Catalog.GetString("(Quitting...)");
		}
		
		internal void OnSgsTemperatureButtonClicked(object o, EventArgs e)
		{
//			if (this.times.Count < 4)
//			{
//				Plugin.host.ShowInfoMessage(this.stabilized_acquisition_window,
//				                            Catalog.GetString("Not enough temperature data available."));
//				return;
//			}
//
//			DataSet temp  = new DataSet(this.times, this.sgs_temperatures);
//			Dialog dialog = new Dialog(Catalog.GetString("SGS temperature characteristic") + 
//			                           " ("  + this.device.Info.SerialNumber + ")",
//			                           this.stabilized_acquisition_window, DialogFlags.Modal);
//			dialog.AddButton(Stock.Save,    0);
//			dialog.AddButton(Stock.Print,   1);
//			dialog.AddButton(Stock.Refresh, 2);
//			dialog.AddButton(Stock.Close, ResponseType.Close);
//			
//			dialog.Response += new ResponseHandler(this.OnFrequencyDialogResponse);
//			
//			dialog.HasSeparator  = true;
//			dialog.WidthRequest  = 780;
//			dialog.HeightRequest = 400;
//			
//			this.freq_plot = new ScrolledPlot();
//			this.freq_plot.Plot.BackgroundColor = this.ui.BackgroundColor;
//			this.freq_plot.Plot.AddDataSet(freq / new DataSet.Operand(60f, 1f));
//			this.freq_plot.Plot.XLabel = Catalog.GetString("Time / min");
//			this.freq_plot.Plot.YLabel = Catalog.GetString("Mirror frequency / Hz");
//			this.freq_plot.Plot.Title = Catalog.GetString("Device serial:") + 
//				                        " " + this.device.Info.SerialNumber;
//			this.freq_plot.Plot.LegendVisible = false;
//			dialog.VBox.PackStart(this.freq_plot);
//			this.freq_plot.ShowAll();
//			
//			do
//			{
//				dialog.Run();
//			} while (this.run_frequency_dialog);
//			
//			dialog.Destroy();
			
		}
		
		internal void OnFinderTemperatureButtonClicked(object o, EventArgs e)
		{
		}

		private bool _resetting_checkbutton = false;

		internal void OnLightSourceStabilizationCheckbuttonToggled(object o, EventArgs e)
		{
			if (this._resetting_checkbutton)
				return;

			CheckButton cb = o as CheckButton;

			if (this.state != AcquisitionState.Ready &&
			    this.state != AcquisitionState.Warmup)
			{
				this._resetting_checkbutton = true;
				cb.Active = !cb.Active;
				this._resetting_checkbutton = false;
				Plugin.host.ShowInfoMessage(this.sample_acquisition_window, 
				                            Catalog.GetString("Cannot change this preference while device is busy."));
				return;
			}

			if (cb.Active)
			{

				try
				{
					this.finder.Device.SetFinderLight(true, null);
				}
				catch
				{
					// real exception handling is done by Quickstep
					cb.Active = !cb.Active;
				}
				this.times.Clear();
				this.sgs_temperatures.Clear();
				this.finder_temperatures.Clear();
				this.dark_master = null;
				this.white_master = null;
			}
			else
			{
				try
				{
					this.finder.Device.SetFinderLight(false, null);
				}
				catch
				{
					// real exception handling is done by Quickstep
					cb.Active = !cb.Active;
				}
			}
		}
		
		[Widget] Gtk.Window message_window = null;
		[Widget] Gtk.Label  info_label = null;
		[Widget] Gtk.Button info_ok_button = null;

		public void ShowInfoMessage(string text)
		{
			if (this.message_window == null)
			{
				Glade.XML gui = new Glade.XML(null, "Acquisition.Acquisition.glade", "message_window", "Hiperscan");
				gui.Autoconnect(this);

				this.info_ok_button.Clicked += delegate {
					this.message_window.HideAll();
				};
			}

			this.message_window.TransientFor = this.sample_acquisition_window;
			this.message_window.SetPosition(WindowPosition.Center);
			
			this.message_window.Title = Plugin.name + " " + Catalog.GetString("Information");
			this.message_window.IconList = Plugin.FinderIcons;
			
			this.info_label.Text = text;
			this.message_window.Modal = true;
			
			this.message_window.ShowAll();
			this.message_window.Present();
		}

		public bool StabilizedLightSource
		{
			get { return this.lightsource_stabilization_checkbutton.Active; }
		}
	}
}