// 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_15 : Device0_14
	{
		protected new class Protocol : Device0_14.Protocol
		{
			public static new readonly Parameter FinderPresent                      = new Parameter('F', 'D', typeof(byte[]), 0);
			public static     readonly Parameter WriteFinderOverrideHardwareVersion = new Parameter('F', 'W', typeof(byte[]), 4);
			public static     readonly Parameter ReadFinderHardwareVersion          = new Parameter('F', 'E', typeof(byte[]), 0);
			public static new readonly Parameter WriteI2C                           = new Parameter('I', 'W', typeof(byte[]), 0);
			public static new readonly Parameter ReadI2C                            = new Parameter('I', 'R', typeof(byte[]), 3);
		}
		
		public Device0_15(DeviceInfo info, VersionInfo version_info) : base (info, version_info)
		{
		}
		
		public override void Open(bool auto_init)
		{
			lock (this.opening_mutex)
			{
				base.Open(auto_init);
				
				if (this.IsFinder && auto_init)
					this.ReadFinderHardwareVersion();
			}
		}
		
		// new implemented: error in firmware fixed. device answers 1 in case of success (see 0.14)
		public override void SetFinderWheel(FinderWheelPosition position, IdleTaskHandler idle_task, bool no_wait=false)
		{
			if (!this.IsFinder)
				return;
			
			Protocol.Parameter parameter = new Protocol.Parameter(Protocol.FinderWheel);
			parameter.ByteValue = (byte)position;

			this.FinderWheelPosition = FinderWheelPosition.Moving;
			lock (this)
				this.state_changed = true;

			if (!this.WriteParameter(parameter, 10000, idle_task))
			{
				if (!this.SetState(State.Unknown))
					throw new SGSException(Catalog.GetString("Device not ready."), this);

				this.FinderWheelPosition = FinderWheelPosition.Unknown;
				throw new SGSException(Catalog.GetString("Cannot set finder wheel position: Device returned error."), this);
			}

			this.FinderWheelPosition = position;
            idle_task?.Invoke();
		}
		
		internal override bool FinderPresent()
		{
			byte[] result = this.ReadByteArrayParameter(Protocol.FinderPresent);
			
			if (result == null ||
			    result.Length == 0 ||
			    result.Length == 1 && result[0] != 0)
				throw new SGSException(Catalog.GetString("Cannot determine finder hardware version: Unexpected device response."), this);
			else if (result.Length == 1 && result[0] == 0)
				return false;
			
			if (result.Length != 4)
				throw new SGSException(Catalog.GetString("Cannot determine finder hardware version: Unexpected device response."), this);

			this.finder_version = new FinderVersion(result);
			return true;
		}
		
		public override void WriteFinderOverrideHardwareVersion(byte[] key, byte[] versions)
		{
			if (versions == null || versions.Length != 4)
				throw new ArgumentException(Catalog.GetString("Invalid finder hardware version information."));
			
			this.SetMaintenanceMode(key);

			Protocol.Parameter parameter = new Protocol.Parameter(Protocol.WriteFinderOverrideHardwareVersion);
			
			parameter.Count = (ushort)(versions.Length);
			parameter.ByteArray = versions;
			
			if (!this.WriteParameter(parameter, 5000) || !this.FinderPresent())
				throw new SGSException(Catalog.GetString("Operation failed."), this);
			
			this.FinderVersion = new FinderVersion(this.ReadByteArrayParameter(Protocol.FinderPresent));
		}
		
		public override byte[] ReadFinderHardwareVersion()
		{
			return this.ReadByteArrayParameter(Protocol.ReadFinderHardwareVersion);
		}
		
		public override void WriteI2C(byte[] key, I2CBusId bus_id, byte address, byte[] data)
		{
			if (data == null || data.Length == 0 || data.Length + 1 > ushort.MaxValue)
				throw new ArgumentException(Catalog.GetString("Unexpected I2C parameter count."));
			
			this.SetMaintenanceMode(key);

			Protocol.Parameter parameter = new Protocol.Parameter(Protocol.WriteI2C);
			parameter.Count = (ushort)(2 + data.Length);
			
			byte[] buf = new byte[data.Length+1];
			buf[0] = (byte)bus_id;
			buf[1] = address;
			Array.Copy(data, 0, buf, 2, data.Length);
			
			parameter.ByteArray = buf;
			
			if (!this.WriteParameter(parameter, 5000))
				throw new SGSException(Catalog.GetString("I2C write operation failed."), this);
		}
		
		public override byte[] ReadI2C(byte[] key, I2CBusId bus_id, byte address, byte count)
		{
			this.SetMaintenanceMode(key);
			
			Protocol.Parameter parameter = new Protocol.Parameter(Protocol.ReadI2C);
			parameter.ByteArray    = new byte[] {(byte)bus_id, address, count};
			parameter.QueryHasData = true;
			byte[] buf = this.ReadByteArrayParameter(parameter, 5000);
			
			if (buf.Length == 0)
				throw new SGSException(Catalog.GetString("I2C read operation failed."), this);
			
			return buf;
		}
		
		protected override void ReadSpectrum(out uint[] add_data, out double[] lambda, out double[] intensity, IdleTaskHandler idle_task)
		{
			uint count = 0;

			TimeSpan span = new TimeSpan(0, 0, 2);

			while (count++ < Device.ReadSpectrumTryCount)
			{
				DateTime then = DateTime.Now;

				base.ReadSpectrum(out add_data, out lambda, out intensity, idle_task);
				
				if (!double.IsInfinity(this.AcquisitionInfo.MirrorFreq))
				{
					Array.Resize(ref add_data, add_data.Length+1);
					add_data[add_data.Length-1] = count;
					return;
				}

				while (DateTime.Now - then < span)
				{
					System.Threading.Thread.Sleep(100);
				}
				
				Console.WriteLine("Device returned invalid spectrum. Try again ({0}).", count);
			}
			
			intensity = new double[0];
			throw new InvalidSpectrumException(Catalog.GetString("Device returned invalid spectrum."), this);
		}
	}
}
