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


using System;
using System.Collections.Generic;

using Hiperscan.Spectroscopy;
using Hiperscan.Unix;


namespace Hiperscan.SGS
{
	internal class Device0_16 : Device0_15
	{
		protected new class Protocol : Device0_15.Protocol
		{
			public static readonly Parameter ReadTemperatureControlInput  = new Parameter('B', 'R', typeof(byte),  sizeof(byte));
			public static readonly Parameter WriteTemperatureControlInput = new Parameter('B', 'W', typeof(byte),  sizeof(byte));
			public static readonly Parameter WriteTemperatureControlInfo  = new Parameter('H', 'W', typeof(byte[]), 4);
			public static readonly Parameter ReadTemperatureControlInfo   = new Parameter('H', 'R', typeof(byte[]), 0);
			public static readonly Parameter Connect                      = new Parameter('S', 'C', typeof(byte),   0);
		}
		
		public Device0_16(DeviceInfo info, VersionInfo version_info) : base (info, version_info)
		{
		}

		public override void Open(bool auto_init)
		{
			lock (this.opening_mutex)
			{
				this.IgnoreSgsExceptions = false;

				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();
						
						// FIXME: read buffers empty (since purge does not work for some reason!)
						try
						{
							usb.ReadBlocking(5000, 10, null);
						}
						catch (TimeoutException) { }

						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.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\t"  + current_config.Amplitude);
						Console.WriteLine("\tDeviation:\t\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();

						Console.WriteLine("Read system parameter:");
						Console.WriteLine("Firmware version:\t" + this.firmware_version);
						Console.WriteLine("Firmware revision:\t" + this.firmware_revision);
						Console.WriteLine("LUT timestamp:\t" + this.lut_timestamp.ToString());
						Console.WriteLine("Clock frequency:\t" + this.clock_frequency.ToString());
						Console.WriteLine("Is Finder:\t\t" + this.is_finder.ToString());

						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;
				}
				
				this.ReadAdcCorrection();

				if (this.IsFinder)
					this.ReadFinderHardwareVersion();
			}
		}

		public override double ReadStatus()
		{
			short temperature = this.ReadShortParameter(Protocol.ReadStatus);
			
			if (temperature == short.MaxValue)
				throw new NotImplementedException(Catalog.GetString("Not supported by hardware."));
			
			return (double)temperature / 100.0;
		}

		public override double ReadFinderTemperature ()
		{
			if (!this.IsFinder)
				throw new NotImplementedException(Catalog.GetString("Not supported by hardware."));
			
			short temperature = this.ReadShortParameter(Protocol.ReadFinderTemperature);

			if (temperature == short.MaxValue)
				throw new NotImplementedException(Catalog.GetString("Not supported by hardware."));
			
			return (double)temperature / 100.0;
		}
		
		public override void WriteTemperatureControlInput(byte[] key, byte val)
		{
			if (!this.IsFinder)
				return;

			this.SetMaintenanceMode(key);
			
			Protocol.Parameter parameter = new Protocol.Parameter(Protocol.WriteTemperatureControlInput);
			parameter.ByteValue = val;
			
			if (!this.WriteParameter(parameter))
				throw new SGSException(Catalog.GetString("Operation failed."), this);
		}
		
		public override byte ReadTemperatureControlInput ()
		{
			if (!this.IsFinder)
				return 0;

			return this.ReadByteParameter(Protocol.ReadTemperatureControlInput);
		}
		
		public override void WriteTemperatureControlInfo(byte[] key, byte version, byte control_value, double target_temperature)
		{
			if (!this.IsFinder)
				throw new NotImplementedException(Catalog.GetString("Not supported by hardware."));
			
			this.SetMaintenanceMode(key);

			Protocol.Parameter parameter = new Protocol.Parameter(Protocol.WriteTemperatureControlInfo);

			byte[] tmp = BitConverter.GetBytes((Int16)(target_temperature * 100.0));
			
			byte[] buf = new byte[parameter.Count];
			buf[0] = version;
			buf[1] = control_value;
			Array.Copy(tmp, 0, buf, 2, tmp.Length);
			
			parameter.ByteArray = buf;
			
			if (!this.WriteParameter(parameter, 5000))
				throw new SGSException(Catalog.GetString("Cannot write temperature control info."), this);
		}
		
		public override void ReadTemperatureControlInfo (out byte version, out byte control_value, out double target_temperature)
		{
			if (!this.IsFinder)
				throw new NotImplementedException(Catalog.GetString("Not supported by hardware."));
			
			byte[] buf = this.ReadByteArrayParameter(Protocol.ReadTemperatureControlInfo);
			
			version            = buf[0];
			control_value      = buf[1];
			target_temperature = (double)BitConverter.ToInt16(buf, 2) / 100.0;
		}
		
		protected override void Connect()
		{
			if (this.WriteParameter(Protocol.Connect, 30000) == false)
				throw new SGSException(Catalog.GetString("Cannot connect device."), this);
		}
	}
}
