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



using System;
using System.Threading;
using System.Diagnostics;

using Hiperscan.Common;
using Hiperscan.Spectroscopy;
using Hiperscan.Unix;


namespace Hiperscan.SGS
{
	internal class Device0_0 : Device
	{
		public enum Protocol : byte
		{
			WriteConfig = 1,        //sending config data
			StartAcquisition = 25,  //get ready to start a messurement //take a oneshot
			DebugParam = 3,         //set debug parameter
			DebugControl = 4,       //make configurations during the debug
			SetOffset = 5,          //configure offset (? may the offset of the spectrometer)
			ReadADC = 6,            //Read ADC value
			GetAVRVersion = 7,      //get the version of the MC firmware
			GetSelftest = 8,        //get the selftest infos
			ResetDSP = 9,           //reset DSP via watchdog
			GetMirrorParam = 10,    //get the mirror parameter
			SetADCVoltage = 11,     //set the DAC voltage
			SetMirrorFreq = 12,     //set the mirror frequency
			SaveSpectrumParam = 13, //save the spectrum parameter
			Reset = 14,             //reset the AVR and reread the EEPROM
			SaveMirrorPram = 15,    //save the mirror parameter
			StartBurst = 16,        //start a burst messurement
			StopBurst = 17,
			ReadConfig = 18,        //read parameter (?)
			// SearchOP = 19,        //search the mirror working point //Depricated
			SetCLPParam = 20,       //set the "closed loop parameter"
			GetCLPParam = 21,       //get...
			SetDebugInfo = 22,      //configure the debug information
			ReadEEPROM = 23,        //Read the EEPROM
			ReadOPStatus = 24,      // read the operation point status //Depricated
			
			Acknowledge = 0xAA
		}
	
		
		public Device0_0(DeviceInfo info, VersionInfo version_info) : base(info, version_info)
		{
		}

		public override void Open(bool auto_init)
		{
			lock (this.opening_mutex)
			{
				try
				{
					if (this.GetState() == State.Disconnected)
					{
						// initialize usb device
						try
						{
							this.usb.OpenBySerialNumber(this.info.SerialNumber);
						}
						catch (System.Runtime.Remoting.RemotingException ex)
						{
							throw ex;
						}
						catch (System.Exception ex)
						{
							PlatformID osversion = Environment.OSVersion.Platform;
							if (osversion == PlatformID.Win32NT      ||
							    osversion == PlatformID.Win32S       ||
							    osversion == PlatformID.Win32Windows ||
							    osversion == PlatformID.WinCE)
							{
								Console.WriteLine("Exception while opening usb device: " + ex.Message);
								Console.WriteLine("\t-> try once more in 4 seconds...");
								System.Threading.Thread.Sleep(4000);
								this.usb.OpenBySerialNumber(this.info.SerialNumber);
							}
							else
							{
								throw ex;
							}
						}
						
						this.usb.ResetDevice();
						this.usb.SetTimeouts();
						this.usb.SetLatency();
						
						this.WriteCommand(Protocol.StopBurst);

						this.SetState(State.Idle);

						if (auto_init == false)
							return;

						this.Connect();
						
						try
						{						
							this.current_config = this.ReadConfig();
						}
						catch (System.TimeoutException)
						{
							Console.WriteLine("Exception while ReadConfig() -> try once more");
							this.usb.ResetDevice();
							this.usb.SetTimeouts();
							this.usb.SetLatency();
							
							this.WriteCommand(Protocol.StopBurst);
							this.SetState(State.Idle);
							this.current_config = this.ReadConfig();
						}
						
						this.current_config.Samples = 5000;
						this.current_config.Average = 500;
						this.current_config.Collect = false;
						this.WriteConfig(this.CurrentConfig);
						
						Console.WriteLine("Current config:");
						Console.WriteLine("\tAmplitude:\t"  + current_config.Amplitude);
						Console.WriteLine("\tDeviation:\t"  + current_config.Deviation);
						Console.WriteLine("\tLambdaCenter:\t" + current_config.LambdaCenter);
						
						this.firmware_version  = this.ReadFirmwareVersion();
						this.firmware_revision = this.ReadFirmwareRevision();
						this.lut_timestamp     = this.ReadLutTimestamp();
						this.clock_frequency   = this.ReadClockFrequency();
						this.is_finder = this.FinderPresent();
						
						if (this.is_finder)
						{
							this.SetFinderWheel(FinderWheelPosition.Initial, null);
							this.SetFinderLight(false, null);
						}

						Console.WriteLine("Is finder: " + this.is_finder.ToString());
					}
				}
				catch (System.Runtime.Remoting.RemotingException ex)
				{
					this.SetState(State.Disconnected);
					throw ex;
				}
				catch (Exception ex)
				{
					if (this.IsUnprogrammed)
					{
						this.current_config = new Config(this);
						this.firmware_version  = this.ReadFirmwareVersion();
						this.firmware_revision = this.ReadFirmwareRevision();
						this.SetState(State.Unprogrammed);
					}
					else
					{
						this.SetState(State.Disconnected);
						try
						{
							this.usb.Close();
						}
						catch {}
					}
					throw ex;
				}
				finally
				{
					this.is_initialized = true;
				}
			}
		}

		public override void Close()
		{
			lock (this.opening_mutex)
			{
				if ((this.GetState() == State.Continuous) ||
				    (this.GetState() == State.Stream)      ||
				    (this.GetState() == State.QuickStream))
					this.StopStream();

				try
				{
					// workaround for #22821: since we do not have a disconnect command until protocol 0.14
					this.IgnoreSgsExceptions = true;
					this.NotifyBeforeDisconnect();
				}
				catch { }

				if (this.GetState() != State.Disconnected)
				{
					try
					{
						if (this.IsShared)
							this.Unshare();
					}
					catch {}
					try
					{
						this.usb.Close();
					}
					catch {}
					
					this.SetState(State.Disconnected);
				}
			}
		}
		
//		public override void ResetDSP()
//		{
//			this.Write(new byte[] {(byte)Protocol.ResetDSP});
//			System.Threading.Thread.Sleep(200);
//			this.usb.Purge();			
//		}

		internal override Config ReadConfig(bool throw_if_invalid)
		{
			if (!this.SetState(State.ReadConfig))
				throw new SGSException(Catalog.GetString("Cannot read config: Device is not ready."), this);

			this.usb.Purge();
			this.WriteCommand(Protocol.ReadConfig);
			
			byte[] data = this.ReadBlocking(20, this.CurrentConfig.Timeout);
			
			this.SetState(State.Idle);
			Config config = new Config(this);

			try
			{
				config.Name = this.ReadDeviceName();
			}
			catch (NotImplementedException)
			{
				this.current_config.Name = this.Info.SerialNumber;
			}
			
			config.AutoTimeout  = this.CurrentConfig.AutoTimeout;
			config.Timeout      = this.CurrentConfig.Timeout;
			
			config.Samples      = (ushort)(data[0]  | (data[1]  << 8));
			config.LambdaMin    = (double)(data[2]  | (data[3]  << 8)) /  10f;
			config.LambdaMax    = (double)(data[4]  | (data[5]  << 8)) /  10f;
			config.Amplitude    = (double)(data[6]  | (data[7]  << 8)) / 100f;
			config.TriggerDelay = (ushort)(data[8]  | (data[9]  << 8));
			config.LambdaCenter = (double)(data[10] | (data[11] << 8));
			config.Deviation    = (double)(data[12] | (data[13] << 8)) / 100f;
			config.Grid         = (ushort)(data[14] | (data[15] << 8));
			config.SampleRate   = (double)(data[16] | (data[17] << 8) 
			                                        | (data[18] << 16) 
			                                        | (data[19] << 24)) / 100f;
			
			if (!config.IsValid && throw_if_invalid)
				throw new SGSException(Catalog.GetString("Device configuration is not valid."), this);
			
			config.IsInitialized = true;
			return config;
		}
		
		internal override void WriteConfig(Config config)
		{
			const int buf_len = 16;
			byte[] data = new byte[buf_len];
			
			data[0] = (byte)(config.Samples & 0xFF);
			data[1] = (byte)(config.Samples >>   8);
			
			data[2] = (byte)(config.Average & 0xFF);
			data[3] = (byte)(config.Average >>   8);
			
			data[4] = Convert.ToByte(config.Collect);
			data[5] = 0;
			
			data[6] = (byte)(config.Grid & 0xFF);
			data[7] = (byte)(config.Grid >>   8);
			
			data[8] = (byte)((int)config.LambdaCenter & 0xFF);
			data[9] = (byte)((int)config.LambdaCenter >>   8);
			
			double deviation = config.Deviation * 100f;
			data[10] = (byte)((int)deviation & 0xFF);
			data[11] = (byte)((int)deviation >>   8);
			
			data[12] = (byte)(config.TriggerDelay & 0xFF);
			data[13] = (byte)(config.TriggerDelay >>   8);
			
			double amplitude = config.Amplitude * 100f;
			data[14] = (byte)((int)amplitude & 0xFF);
			data[15] = (byte)((int)amplitude >>   8);

			if (!this.SetState(State.WriteConfig))
				throw new SGSException(Catalog.GetString("Cannot write config: Device is not ready."), this);
			
			System.Threading.Thread.Sleep(500);
			this.WriteCommand(Protocol.WriteConfig);
			
			uint count = this.Write(data);			
			if (count != buf_len)
			{
				throw new SGSException(Catalog.GetString("Cannot write config."), this);
			}
			
			// read op status (?)
			System.Threading.Thread.Sleep(200);
			this.usb.Purge();
			this.Write(new byte[] {(byte)Protocol.ReadOPStatus});
			this.ReadBlocking(1, this.CurrentConfig.Timeout);
			System.Threading.Thread.Sleep(200);
			
			this.SetState(State.Idle);

			try
			{
				this.WriteDeviceName(config.Name);
			}
			catch (NotImplementedException) { }
		}

		public override void WriteEEPROM()
		{
			byte[] tx_buf = new byte[1];
			tx_buf[0] = (byte)Protocol.SaveSpectrumParam;
			
			uint count = this.Write(tx_buf);
			
			if (count != tx_buf.Length)
				throw new SGSException(Catalog.GetString("Cannot write command to device."), this);
		}

		public override void StartSingle(bool auto_finder_light)
		{
			Thread run_single = new Thread(new ParameterizedThreadStart(this.DoRunSingle));
			run_single.Start(auto_finder_light);
		}
		
		private void DoRunSingle(object o)
		{
			bool auto_finder_light = (bool)o;
			
			try
			{
				Spectrum spectrum = this.Single(auto_finder_light);
				this.spectrum_queue.Enqueue(spectrum);
			}
			catch (System.Exception ex)
			{
				this.ExceptionHandler(Catalog.GetString("Cannot read single spectrum:") +
				                      " " + ex.Message, ex);
			}
		}

		protected virtual void ReadSpectrum(out uint[] add_data, out double[] lambda, out double[] intensity, IdleTaskHandler idle_task)
		{
			this.usb.Purge();
			
			this.WriteCommand(Protocol.StartAcquisition);
			this.WaitForAcknowledge();
			
			this.ReadFixedData();
			
			add_data  = this.ReadAdditionalData();
			lambda    = this.ReadWavelengthData(this.acquisition_info.Samples);
			intensity = this.ReadIntensityData(this.acquisition_info.Samples);

			if (this.CheckIntensity(lambda, intensity) == false)
				throw new InvalidLUTException(Catalog.GetString("Device returned invalid spectrum. This can be caused by an invalid configuration. Please restart spectrometer."), this);

			this.ReadMaxIntensity();
		}
		
		public override Spectrum Single(bool auto_finder_light, IdleTaskHandler idle_task)
		{
			try
			{
				if (!this.current_config.IsValid)
					throw new SGSException(Catalog.GetString("Device configuration is not valid."), this);

				Spectrum spectrum = null;
				Stopwatch sw = new Stopwatch();

				if (this.GetState() == State.Continuous ||
				    this.GetState() == State.Stream     ||
				    this.GetState() == State.QuickStream)
				{
					while (spectrum == null)
					{
						lock (this.current_spectrum_mutex)
							spectrum = new Spectrum(this.current_spectrum);
						
						System.Threading.Thread.Sleep(50);
					}
				}
				else
				{
					if (auto_finder_light)
						this.SetFinderLight(true, idle_task);
					
					if (!this.SetState(State.Single))
						throw new SGSException(Catalog.GetString("Device is not ready."), this);
					
					uint[]   add_data;
					double[] lambda;
					double[] intensity;

					sw.Start();
					this.ReadSpectrum(out add_data, out lambda, out intensity, idle_task);
					sw.Stop();
					Console.WriteLine("Acquisition period: {0}", sw.Elapsed);
				
					this.SetState(State.Idle);
					
					if ((lambda.Length < 2) || (lambda.Length != intensity.Length))
					{
						Console.WriteLine("Read spectrum is not valid.");
						this.ExceptionHandler(Catalog.GetString("Read spectrum is not valid."), null);
						return null;
					}
					
					this.SpectrumCount = 1;

					WavelengthLimits limits = new WavelengthLimits(this.HardwareProperties.MinWavelength, this.HardwareProperties.MaxWavelength);
					spectrum = new Spectrum(new DataSet(lambda, intensity), limits);
					
					spectrum.AddData = add_data;
					
					if (auto_finder_light)
						this.SetFinderLight(false, idle_task);
				}
				
				DateTime now = DateTime.Now;
				
				spectrum.Serial             = this.info.SerialNumber;
				spectrum.FirmwareVersion    = this.FirmwareVersion.ToString();
				spectrum.FirmwareRevision   = this.FirmwareRevision;
				spectrum.LutTimestamp       = this.LutTimestamp.ToString();
				spectrum.SpectralResolution = this.HardwareProperties.SpectralResolution;
				spectrum.AverageCount       = this.CurrentConfig.Average;
				spectrum.SpectrumType       = SpectrumType.Single;

				if (this.IsFinder)
                    spectrum.ProbeType = this.GetFinderProbeType();

				spectrum.Timestamp           = now;
				spectrum.Id                 = "1_" + now.ToString("yyyy-MM-dd_HH-mm-ss_") + this.info.SerialNumber;
				spectrum.Label              = now.ToString() + " " + this.CurrentConfig.Name;
				spectrum.DefaultLabel       = true;
				spectrum.Comment            = this.CurrentConfig.DefaultComment;
				spectrum.IsFromFile         = false;
				spectrum.Number             = (long)this.SpectrumCount;
				spectrum.LimitBandwidth     = this.LimitBandwith;

				spectrum.HardwareInfo = this.FinderVersion.ToArray();

				if (this.CurrentConfig.HasAdcCorrection)
					spectrum.AdcCorrection = this.CurrentConfig.AdcCorrection;

				if (Environment.GetEnvironmentVariable(Env.HCL.LOG_ACQUISITION_PERIOD) == "1")
					spectrum.Comment += "Spectrum acquisition period: " + sw.Elapsed.ToString();
				
				if (this.StateNotification)
					this.NewSingleNotify(spectrum.Clone());

				return spectrum;
			}
			catch (Exception ex)
			{
                if ((ex is InvalidSpectrumException) && 
                    (this.DisableInternalExceptionHandling || this.IgnoreSgsExceptions))
                {
                    this.SetState(State.Idle);
                }
                
				this.ExceptionHandler(Catalog.GetString("Cannot read single spectrum:") +
				                      " " + ex.Message, ex);
				return null;
			}
		}

		public override void StartQuickStream()
		{
			Thread run_quick = new Thread(new ThreadStart(this.DoRunQuick));
			run_quick.Start();
		}

		public override void StartStream()
		{
			this.quick_stream_running = false;
			
			switch (this.CurrentConfig.StreamType)
			{
				
			case StreamType.Continuous:
				Thread run_continuous = new Thread(new ThreadStart(this.DoRunContinuous));
				run_continuous.Start();
				break;
				
			case StreamType.Timed:
				Thread run_timed = new Thread(new ThreadStart(this.DoRunTimed));
				run_timed.Start();
				break;
				
			default:
				throw new SGSException("Unknown StreamType", this);
				
			}
		}

		public override void StopStream()
		{
			if (this.quick_stream_running)
			{
				this.SetState(State.IdleRequest);
				return;
			}
			
			switch (this.CurrentConfig.StreamType)
			{
				
			case StreamType.Continuous:
				this.WriteCommand(Protocol.StopBurst);
				this.SetState(State.IdleRequest);
				break;
				
			case StreamType.Timed:
				this.SetState(State.IdleRequest);
				break;
								
			default:
				throw new SGSException("Unknown StreamType", this);
				
			}
		}

		private void WriteCommand(Protocol command)
		{
			byte[] tx_buf = new byte[2];
			tx_buf[0] = (byte)command;
			tx_buf[1] = 0;			
			
			uint count = this.Write(tx_buf);
			
			if (count != tx_buf.Length)
				throw new SGSException(Catalog.GetString("Cannot write command to device."), this);
		}
		
		private double[] ReadWavelengthData(int samples)
		{
			byte[] rx_buf = this.ReadBlocking(2*(uint)samples, this.CurrentConfig.Timeout);
			
			double[] lambda = new double[samples];
			for (int ix = 0; ix < samples; ++ix)
			{
				lambda[ix] = (double)( rx_buf[2*ix] + (rx_buf[2*ix+1] << 8) ) / 10f;
			}
			
			return lambda;
		}
		
		private double[] ReadIntensityData(int samples)
		{
			byte[] rx_buf = this.ReadBlocking(2*(uint)samples, this.CurrentConfig.Timeout);

			double[] intensity = new double[samples];
			for (int ix = 0; ix < samples; ++ix)
			{
				intensity[ix] = (double)( rx_buf[2*ix] + (rx_buf[2*ix+1] << 8) ) / 10000f;
			}
			
			return intensity;
		}
		
		private ushort ReadMaxIntensity()
		{
			byte[] rx_buf = this.ReadBlocking(2, this.CurrentConfig.Timeout);
			
			return BitConverter.ToUInt16(rx_buf, 0);
		}
		
		private void ReadFixedData()
		{
			byte[] rx_buf = this.ReadBlocking(8, this.CurrentConfig.Timeout);
			
			double mirror_freq = ( rx_buf[0] + (rx_buf[1] << 8) ) / 100.0;
			int    samples     =   rx_buf[2] + (rx_buf[3] << 8);
			double sample_rate =   rx_buf[4] + (rx_buf[5] << 8) + (rx_buf[6] << 16) + (rx_buf[7] << 24);

			lock (this)
				this.acquisition_info = new AcquisitionInfo(mirror_freq, samples, sample_rate);
			
			TimeSpan span = DateTime.Now - this.creation_time;
			
			lock (this)
				this.frequency.Add((double)span.TotalSeconds, mirror_freq); 
		}
		
		private uint[] ReadAdditionalData()
		{
			byte[] rx_buf = this.ReadBlocking(1, this.CurrentConfig.Timeout);
			uint count = rx_buf[0];
			
			if (count > 0)
			{
				rx_buf = this.ReadBlocking(count, this.CurrentConfig.Timeout);
			}
			
			//each value is splitted in 4 Byte and will transmitted multiplied by 1000
			uint[] add_data = new uint[count/4];
			for (int ix = 0; ix < add_data.Length; ++ix)
			{
				add_data[ix] = (UInt32)(
				                        ((UInt32)rx_buf[ix*4]        ) +
				                        ((UInt32)rx_buf[ix*4+1] << 8 ) +
				                        ((UInt32)rx_buf[ix*4+2] << 16) +
				                        ((UInt32)rx_buf[ix*4+3] << 24) 
				                        );
			}
			
			return add_data;
		}
		
		private void DoRunContinuous()
		{
			try
			{
				if (this.GetState() == State.QuickStream)
				{
					this.StopStream();
					this.WaitForIdle(5*this.CurrentConfig.Timeout);
					this.StartStream();
					return;
				}
				
				if (!this.current_config.IsValid)
					throw new SGSException(Catalog.GetString("Device configuration is not valid."), this);
				
				this.SetFinderLight(true, null);
				
				if (!this.SetState(State.Continuous))
					throw new SGSException(Catalog.GetString("Device is not ready."), this);
				
				this.usb.Purge();
				this.WriteCommand(Protocol.StartBurst);
				
				this.SpectrumCount = 0;
				
				for (;;)
				{
					try
					{
						this.WaitForAcknowledge();
					}
					catch (Exception ex)
					{
						if (!(ex is TimeoutException) || (this.GetState() != State.IdleRequest))
						{
							try
							{
								this.StopStream();
							}
							catch {}
							throw ex;
						}
						break;
					}
					
					try
					{
						this.ReadFixedData();
						
						double[] lambda    = this.ReadWavelengthData(this.acquisition_info.Samples);
						double[] intensity = this.ReadIntensityData(this.acquisition_info.Samples);
						
						this.ReadMaxIntensity();					

						WavelengthLimits limits = new WavelengthLimits(this.HardwareProperties.MinWavelength, this.HardwareProperties.MaxWavelength);
						Spectrum spectrum = new Spectrum(new DataSet(lambda, intensity), limits);
						
						spectrum.Timestamp         = DateTime.Now;
						spectrum.Serial           = this.info.SerialNumber;
						spectrum.FirmwareVersion  = this.FirmwareVersion.ToString();
						spectrum.FirmwareRevision = this.FirmwareRevision;
						spectrum.LutTimestamp     = this.LutTimestamp.ToString();
						spectrum.AverageCount     = this.CurrentConfig.Average;
						spectrum.SpectrumType     = SpectrumType.Stream;

						if (this.IsFinder)
                            spectrum.ProbeType = this.GetFinderProbeType();

						spectrum.Id              = "5_continuous" + this.info.SerialNumber;
						spectrum.Label           = Enum.GetName(typeof(State), State.Continuous) + " "  + this.CurrentConfig.Name;
						spectrum.DefaultLabel    = true;
						spectrum.Comment         = this.CurrentConfig.DefaultComment;
						spectrum.IsFromFile      = false;
						spectrum.Number          = (long)(++this.SpectrumCount);
						spectrum.LimitBandwidth  = this.LimitBandwith;
						
						if (this.CurrentConfig.HasAdcCorrection)
							spectrum.AdcCorrection = this.CurrentConfig.AdcCorrection;
				
						if (this.StateNotification)
							this.NewStreamNotify(spectrum.Clone());
						
						this.spectrum_queue.Enqueue(spectrum);
						
						lock (this.current_spectrum_mutex)
							this.current_spectrum = spectrum;
					}
					catch (Exception ex)
					{
						try
						{
							this.StopStream();
						}
						catch {}
						throw ex;
					}
				}
				Console.WriteLine("Queue contains still " + this.spectrum_queue.Count + " spectra.");
				
				State state = this.GetState();

				if (state == State.Unknown)
					return;

				if (state != State.Disconnected)
					this.SetState(State.Idle);
				
				this.SetFinderLight(false, null);
			}
			catch (Exception ex)
			{
				this.ExceptionHandler(Catalog.GetString("Cannot read in continuous mode:") +
				                      " " + ex.Message, ex);
			}
		}
		
		private void DoRunTimed()
		{
			try
			{
				if (this.GetState() == State.QuickStream)
				{
					this.StopStream();
					this.WaitForIdle(5*this.CurrentConfig.Timeout);
					this.StartStream();
					return;
				}
				
				if (!this.current_config.IsValid)
					throw new SGSException(Catalog.GetString("Device configuration is not valid."), this);
				
				this.SetFinderLight(true, null);
				
				if (!this.SetState(State.Stream))
					throw new SGSException(Catalog.GetString("Device is not ready."), this);
				
				DateTime then = DateTime.Now;
				this.SpectrumCount = 0;
				
				while ( (this.GetState() == State.Stream)     &&
				       ((this.current_config.TimedCount == 0) ||
				        (this.SpectrumCount < this.CurrentConfig.TimedCount)) )
				{
					try
					{
						uint[]   add_data;
						double[] lambda;
						double[] intensity;
						Stopwatch sw = new Stopwatch();

						sw.Start();
						this.ReadSpectrum(out add_data, out lambda, out intensity, null);
						sw.Stop();
						Console.WriteLine("Acquisition period: {0}", sw.Elapsed);

						WavelengthLimits limits = new WavelengthLimits(this.HardwareProperties.MinWavelength, this.HardwareProperties.MaxWavelength);
						Spectrum spectrum = new Spectrum(new DataSet(lambda, intensity), limits);
						
						spectrum.AddData            = add_data;
						spectrum.Timestamp           = DateTime.Now;
						spectrum.Serial             = this.info.SerialNumber;
						spectrum.FirmwareVersion    = this.FirmwareVersion.ToString();
						spectrum.FirmwareRevision   = this.FirmwareRevision;
						spectrum.LutTimestamp       = this.LutTimestamp.ToString();
						spectrum.SpectralResolution = this.HardwareProperties.SpectralResolution;
						spectrum.AverageCount       = this.CurrentConfig.Average;
						spectrum.SpectrumType       = SpectrumType.Stream;

						if (this.IsFinder)
                            spectrum.ProbeType = this.GetFinderProbeType();

						spectrum.Id                 = "5_stream" + this.info.SerialNumber;
						spectrum.Label              = Enum.GetName(typeof(State), State.Stream) + " "  + this.CurrentConfig.Name;
						spectrum.DefaultLabel       = true;
						spectrum.Comment            = this.CurrentConfig.DefaultComment;
						spectrum.IsFromFile         = false;
						spectrum.Number             = (long)(++this.SpectrumCount);
						spectrum.LimitBandwidth     = this.LimitBandwith;
						
						spectrum.HardwareInfo = this.FinderVersion.ToArray();

						if (this.CurrentConfig.HasAdcCorrection)
							spectrum.AdcCorrection = this.CurrentConfig.AdcCorrection;

						if (Environment.GetEnvironmentVariable(Env.HCL.LOG_ACQUISITION_PERIOD) == "1")
							spectrum.Comment += "Spectrum acquisition period: " + sw.Elapsed.ToString();
				
						if (this.StateNotification)
							this.NewStreamNotify(spectrum.Clone());
						
						this.spectrum_queue.Enqueue(spectrum);
						
						lock (this.current_spectrum_mutex)
							this.current_spectrum = spectrum;
						
					}
					catch (Exception ex)
					{
                        if ((ex is InvalidSpectrumException) && 
                        (this.DisableInternalExceptionHandling || this.IgnoreSgsExceptions))
                        {
                            this.SetState(State.Idle);
                        }

                        this.ExceptionHandler(Catalog.GetString("Cannot read in timed mode:") + 
						                      " " + ex.Message, ex);
						break;
					}
					
					TimeSpan span;
					ulong mseconds = 0;
					ulong maxtime  = 0;
					bool  light_is_on = true;
					
					while (this.GetState() == State.Stream)
					{
						if (this.CurrentConfig.TimedSpan == 0)
							break;
						
						span     = DateTime.Now - then;
						mseconds = (ulong)span.TotalMilliseconds % (this.CurrentConfig.TimedSpan * 1000);
						
						if (mseconds < maxtime)
							break;
						else
							maxtime = mseconds;
						
						if ((this.CurrentConfig.TimedCount != 0) &&
						    (this.SpectrumCount >= this.CurrentConfig.TimedCount))
							break;
						
						// switch finder light off if there are more than 3.5 seconds to wait
						if (this.IsFinder && light_is_on && 
						    this.CurrentConfig.TimedSpan * 1000 - mseconds > 3500)
						{
							Console.WriteLine("Next acquisition in {0} ms.", 
							                  this.CurrentConfig.TimedSpan * 1000 - mseconds);
							lock (this)
							{
								this.SetState(State.Idle);
								this.SetFinderLight(false, null);
								this.SetState(State.Stream);
							}
							light_is_on = false;
						}

						Thread.Sleep(50);
					}
					
					if (!light_is_on && this.GetState() == State.Stream &&
					    (this.SpectrumCount < this.CurrentConfig.TimedCount || 
					     this.CurrentConfig.TimedCount == 0))
					{
						lock (this)
						{
							this.SetState(State.Idle);
							this.SetFinderLight(true, null);
							this.SetState(State.Stream);
						}
					}
				}
				Console.WriteLine("Queue contains still " + this.spectrum_queue.Count + " spectra.");
				
				State state = this.GetState();

				if (state == State.Unknown)
					return;

				if (state != State.Disconnected)
					this.SetState(State.Idle);
				
				this.SetFinderLight(false, null);
			}
			catch (Exception ex)
			{
				this.ExceptionHandler(Catalog.GetString("Cannot read in timed mode:") +
				                      " " + ex.Message, ex);
			}
		}

		private void DoRunQuick()
		{
			try
			{
				if (this.GetState() == State.Continuous ||
				    this.GetState() == State.Stream)
				{
					this.StopStream();
					this.WaitForIdle(this.CurrentConfig.Timeout);
					
					this.StartQuickStream();
					return;
				}
				
				Console.WriteLine("Write QuickStream config");
				this.old_config = new Config(this.CurrentConfig);
				this.CurrentConfig.Average = this.CurrentConfig.QuickAverage;
				this.CurrentConfig.Samples = this.CurrentConfig.QuickSamples;
				this.quick_stream_running  = true;
				this.WriteConfig(this.CurrentConfig);
				
				if (!this.current_config.IsValid)
					throw new SGSException(Catalog.GetString("Device configuration is not valid."), this);
				
				this.SetFinderLight(true, null);
				
				if (!this.SetState(State.QuickStream))
					throw new SGSException(Catalog.GetString("Device is not ready."), this);
				
				while (this.GetState() == State.QuickStream)
				{
					try
					{
						uint[]   add_data;
						double[] lambda;
						double[] intensity;
						this.ReadSpectrum(out add_data, out lambda, out intensity, null);

						WavelengthLimits limits = new WavelengthLimits(this.HardwareProperties.MinWavelength, this.HardwareProperties.MaxWavelength);
						Spectrum spectrum = new Spectrum(new DataSet(lambda, intensity), limits);
						
						spectrum.AddData            = add_data;
						spectrum.Timestamp           = DateTime.Now;
						spectrum.Serial             = this.info.SerialNumber;
						spectrum.FirmwareVersion    = this.FirmwareVersion.ToString();
						spectrum.FirmwareRevision   = this.FirmwareRevision;
						spectrum.LutTimestamp       = this.LutTimestamp.ToString();
						spectrum.SpectralResolution = this.HardwareProperties.SpectralResolution;
						spectrum.AverageCount       = this.CurrentConfig.Average;
						spectrum.SpectrumType       = SpectrumType.QuickStream;

						if (this.IsFinder)
                            spectrum.ProbeType = this.GetFinderProbeType();

						spectrum.Id                 = "5_stream" + this.info.SerialNumber;
						spectrum.Label              = Enum.GetName(typeof(State), State.QuickStream) + " "  + this.CurrentConfig.Name;
						spectrum.DefaultLabel       = true;
						spectrum.Comment            = this.CurrentConfig.DefaultComment;
						spectrum.IsFromFile         = false;
						//spectrum.Number       = ++this.SpectrumCount;
						spectrum.LimitBandwidth     = this.LimitBandwith;

						spectrum.HardwareInfo = this.FinderVersion.ToArray();
						
						if (this.CurrentConfig.HasAdcCorrection)
							spectrum.AdcCorrection = this.CurrentConfig.AdcCorrection;

						if (this.StateNotification)
							this.NewQuickStreamNotify(spectrum.Clone());

						this.spectrum_queue.Enqueue(spectrum);
						
						lock (this.current_spectrum_mutex)
							this.current_spectrum = spectrum;
						
					}
					catch (Exception ex)
					{
                        if ((ex is InvalidSpectrumException) && 
                            (this.DisableInternalExceptionHandling || this.IgnoreSgsExceptions))
                        {
                            this.SetState(State.Idle);
                        }

                        this.ExceptionHandler(Catalog.GetString("Cannot read in quick stream mode:") +
						                      " " + ex.Message, ex);
						break;
					}
				}
				
				Console.WriteLine("Queue contains still " + this.spectrum_queue.Count + " spectra.");
				
				State state = this.GetState();

				if (state == State.Unknown)
					return;

				if (state != State.Disconnected)
					this.SetState(State.Idle);

				this.SetFinderLight(false, null);
				
				this.CurrentConfig = this.old_config;
				this.old_config = null;
				this.WriteConfig(this.CurrentConfig);
			}
			catch (Exception ex)
			{
				this.ExceptionHandler(Catalog.GetString("Cannot read in quick stream mode:") + 
				                      " " + ex.Message, ex);
			}
		}

		private void WaitForAcknowledge()
		{
			byte[] rx_buf;
			DateTime then = DateTime.Now;
			
			for (;;)
			{
				if ( this.Read(out rx_buf, 1) > 0 )
				{
					if (rx_buf[0] == (byte)Protocol.Acknowledge)
					{
						this.Read(out rx_buf, 1);
						break;
					}
				}
				
				TimeSpan span = DateTime.Now - then;

				if (this.CurrentConfig.Timeout > 0 &&
				    this.CurrentConfig.Timeout < span.TotalMilliseconds)
				{
					if (this.GetState() != State.IdleRequest)
						this.SetState(State.Unknown);
					throw new TimeoutException(Catalog.GetString("Timeout while waiting for device acknowledge."));
				}
				
				this.IdleTaskNotify();
				System.Threading.Thread.Sleep(10);
			}
		}
	}
}
