//
//  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.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Text.RegularExpressions;

using Glade;

using Gtk;

using Hiperscan.Common;
using Hiperscan.SGS.Benchtop;
using Hiperscan.SGS.Common;
using Hiperscan.Spectroscopy;
using Hiperscan.Spectroscopy.IO;
using Hiperscan.Spectroscopy.Math;
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 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 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;

        [Widget] Gtk.Window message_window = null;
        [Widget] Gtk.Label info_label = null;
        [Widget] Gtk.Button info_ok_button = 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 string sav_sgs_gradient_string;
        private string sav_sgs_temperature_string;

        private bool _resetting_checkbutton = false;

        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 readonly bool autonomous_mode = false;
        private readonly 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) == false)
                this.filechooser.SetCurrentFolder(Settings.Current.TargetDirectory);
            else
                this.filechooser.SetCurrentFolder(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments));
            
            if (string.IsNullOrEmpty(Settings.Current.ProbeSerial) == false)
                this.probe_serial_entry.Text = Settings.Current.ProbeSerial;

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

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

            this.filechooser.CurrentFolderChanged += (sender, 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 += (sender, e) => this.UpdateControls();
            
            this.probe_serial_entry.Changed += (sender, e) =>
            {
                this.extwhite_timestamp = DateTime.MinValue;
                this.extempty_timestamp = DateTime.MinValue;
                this.transflectance_reference_timestamp = DateTime.MinValue;
                this.UpdateControls();
            };
            
            this.white_reference_serial_entry.Changed += (sender, e) =>
            {
                this.extwhite_timestamp = DateTime.MinValue;
                this.extempty_timestamp = DateTime.MinValue;
                this.transflectance_reference_timestamp = DateTime.MinValue;
                this.UpdateControls();
            };
            
            this.black_reference_serial_entry.Changed += (sender, e) =>
            {
                this.extwhite_timestamp = DateTime.MinValue;
                this.extempty_timestamp = DateTime.MinValue;
                this.transflectance_reference_timestamp = DateTime.MinValue;
                this.UpdateControls();
            };

            this.average_count_spin.ValueChanged += (sender, 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.Icons.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
            {
                this.finder.Device.ReadTemperatureControlInfo(out byte version, out byte control_value, out double 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 (Exception ex) when (ex is NotSupportedException || ex is 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;
            Unix.Timeout.Add(0, () =>
            {
                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;
                    
            while (true)
            {
                if (this.finder.Device.GetState() == Spectrometer.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.SetFinderLightSource(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.Equals(0.0) ||
                        sigma0.Equals(0.0) || 
                        this.add_data_di0.Equals(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.SetFinderLightSource(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.SetFinderLightSource(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.Reference = 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.Reference = 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.ReferenceSpectrum = 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 == false, 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.BlackReferenceException 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 == false,
                                                                                                       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) == false)
                            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.ReferenceSpectrum = this.current_transflectance_reference_spectrum.Clone();
                                else
                                    this.finder.Device.CurrentConfig.ReferenceSpectrum = 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.Reference = 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);
                            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.SetFinderLightSource(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()
        {
            try
            {
                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);
            }
            catch (Exception ex)
            {
                Plugin.host.ShowInfoMessage(this.sample_acquisition_window,
                                            string.Format(Catalog.GetString("An error occured while updating temperature status: {0}"),
                                                          ex.Message));
                throw;
            }
        }
        
        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) == false && 
                         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) == false &&
                         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) == false &&
                         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) == false)
                    this.sgs_gradient_info_label.Text += this.sav_sgs_gradient_string;

                if (string.IsNullOrEmpty(this.sav_sgs_temperature_string) == false)
                    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');
                if (ix >= 0)
                {
                    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');
                if (ix >= 0)
                {
                    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 void SaveReferenceSpectrum(Spectrum spectrum, string type)
        {
            if (this.state == AcquisitionState.Close)
                return;
            
            string dir = Path.Combine(this.filechooser.Filename, type);
            if (Directory.Exists(dir) == false)
                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...)"), StringComparison.InvariantCulture) == false)
                this.sample_acquisition_window.Title += " " + Catalog.GetString("(Quitting...)");
        }
        
        internal void OnSgsTemperatureButtonClicked(object o, EventArgs e)
        {
        }
        
        internal void OnFinderTemperatureButtonClicked(object o, EventArgs e)
        {
        }

        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.SetFinderLightSource(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.SetFinderLightSource(false, null);
                }
                catch
                {
                    // real exception handling is done by Quickstep
                    cb.Active = !cb.Active;
                }
            }
        }
        
        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 => this.lightsource_stabilization_checkbutton.Active;
    }
}