// 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.Text;

using Hiperscan.Spectroscopy;
using Hiperscan.Unix;


namespace Hiperscan.SGS
{
	internal class Device0_10 : Device0_0
	{
		
		protected new class Protocol
		{
			public static readonly int HeaderLength = 4;
			
			public static readonly Parameter FinderLight     = new Parameter('F', 'L', typeof(byte), sizeof(byte));
			public static readonly Parameter FinderWheel     = new Parameter('F', 'P', typeof(byte), sizeof(byte));
			public static readonly Parameter FanControl      = new Parameter('F', 'F', typeof(byte), sizeof(byte));
			public static readonly Parameter FinderPresent   = new Parameter('F', 'D', typeof(byte), sizeof(byte));
			public static readonly Parameter FinderStatusLED = new Parameter('F', 'B', typeof(byte), sizeof(byte));
			public static readonly Parameter FinderButton    = new Parameter('F', 'S', typeof(byte), sizeof(byte));
		
			public class Parameter
			{
				private byte[] word;
				private ushort count;
				private Type   type;
				private bool   query_has_data = false;
				
				public Parameter(char c1, char c2, Type type, int count)
				{
					this.type  = type;
					this.count = (ushort)count;
					this.word  = new Byte[4 + count];
					
					this.word[0] = (byte)c1;
					this.word[1] = (byte)c2;
					this.word[2] = BitConverter.GetBytes(count)[0];
					this.word[3] = BitConverter.GetBytes(count)[1];
				}
				
				public Parameter(Parameter p)
				{
					this.type  = p.Type;
					this.count = p.Count;
					this.word  = (byte[])p.Word.Clone();
				}
				
				public Parameter(byte[] buf, Type type)
				{
					this.type  = type;
					this.count = (ushort)(buf[2] + (buf[3] << 8));
					
					if (this.count != buf.Length-4)
						throw new Exception(Catalog.GetString("Unexpected data length."));
					
					this.word = (byte[])buf.Clone();
				}
				
				public bool CheckResponse(Parameter response, bool data_request)
				{
					if (this.word[0] != response.Word[0] ||
					    this.word[1] != response.Word[1])
						throw new Exception(Catalog.GetString("Unexpected device response (command ID)."));
					
					int count = response.Word[2] + (response.Word[3] << 8);

					if (data_request && count < 1)
						throw new Exception(Catalog.GetString("Unexpected device response (byte count).)"));
					
					if (data_request)
						return true;
					
					if (count != 1)
						throw new Exception(Catalog.GetString("Unexpected device response (byte count)."));

					if (response[4] > 1)
						throw new Exception(Catalog.GetString("Unexpected device response (return value)."));
					
					return (response[4] == 1);
				}
				
				public byte ByteValue
				{
					get
					{
						if (this.type != typeof(byte))
							throw new Exception(Catalog.GetString("Parameter data type is not 'byte'."));
						
						return this.word[4];
					}
					set
					{
						if (this.type != typeof(byte))
							throw new Exception(Catalog.GetString("Parameter data type is not 'byte'."));
						
						this.word[4] = value;
					}
				}
				
				public byte ByteValue2
				{
					get
					{
						if (this.type != typeof(byte))
							throw new Exception(Catalog.GetString("Parameter data type is not 'byte'."));
						
						return this.word[5];
					}
					set
					{
						if (this.type != typeof(byte))
							throw new Exception(Catalog.GetString("Parameter data type is not 'byte'."));
						
						this.word[5] = value;
					}
				}
				
				public short ShortValue
				{
					get
					{
						if (this.type != typeof(short))
							throw new Exception(Catalog.GetString("Parameter data type is not 'short'."));
						
						return BitConverter.ToInt16(this.word, 4);
					}
					set
					{
						if (this.type != typeof(short))
							throw new Exception(Catalog.GetString("Parameter data type is not 'short'."));
						
						this.word[4] = BitConverter.GetBytes(value)[0];
						this.word[5] = BitConverter.GetBytes(value)[1];
					}
				}

				public ushort UShortValue
				{
					get
					{
						if (this.type != typeof(ushort))
							throw new Exception(Catalog.GetString("Parameter data type is not 'ushort'."));
						
						return BitConverter.ToUInt16(this.word, 4);
					}
					set
					{
						if (this.type != typeof(ushort))
							throw new Exception(Catalog.GetString("Parameter data type is not 'ushort'."));
						
						this.word[4] = BitConverter.GetBytes(value)[0];
						this.word[5] = BitConverter.GetBytes(value)[1];
					}
				}
				
				public ushort UShortValue2
				{
					get
					{
						if (this.type != typeof(ushort))
							throw new Exception(Catalog.GetString("Parameter data type is not 'ushort'."));
						
						return BitConverter.ToUInt16(this.word, 6);
					}
					set
					{
						if (this.type != typeof(ushort))
							throw new Exception(Catalog.GetString("Parameter data type is not 'ushort'."));
						
						this.word[6] = BitConverter.GetBytes(value)[0];
						this.word[7] = BitConverter.GetBytes(value)[1];
					}
				}
				
				public int IntValue
				{
					get
					{
						if (this.type != typeof(int))
							throw new Exception(Catalog.GetString("Parameter data type is not 'int'."));
						
						return BitConverter.ToInt32(this.word, 4);
					}
					set
					{
						if (this.type != typeof(int))
							throw new Exception(Catalog.GetString("Parameter data type is not 'int'."));
						
						this.word[4] = BitConverter.GetBytes(value)[0];
						this.word[5] = BitConverter.GetBytes(value)[1];
						this.word[6] = BitConverter.GetBytes(value)[2];
						this.word[7] = BitConverter.GetBytes(value)[3];
					}
				}
				
				public uint UIntValue
				{
					get
					{
						if (this.type != typeof(uint))
							throw new Exception(Catalog.GetString("Parameter data type is not 'uint'."));
						
						return BitConverter.ToUInt32(this.word, 4);
					}
					set
					{
						if (this.type != typeof(uint))
							throw new Exception(Catalog.GetString("Parameter data type is not 'uint'."));
						
						this.word[4] = BitConverter.GetBytes(value)[0];
						this.word[5] = BitConverter.GetBytes(value)[1];
						this.word[6] = BitConverter.GetBytes(value)[2];
						this.word[7] = BitConverter.GetBytes(value)[3];
					}
				}
				
				public uint UIntValue2
				{
					get
					{
						if (this.type != typeof(uint))
							throw new Exception(Catalog.GetString("Parameter data type is not 'uint'."));
						
						return BitConverter.ToUInt32(this.word, 8);
					}
					set
					{
						if (this.type != typeof(uint))
							throw new Exception(Catalog.GetString("Parameter data type is not 'uint'."));
						
						this.word[8]  = BitConverter.GetBytes(value)[0];
						this.word[9]  = BitConverter.GetBytes(value)[1];
						this.word[10] = BitConverter.GetBytes(value)[2];
						this.word[11] = BitConverter.GetBytes(value)[3];
					}
				}
				
				public long LongValue
				{
					get
					{
						if (this.type != typeof(long))
							throw new Exception(Catalog.GetString("Parameter data type is not 'long'."));
						
						return BitConverter.ToInt64(this.word, 4);
					}
					set
					{
						if (this.type != typeof(long))
							throw new Exception(Catalog.GetString("Parameter data type is not 'long'."));
						
						this.word[4]  = BitConverter.GetBytes(value)[0];
						this.word[5]  = BitConverter.GetBytes(value)[1];
						this.word[6]  = BitConverter.GetBytes(value)[2];
						this.word[7]  = BitConverter.GetBytes(value)[3];
						this.word[8]  = BitConverter.GetBytes(value)[4];
						this.word[9]  = BitConverter.GetBytes(value)[5];
						this.word[10] = BitConverter.GetBytes(value)[6];
						this.word[11] = BitConverter.GetBytes(value)[7];
					}
				}

				public string StringValue
				{
					get
					{
						if (this.type != typeof(string))
							throw new Exception(Catalog.GetString("Parameter data type is not 'string'."));

						return ASCIIEncoding.Default.GetString(this.word, Protocol.HeaderLength,
						                                       this.word.Length - Protocol.HeaderLength);
					}
					set
					{
						if (this.type != typeof(String))
							throw new Exception(Catalog.GetString("Parameter data type is not 'string'."));
						
						if (this.count != 0 && value.Length != this.count)
							throw new Exception(Catalog.GetString("Unexpected string length."));

						byte[] buf = ASCIIEncoding.Default.GetBytes(value);
						Buffer.BlockCopy(buf, 0, this.word, Protocol.HeaderLength, value.Length);
					}
				}
				
				public byte[] ByteArray
				{
					get
					{
						if (this.type != typeof(byte[]))
							throw new Exception(Catalog.GetString("Parameter data type is not 'byte[]'."));
						
						byte[] buf = new byte[this.word.Length - Protocol.HeaderLength];
						Buffer.BlockCopy(this.word, Protocol.HeaderLength, buf, 0, buf.Length);
						return buf;
					}
					set
					{
						if (this.type != typeof(byte[]))
							throw new Exception(Catalog.GetString("Parameter data type is not 'byte[]'."));
						
						if (this.count != 0 && value.Length != this.count)
							throw new Exception(Catalog.GetString("Parameter data array has unexpected length."));
						
//						if (this.count == 0)
//						{
//							Array.Resize(ref this.word, this.word.Length + value.Length);
//							this.count = value.Length;
//						}
						
						Buffer.BlockCopy(value, 0, this.word, Protocol.HeaderLength, value.Length);
					}
				}
				
				public short[] ShortArray
				{
					get
					{
						if (this.type != typeof(short[]))
							throw new Exception(Catalog.GetString("Parameter data type is not 'short[]'."));
						
						short[] buf = new short[(this.word.Length - Protocol.HeaderLength) / 2];
						Buffer.BlockCopy(this.word, Protocol.HeaderLength, buf, 0, buf.Length*2);
						return buf;
					}
					set
					{
						if (this.type != typeof(short[]))
							throw new Exception(Catalog.GetString("Parameter data type is not 'short[]'."));
						
						if (this.count != 0 && value.Length != this.count / 2)
							throw new Exception(Catalog.GetString("Parameter data array has unexpected length."));
						
						Buffer.BlockCopy(value, 0, this.word, Protocol.HeaderLength, value.Length*2);
					}
				}

				public ushort[] UShortArray
				{
					get
					{
						if (this.type != typeof(ushort[]))
							throw new Exception(Catalog.GetString("Parameter data type is not 'ushort[]'."));
						
						ushort[] buf = new ushort[(this.word.Length - Protocol.HeaderLength) / 2];
						Buffer.BlockCopy(this.word, Protocol.HeaderLength, buf, 0, buf.Length*2);
						return buf;
					}
					set
					{
						if (this.type != typeof(ushort[]))
							throw new Exception(Catalog.GetString("Parameter data type is not 'ushort[]'."));
						
						if (this.count != 0 && value.Length != this.count / 2)
							throw new Exception(Catalog.GetString("Parameter data array has unexpected length."));
						
						Buffer.BlockCopy(value, 0, this.word, Protocol.HeaderLength, value.Length*2);
					}
				}

				public int[] IntArray
				{
					get
					{
						if (this.type != typeof(int[]))
							throw new Exception(Catalog.GetString("Parameter data type is not 'int[]'."));
						
						int[] buf = new int[(this.word.Length - Protocol.HeaderLength) / 4];
						Buffer.BlockCopy(this.word, Protocol.HeaderLength, buf, 0, buf.Length*4);
						return buf;
					}
					set
					{
						if (this.type != typeof(int[]))
							throw new Exception(Catalog.GetString("Parameter data type is not 'int[]'."));
					
						if (this.count != 0 && value.Length != this.count / 4)
							throw new Exception(Catalog.GetString("Parameter data array has unexpected length."));
						
						Buffer.BlockCopy(value, 0, this.word, Protocol.HeaderLength, value.Length*4);
					}
				}

				public uint[] UIntArray
				{
					get
					{
						if (this.type != typeof(uint[]))
							throw new Exception(Catalog.GetString("Parameter data type is not 'uint[]'."));

						uint[] buf = new uint[(this.word.Length - Protocol.HeaderLength) / 4];
						Buffer.BlockCopy(this.word, Protocol.HeaderLength, buf, 0, buf.Length*4);
						return buf;
					}
					set
					{
						if (this.type != typeof(uint[]))
							throw new Exception(Catalog.GetString("Parameter data type is not 'uint[]'."));

						if (this.count != 0 && value.Length != this.count / 4)
							throw new Exception(Catalog.GetString("Parameter data array has unexpected length."));

						Buffer.BlockCopy(value, 0, this.word, Protocol.HeaderLength, value.Length*4);
					}
				}

				public byte[] Word
				{
					get { return this.word; }
				}
				
				public byte[] Query
				{
					get
					{
						byte[] buf = new byte[4];
						Array.Copy(this.word, buf, 2);
						return buf;
					}
				}
				
				public bool QueryHasData
				{
					get { return this.query_has_data;  }
					set { this.query_has_data = value; }
				}
				
				public Type Type
				{
					get { return this.type; }
				}
				
				public ushort Count
				{
					get { return this.count; }
					internal set 
					{ 
						this.count = value; 
						byte[] new_word  = new Byte[4 + this.count];
						new_word[0] = this.word[0];
						new_word[1] = this.word[1];
						new_word[2] = BitConverter.GetBytes(this.count)[0];
						new_word[3] = BitConverter.GetBytes(this.count)[1];
						this.word = new_word;
					}
				}
				
				public byte this[int ix]
				{
					get { return this.word[ix]; }
				}
				
				public static bool operator==(Parameter lhs, Parameter rhs)
				{
					if (lhs.Word.Length != rhs.Word.Length)
						return false;
					
					for (int ix=0; ix < lhs.Word.Length; ++ix)
					{
						if (lhs.Word[ix] != rhs.Word[ix])
							return false;
					}
					
					return true;
				}
				
				public static bool operator!=(Parameter lhs, Parameter rhs)
				{
					return !(lhs == rhs);
				}
				
				public override bool Equals(object o)
				{
					try
					{
						return (this == (Parameter)o);
					}
					catch
					{
						return false;
					}
				}
				
				public override int GetHashCode()
				{
					return this[0].GetHashCode() ^ this[1].GetHashCode();
				}
			}
		}
		
		public Device0_10(DeviceInfo info, VersionInfo version_info) : base (info, version_info)
		{
		}
		
		internal override bool FinderPresent()
		{
			return this.ReadByteParameter(Protocol.FinderPresent) == 1;
		}
		
		public override bool FinderButtonPressed()
		{
			if (!this.is_finder)
				return false;
			
			if (!this.IsIdle)
				return false;
			
			return this.ReadByteParameter(Protocol.FinderButton) == 1;
		}
		
		public override void SwitchFinderStatusLED(bool green)
		{
			if (!this.IsFinder)
				return;
			
			if (this.AssumedFinderLEDState == green)
				return;

			Protocol.Parameter parameter = new Protocol.Parameter(Protocol.FinderStatusLED);
			if (green)
				parameter.ByteValue = 1;
			else
				parameter.ByteValue = 0;
			this.WriteParameter(parameter);
			
			this.assumed_finder_led_state = green;
		}
				
		public override void SetFinderLight(bool on, IdleTaskHandler idle_task)
		{
			if (!this.IsFinder || this.GetState() == Device.State.Unknown)
				return;

			Console.WriteLine("Switching finder light: " + on);
			
			Protocol.Parameter parameter = new Protocol.Parameter(Protocol.FinderLight);
			if (on)
				parameter.ByteValue = 1;
			else
				parameter.ByteValue = 0;
			
			this.SetState(State.Waiting);
			if (this.WriteParameter(parameter) == false)
				throw new SGSException(Catalog.GetString("Cannot switch light source: Device returned error."), this);

			this.FinderLampStatus = on;
			lock (this)
				this.state_changed = true;
			
			if (idle_task != null)
				idle_task();
			
			if (on)
				Thread.Sleep((int)this.LightSourceDelay);
			
			this.SetState(State.Idle);
		}
				
		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))
				throw new SGSException(Catalog.GetString("Cannot set finder wheel position: Device returned error."), this);

			this.FinderWheelPosition = position;

            if (no_wait == false)
            {
                this.SetState(State.Waiting);
                idle_task?.Invoke();
                Thread.Sleep(200);
            }

            this.SetState(State.Idle);
		}
				
		public override void SetFinderFanSpeed(bool on)
		{
			if (!this.IsFinder)
				return;
			
			Protocol.Parameter parameter = new Protocol.Parameter(Protocol.FanControl);
			if (on)
				parameter.ByteValue = 1;
			else
				parameter.ByteValue = 0;
			this.WriteParameter(parameter);
		}
		
		protected bool WriteParameter(Protocol.Parameter parameter)
		{
			return this.WriteParameter(parameter, this.CurrentConfig.Timeout, null);
		}
		
		protected bool WriteParameter(Protocol.Parameter parameter, int timeout)
		{
			return this.WriteParameter(parameter, timeout, null);
		}
		
		protected virtual bool WriteParameter(Protocol.Parameter parameter, int timeout, IdleTaskHandler idle_task)
		{
			try
			{
				Device.State sav_state = this.GetState();
				
				if (!this.SetState(State.TxD))
					throw new SGSException(Catalog.GetString("Device not ready."), this);
				
				this.PrintCommandInfo(parameter.Word, true);
				uint result = this.Write(parameter.Word);

				this.SetState(sav_state);
				
				if (result != parameter.Word.Length)
					throw new SGSException(Catalog.GetString("Transmission interrupted."), this);
				
				byte[] rx_buf = this.ReadResponse(timeout, idle_task);
				Protocol.Parameter response = new Protocol.Parameter(rx_buf, parameter.Type);
				
				if (parameter != response)
					throw new SGSException(Catalog.GetString("Wrong device response."), this);
				
				return true;
			}
			catch (Exception ex)
			{
				this.ExceptionHandler(Catalog.GetString("Cannot transmit parameter:") + " " + ex.Message, ex);
				return false;
			}
		}
		
		protected byte[] ReadResponse(int timeout, IdleTaskHandler idle_task)
		{
			State sav_state = this.GetState();
			
			if (!this.SetState(State.RxD))
				throw new SGSException(Catalog.GetString("Device not ready."), this);
			
			byte[] header = this.ReadBlocking((uint)Protocol.HeaderLength, timeout, idle_task);
			ushort count  = (ushort)(header[2] + (header[3] << 8));

			byte[] data = this.ReadBlocking(count, timeout, idle_task);
			
			byte[] rx_buf = new byte[Protocol.HeaderLength+count];
			Array.Copy(header, rx_buf, Protocol.HeaderLength);
			Array.Copy(data, 0, rx_buf, Protocol.HeaderLength, data.Length);
			
			this.PrintCommandInfo(rx_buf, false);
			
			this.SetState(sav_state);
				
			return rx_buf;
		}

		protected Protocol.Parameter ReadParameter(Protocol.Parameter parameter)
		{
			return this.ReadParameter(parameter, this.CurrentConfig.Timeout);
		}
		
		protected Protocol.Parameter ReadParameter(Protocol.Parameter parameter, int timeout)
		{
			return this.ReadParameter(parameter, timeout, null);
		}
		
		protected virtual Protocol.Parameter ReadParameter(Protocol.Parameter parameter, int timeout, IdleTaskHandler idle_task)
		{
			if (!this.SetState(State.TxD))
				throw new SGSException(Catalog.GetString("Device not ready."), this);
			
			//this.NotifyStateChanges();

			this.PrintCommandInfo(parameter.Query, true);
			uint result = this.Write(parameter.Query);
			
			this.SetState(State.Idle);
				
			if (result != parameter.Query.Length)
				throw new SGSException(Catalog.GetString("Transmission interrupted."), this);
				
			byte[] rx_buf = this.ReadResponse(timeout, idle_task);

			if ((parameter.Type == typeof(short)  ||
			     parameter.Type == typeof(ushort) ||
			     parameter.Type == typeof(int)    ||
			     parameter.Type == typeof(uint)   ||
			     parameter.Type == typeof(long)   ||
			     parameter.Type == typeof(ulong)) &&
			    rx_buf.Length == Protocol.HeaderLength + 1 &&
			    rx_buf[Protocol.HeaderLength] == 0)
				throw new SGSException(Catalog.GetString("Device retuned error status. The requested operation failed."), this);

			return new Protocol.Parameter(rx_buf, parameter.Type);
		}
		
		protected byte ReadByteParameter(Protocol.Parameter parameter)
		{
			try
			{
				if (parameter.Type != typeof(byte))
					throw new SGSException(Catalog.GetString("Parameter data type 'byte' expected."), this);
				
				return this.ReadParameter(parameter).ByteValue;
			}
			catch (Exception ex)
			{
				this.ExceptionHandler(Catalog.GetString("Cannot receive byte parameter:") + " " + ex.Message, ex);
				return 0;
			}
		}

		protected short ReadShortParameter(Protocol.Parameter parameter)
		{
			try
			{
				if (parameter.Type != typeof(short))
					throw new SGSException(Catalog.GetString("Parameter data type 'short' expected."), this);
				
				return this.ReadParameter(parameter).ShortValue;
			}
			catch (Exception ex)
			{
				this.ExceptionHandler(Catalog.GetString("Cannot receive short parameter:") + " " + ex.Message, ex);
				return 0;
			}
		}

		protected ushort ReadUShortParameter(Protocol.Parameter parameter)
		{
			try
			{
				if (parameter.Type != typeof(ushort))
					throw new SGSException(Catalog.GetString("Parameter data type 'ushort' expected."), this);
				
				return this.ReadParameter(parameter).UShortValue;
			}
			catch (Exception ex)
			{
				this.ExceptionHandler(Catalog.GetString("Cannot receive ushort parameter:") + " " + ex.Message, ex);
				return 0;
			}
		}
		
		protected int ReadIntParameter(Protocol.Parameter parameter)
		{
			try
			{
				if (parameter.Type != typeof(int))
					throw new SGSException(Catalog.GetString("Parameter data type 'int' expected."), this);
				
				return this.ReadParameter(parameter).IntValue;
			}
			catch (Exception ex)
			{
				this.ExceptionHandler(Catalog.GetString("Cannot receive uint parameter:") + " " + ex.Message, ex);
				return 0;
			}
		}
		
		protected uint ReadUIntParameter(Protocol.Parameter parameter)
		{
			try
			{
				if (parameter.Type != typeof(uint))
					throw new SGSException(Catalog.GetString("Parameter data type 'uint' expected."), this);
				
				return this.ReadParameter(parameter).UIntValue;
			}
			catch (Exception ex)
			{
				this.ExceptionHandler(Catalog.GetString("Cannot receive uint parameter:") + " " + ex.Message, ex);
				return 0;
			}
		}
		
		protected long ReadLongParameter(Protocol.Parameter parameter)
		{
			try
			{
				if (parameter.Type != typeof(long))
					throw new SGSException(Catalog.GetString("Parameter data type 'long' expected."), this);
				
				return this.ReadParameter(parameter).LongValue;
			}
			catch (Exception ex)
			{
				this.ExceptionHandler(Catalog.GetString("Cannot receive long parameter:") + " " + ex.Message, ex);
				return 0;
			}
		}

		protected string ReadStringParameter(Protocol.Parameter parameter)
		{
			try
			{
				if (parameter.Type != typeof(string))
					throw new SGSException(Catalog.GetString("Parameter data type 'string' expected."), this);
				
				return this.ReadParameter(parameter).StringValue;
			}
			catch (Exception ex)
			{
				this.ExceptionHandler(Catalog.GetString("Cannot receive string parameter:") + " " + ex.Message, ex);
				return null;
			}
		}
		
		protected byte[] ReadByteArrayParameter(Protocol.Parameter parameter)
		{
			return this.ReadByteArrayParameter(parameter, this.CurrentConfig.Timeout);
		}
		
		protected byte[] ReadByteArrayParameter(Protocol.Parameter parameter, int timeout)
		{
			try
			{
				if (parameter.Type != typeof(byte[]))
					throw new SGSException(Catalog.GetString("Parameter data type 'byte[]' expected."), this);
				
				return this.ReadParameter(parameter, timeout).ByteArray;
			}
			catch (Exception ex)
			{
				this.ExceptionHandler(Catalog.GetString("Cannot receive byte array parameter:") + " " + ex.Message, ex);
				return null;
			}
		}
		
		protected short[] ReadShortArrayParameter(Protocol.Parameter parameter)
		{
			return this.ReadShortArrayParameter(parameter, this.CurrentConfig.Timeout);
		}
		
		protected short[] ReadShortArrayParameter(Protocol.Parameter parameter, int timeout)
		{
			try
			{
				if (parameter.Type != typeof(short[]))
					throw new SGSException(Catalog.GetString("Parameter data type 'short[]' expected."), this);
				
				return this.ReadParameter(parameter, timeout).ShortArray;
			}
			catch (Exception ex)
			{
				this.ExceptionHandler(Catalog.GetString("Cannot receive short array parameter:") + " " + ex.Message, ex);
				return null;
			}
		}
		
		protected ushort[] ReadUShortArrayParameter(Protocol.Parameter parameter)
		{
			return this.ReadUShortArrayParameter(parameter, this.CurrentConfig.Timeout);
		}
		
		protected ushort[] ReadUShortArrayParameter(Protocol.Parameter parameter, int timeout)
		{
			try
			{
				if (parameter.Type != typeof(ushort[]))
					throw new SGSException(Catalog.GetString("Parameter data type 'ushort[]' expected."), this);
				
				return this.ReadParameter(parameter, timeout).UShortArray;
			}
			catch (Exception ex)
			{
				this.ExceptionHandler(Catalog.GetString("Cannot receive unsigned short array parameter:") + " " + ex.Message, ex);
				return null;
			}
		}
		
		protected int[] ReadIntArrayParameter(Protocol.Parameter parameter)
		{
			return this.ReadIntArrayParameter(parameter, this.CurrentConfig.Timeout);
		}
		
		protected int[] ReadIntArrayParameter(Protocol.Parameter parameter, int timeout)
		{
			try
			{
				if (parameter.Type != typeof(int[]))
					throw new SGSException(Catalog.GetString("Parameter data type 'int[]' expected."), this);
				
				return this.ReadParameter(parameter, timeout).IntArray;
			}
			catch (Exception ex)
			{
				this.ExceptionHandler(Catalog.GetString("Cannot receive int array parameter:") + " " + ex.Message, ex);
				return null;
			}
		}
		
		protected uint[] ReadUIntArrayParameter(Protocol.Parameter parameter)
		{
			return this.ReadUIntArrayParameter(parameter, this.CurrentConfig.Timeout);
		}

		protected uint[] ReadUIntArrayParameter(Protocol.Parameter parameter, int timeout)
		{
			try
			{
				if (parameter.Type != typeof(uint[]))
					throw new SGSException(Catalog.GetString("Parameter data type 'uint[]' expected."), this);

				return this.ReadParameter(parameter, timeout).UIntArray;
			}
			catch (Exception ex)
			{
				this.ExceptionHandler(Catalog.GetString("Cannot receive uint array parameter:") + " " + ex.Message, ex);
				return null;
			}
		}

		protected void PrintCommandInfo(byte[] word, bool write)
		{
			if (write)
				Console.Write("Write command:");
			else
				Console.Write("Read response:");
			
			foreach (byte b in word)
				Console.Write(" 0x{0:X2}", b);
			Console.WriteLine("\n\t=> {0} {1} {2} ...", (char)word[0], (char)word[1], (ushort)(word[2] + (word[3] << 8)));
		}
	}
}
