// 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_20 : Device0_19
	{
		protected new class Protocol : Device0_19.Protocol
		{
			public static readonly Parameter RawReadEEPROM         = new Parameter('E', 'L', typeof(byte[]), 4);
			public static readonly Parameter WriteFinderPCBCommand = new Parameter('G', 'F', typeof(byte[]), 0);
			public static readonly Parameter ReadSystemStats       = new Parameter('C', 'G', typeof(uint[]), 0);
			public static readonly Parameter ResetSystemStats      = new Parameter('C', 'S', typeof(byte), sizeof(byte));
			public static readonly Parameter SetFinderStatus       = new Parameter('S', 'U', typeof(byte), sizeof(byte));
			public static readonly Parameter WriteFinderIdleTime   = new Parameter('S', 'I', typeof(uint), sizeof(uint));
			public static readonly Parameter ReadFinderIdleTime    = new Parameter('G', 'I', typeof(uint), 0);
            public static readonly Parameter WriteSessionTimeout   = new Parameter('A', 'T', typeof(ushort), sizeof(ushort));
			public static readonly Parameter WriteOemSerialNumber  = new Parameter('Q', 'W', typeof(string),  20*sizeof(byte));
			public static readonly Parameter ReadOemSerialNumber   = new Parameter('Q', 'R', typeof(string),  20*sizeof(byte));

			public static new readonly Parameter ReadStatus        = new Parameter('S', 'T', typeof(short[]), 0);
		}

		public Device0_20(DeviceInfo info, VersionInfo version_info) : base (info, version_info)
		{
		}

		internal override byte[] RawReadEEPROM(byte[] key, UInt16 address, UInt16 count)
		{
			byte[] buf = new byte[4];

			Array.Copy(BitConverter.GetBytes(address), 0, buf, 0, sizeof(UInt16));
			Array.Copy(BitConverter.GetBytes(count)  , 0, buf, 2, sizeof(UInt16));

			this.SetMaintenanceMode(key);
			Protocol.Parameter parameter = new Protocol.Parameter(Protocol.RawReadEEPROM);
			parameter.ByteArray = buf;
			parameter.QueryHasData = true;

			return this.ReadByteArrayParameter(parameter, 0);
		}
		
		public override void WriteFinderPCBCommand(byte[] command)
		{
			if (!this.IsFinder)
				throw new NotImplementedException(Catalog.GetString("Not supported by hardware."));

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

			parameter.Count = (ushort)(command.Length);
			parameter.ByteArray = command;

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

		public override SystemStats ReadSystemStats()
		{
			if (!this.IsFinder)
				throw new NotImplementedException(Catalog.GetString("Not supported by hardware."));

			uint[] stats = this.ReadUIntArrayParameter(Protocol.ReadSystemStats);
			return new SystemStats(stats);
		}

		public override void ResetSystemOperatingHours(byte[] key)
		{
			if (!this.IsFinder)
				throw new NotImplementedException(Catalog.GetString("Not supported by hardware."));

			this.SetMaintenanceMode(key);

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

		public override void ResetLightSourceStats()
		{
			if (!this.IsFinder)
				throw new NotImplementedException(Catalog.GetString("Not supported by hardware."));

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

		public override void SetFinderStatus(FinderStatusType status)
		{
			if (!this.IsFinder)
				throw new NotImplementedException(Catalog.GetString("Not supported by hardware."));

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

		internal override bool FinderPresent()
		{
			byte[] result = null;

            for (int try_count=0; try_count < 15; ++try_count)
			{
				result = this.ReadByteArrayParameter(Protocol.FinderPresent);

				if (result == null || result.Length == 0)
					throw new SGSException(Catalog.GetString("Cannot determine finder hardware version: Unexpected device response."), this);
				
				if (result.Length == 1 && result[0] != 0)
				{
					System.Threading.Thread.Sleep(200);
					continue;
				}
				else if (result.Length == 1 && result[0] == 0)
				{
					return false;
				}
                else
                {
                    break;
                }
			}

			if (result.Length < 8)
				throw new SGSException(Catalog.GetString("Cannot determine finder hardware version: Unexpected device response."), this);

			this.finder_version = new FinderVersion(result);
			return true;
		}

		protected override void Connect()
		{
			base.Connect();
			if (this.IsFinder)
				this.SetFinderStatus(FinderStatusType.Idle);
		}

		public override void WriteFinderIdleTime(uint idle_seconds)
		{
			if (!this.IsFinder)
				return;

			Protocol.Parameter parameter = new Protocol.Parameter(Protocol.WriteFinderIdleTime);
			parameter.UIntValue = idle_seconds;

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

		public override uint ReadFinderIdleTime()
		{
			if (!this.IsFinder)
				return 0;

			return this.ReadUIntParameter(Protocol.ReadFinderIdleTime);
		}

        public override void WriteSessionTimeout(ushort timeout_seconds)
		{
			if (!this.IsFinder)
				return;

			Protocol.Parameter parameter = new Protocol.Parameter(Protocol.WriteSessionTimeout);
            parameter.UShortValue = timeout_seconds;

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

		public override double ReadStatus()
		{
			short[] status = this.ReadShortArrayParameter(Protocol.ReadStatus);
			short temperature = status[0];
			this.sgs_firmware_status = (SGSFirmwareStatusType)status[1];

			if (temperature == short.MaxValue)
				throw new NotImplementedException(Catalog.GetString("Not supported by hardware."));

			return (double)temperature / 100.0;
		}

		public override void WriteOemSerialNumber(string serial)
		{
			Protocol.Parameter parameter = new Protocol.Parameter(Protocol.WriteOemSerialNumber);

			if (serial.Length > parameter.Count)
				throw new SGSException(Catalog.GetString("The OEM serial string is too long."), this);

			for (int ix=serial.Length; ix < parameter.Count; ++ix)
			{
				serial += " ";
			}

			parameter.StringValue = serial;
			this.WriteParameter(parameter, 5000);
		}

		public override string ReadOemSerialNumber()
		{
			string serial = this.ReadStringParameter(Protocol.ReadOemSerialNumber);
			return serial.Trim();
		}
	}
}
