// Created with MonoDevelop
//
//    Copyright (C) 2012 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_17 : Device0_16
	{
		protected new class Protocol : Device0_16.Protocol
		{
			public static readonly     Parameter ReadAcquisitionOffset   = new Parameter('A', 'O', typeof(int),   sizeof(int));
			public static readonly     Parameter WriteHardwareVersion    = new Parameter('S', 'V', typeof(byte[]), 2);
			public static readonly     Parameter ReadDeviceProperties    = new Parameter('J', 'R', typeof(byte[]), 0);
			public static readonly     Parameter WriteHardwareProperties = new Parameter('J', 'W', typeof(byte[]), 24);
			public static readonly     Parameter WriteAdcResolution      = new Parameter('O', 'W', typeof(byte), sizeof(byte));
			public static readonly     Parameter ReadAdcResolution       = new Parameter('O', 'R', typeof(byte), 0);
			public static new readonly Parameter WriteLut                = new Parameter('L', 'W', typeof(byte[]), 10028);
		}
		
		public Device0_17(DeviceInfo info, VersionInfo version_info) : base (info, version_info)
		{
		}

		public override int ReadAcquisitionOffset()
		{
			return this.ReadIntParameter(Protocol.ReadAcquisitionOffset);
		}
		
		public override void WriteHardwareVersion(byte[] key, byte major, byte minor)
		{
			this.SetMaintenanceMode(key);
			
			Protocol.Parameter parameter = new Protocol.Parameter(Protocol.WriteHardwareVersion);
			parameter.Count = 2;
			
			
			byte[] buf = new byte[parameter.Count];
			buf[0] = major;
			buf[1] = minor;
			
			parameter.ByteArray = buf;
			
			if (!this.WriteParameter(parameter, 5000))
				throw new SGSException(Catalog.GetString("Cannot write hardware version."), this);
			
		}

		public override void WriteLut(byte[] key, LUT lut)
		{
			this.SetMaintenanceMode(key);
			
			Protocol.Parameter parameter = new Protocol.Parameter(Protocol.WriteLut);
			
			List<byte> buf = new List<byte>();
			buf.AddRange(BitConverter.GetBytes(lut.DateTime.Ticks));
			buf.AddRange(BitConverter.GetBytes(lut.Offset));
			buf.AddRange(BitConverter.GetBytes(lut.AcquisitionOffset));
			buf.AddRange(BitConverter.GetBytes((UInt16)(lut.MinWavelength*10.0)));
			buf.AddRange(BitConverter.GetBytes((UInt16)(lut.MaxWavelength*10.0)));
			buf.AddRange(BitConverter.GetBytes((UInt16)(lut.Increment*10.0)));
			buf.AddRange(BitConverter.GetBytes((UInt16)(lut.ExtensionRange*10.0)));
			
			foreach (float f in lut.Data)
			{
				buf.AddRange(BitConverter.GetBytes((uint)(f*1000f)));
			}
			
			if (buf.Count != parameter.Count)
				throw new SGSException(Catalog.GetString("Unexpected LUT length."), this);
			
			parameter.ByteArray = buf.ToArray();
			
			this.CurrentConfig.BackgroundSpectrumType = Device.BackgroundSpectrumType.None;
			this.CurrentConfig.BackgroundSpectrum = null;
			this.CurrentConfig.DarkIntensityType = Device.DarkIntensityType.None;
			this.CurrentConfig.DarkIntensity = new DarkIntensity(0.0, 0.0);
			this.CurrentConfig.WavelengthCorrection = null;

			if (!this.WriteParameter(parameter, 20000))
				throw new SGSException(Catalog.GetString("Operation failed."), this);
			
			this.lut_timestamp = lut.DateTime;
		}

		protected override void Connect()
		{
			base.Connect();
			this.ReadHardwareProperties();
		}

		public override void WriteHardwareProperties (byte[] key, HardwareProperties hardware_properties)
		{
			this.SetMaintenanceMode(key);

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

			byte[] buf = new byte[parameter.Count];

			if (hardware_properties.Type.Length == 1)
				hardware_properties.Type += " ";

			if (hardware_properties.Type.Length == 2)
			{
				buf[0] = (byte)hardware_properties.Type[0];
				buf[1] = (byte)hardware_properties.Type[1];
			}
			else
			{
				buf[0] = byte.MaxValue;
				buf[1] = byte.MaxValue;
			}
			byte[] tmp = BitConverter.GetBytes((UInt16)(hardware_properties.GridConstant));
			Array.Copy(tmp, 0, buf, 2, tmp.Length);

			tmp = BitConverter.GetBytes((UInt16)(hardware_properties.IncidenceAngle*10.0));
			Array.Copy(tmp, 0, buf, 4, tmp.Length);

			tmp = BitConverter.GetBytes((UInt16)(hardware_properties.DiffractionAngle*10.0));
			Array.Copy(tmp, 0, buf, 6, tmp.Length);

			tmp = BitConverter.GetBytes((Int16)(hardware_properties.MinTemperature*100.0));
			Array.Copy(tmp, 0, buf, 8, tmp.Length);

			tmp = BitConverter.GetBytes((Int16)(hardware_properties.MaxTemperature*100.0));
			Array.Copy(tmp, 0, buf, 10, tmp.Length);

			tmp = BitConverter.GetBytes((UInt16)(hardware_properties.SpectralResolution*10.0));
			Array.Copy(tmp, 0, buf, 12, tmp.Length);

			tmp = BitConverter.GetBytes((UInt16)(hardware_properties.WavelengthDrift*10.0));
			Array.Copy(tmp, 0, buf, 14, tmp.Length);

			tmp = BitConverter.GetBytes((UInt32)(hardware_properties.Sensitivity));
			Array.Copy(tmp, 0, buf, 16, tmp.Length);

			tmp = BitConverter.GetBytes((UInt16)(hardware_properties.SignalNoiseRatio));
			Array.Copy(tmp, 0, buf, 20, tmp.Length);

			tmp = BitConverter.GetBytes((UInt16)(hardware_properties.DynamicRange));
			Array.Copy(tmp, 0, buf, 22, tmp.Length);

			parameter.ByteArray = buf;

			if (!this.WriteParameter(parameter, 20000))
				throw new SGSException(Catalog.GetString("Operation failed."), this);
		}

		internal void ReadHardwareProperties()
		{
			byte[] buf = this.ReadByteArrayParameter(Protocol.ReadDeviceProperties);

			if (buf.Length == 1)
			{
				if (buf[0] == 0)
				{
					this.NotifyWarning(Catalog.GetString("Hardware properties are invalid or not set."));
					this.HardwareProperties.SetInvalid();
					return;
				}
				else
				{
					throw new SGSException(Catalog.GetString("Invalid device response."), this);
				}
			}

			byte type_byte1 = buf[0];
			byte type_byte2 = buf[1];
			if (type_byte1 != byte.MaxValue)
				this.HardwareProperties.Type = string.Format("{0}{1}", (char)type_byte1, (char)type_byte2);

			this.HardwareProperties.GridConstant         = BitConverter.ToUInt16(buf, 2);
			this.HardwareProperties.IncidenceAngle       = BitConverter.ToUInt16(buf, 4) / 10.0;
			this.HardwareProperties.DiffractionAngle     = BitConverter.ToUInt16(buf, 6) / 10.0;
			this.HardwareProperties.MinTemperature       = BitConverter.ToInt16(buf, 8) / 100.0;
			this.HardwareProperties.MaxTemperature       = BitConverter.ToInt16(buf, 10) / 100.0;
			this.HardwareProperties.SpectralResolution   = BitConverter.ToUInt16(buf, 12) / 10.0;
			this.HardwareProperties.WavelengthDrift      = BitConverter.ToUInt16(buf, 14) / 10.0;
			this.HardwareProperties.Sensitivity          = BitConverter.ToUInt32(buf, 16);
			this.HardwareProperties.SignalNoiseRatio     = BitConverter.ToUInt16(buf, 20);
			this.HardwareProperties.DynamicRange         = BitConverter.ToUInt16(buf, 22);
			this.HardwareProperties.MinWavelength        = BitConverter.ToUInt16(buf, 24) / 10.0;
			this.HardwareProperties.MaxWavelength        = BitConverter.ToUInt16(buf, 26) / 10.0;
			this.HardwareProperties.WavelengthIncrement  = BitConverter.ToUInt16(buf, 28) / 10.0;
			this.HardwareProperties.WavelengthExtension  = BitConverter.ToUInt16(buf, 30) / 10.0;
			this.HardwareProperties.MinAverage           = BitConverter.ToUInt16(buf, 32);
			this.HardwareProperties.MaxAverage           = BitConverter.ToUInt16(buf, 34);

			if (buf.Length >= 38)
				this.HardwareProperties.MEMSInfo = BitConverter.ToUInt16(buf, 36);

			if (buf.Length >= 40)
				this.HardwareProperties.AdcConfigurationId = BitConverter.ToUInt16(buf, 38);
		}

		public override void WriteAdcResolution(byte[] key, byte bits)
		{
			this.SetMaintenanceMode(key);

			Protocol.Parameter parameter = new Protocol.Parameter(Protocol.WriteAdcResolution);
			parameter.ByteValue = bits;
			
			if (!this.WriteParameter(parameter))
				throw new SGSException(Catalog.GetString("Operation failed."), this);
		}

		public override byte ReadAdcResolution()
		{
			return this.ReadByteParameter(Protocol.ReadAdcResolution);
		}
	}
}
