// 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.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;

using Hiperscan.Spectroscopy;
using Hiperscan.Unix;


namespace Hiperscan.SGS
{
	internal class Device0_11 : Device0_10
	{
		
		protected new class Protocol : Device0_10.Protocol
		{
			public static readonly Parameter MaintenanceMode     = new Parameter('M', 'M', typeof(byte[]), 20*sizeof(byte));
			public static readonly Parameter WriteSerialNumber   = new Parameter('S', 'W', typeof(string),  8*sizeof(byte));
			public static readonly Parameter ReadSerialNumber    = new Parameter('S', 'R', typeof(string),  8*sizeof(byte));
			public static readonly Parameter WriteDeviceName     = new Parameter('N', 'W', typeof(string), 20*sizeof(byte));
			public static readonly Parameter ReadDeviceName      = new Parameter('N', 'R', typeof(string), 20*sizeof(byte));
			public static readonly Parameter WriteMasterKey      = new Parameter('G', 'W', typeof(byte[]), KeyPadSize*sizeof(byte));
			public static readonly Parameter ReadMasterKey       = new Parameter('G', 'R', typeof(byte[]), KeySize*sizeof(byte));
			public static readonly Parameter WriteIndividualKey  = new Parameter('K', 'W', typeof(byte[]), KeyPadSize*sizeof(byte));
			public static readonly Parameter ReadIndividualKey   = new Parameter('K', 'R', typeof(byte[]), KeySize*sizeof(byte));
			public static readonly Parameter ReadFirmwareVersion = new Parameter('V', 'R', typeof(ushort), sizeof(ushort));
		}
		
		public Device0_11(DeviceInfo info, VersionInfo version_info) : base (info, version_info)
		{
		}
		
		public override int GetKeyPadSize()
		{
			return Device0_11.KeyPadSize;
		}
		
		public override void SetMaintenanceMode(byte[] key)
		{
			Protocol.Parameter parameter = new Protocol.Parameter(Protocol.MaintenanceMode);
			parameter.ByteArray = key;
			this.WriteParameter(parameter);
		}

		public override void WriteSerialNumber(byte[] key, string serial)
		{
			this.SetMaintenanceMode(key);

			Protocol.Parameter parameter = new Protocol.Parameter(Protocol.WriteSerialNumber);
			
			if (serial.Length > parameter.Count)
				throw new SGSException(Catalog.GetString("The 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 ReadSerialNumber()
		{
			string serial = this.ReadStringParameter(Protocol.ReadSerialNumber);
			return serial.Trim();
		}
		
		internal override void WriteDeviceName(string name)
		{
			Console.WriteLine("Saving device name \"{0}\".", name);
			Protocol.Parameter parameter = new Protocol.Parameter(Protocol.WriteDeviceName);
			
			if (name.Length > parameter.Count)
				throw new SGSException(Catalog.GetString("The name string is too long."), this);
			
			for (int ix=name.Length; ix < parameter.Count; ++ix)
			{
				name += " ";
			}
			
			parameter.StringValue = name;
			this.WriteParameter(parameter, 5000);
		}

		internal override string ReadDeviceName()
		{
			string name = this.ReadStringParameter(Protocol.ReadDeviceName);
			if (string.IsNullOrEmpty(name))
				return name;
			Console.WriteLine("Read device name: \"{0}\"", name);
			if (name.Length == name.Trim().Length)
				return this.Info.SerialNumber;
			else
				return name.Trim();
		}

		internal override string ReadFirmwareVersion()
		{
			ushort id = this.ReadUShortParameter(Protocol.ReadFirmwareVersion);
			int minor = id & 0xFF;
			int major = (id >> 8) & 0xFF;
			return string.Format("{0}.{1}", major, minor);
		}
		
		public override void WriteMasterKey(byte[] maintenance_key, byte[] key)
		{
			this.SetMaintenanceMode(maintenance_key);

			Protocol.Parameter parameter = new Protocol.Parameter(Protocol.WriteMasterKey);
			
			if (key.Length != parameter.Count)
				throw new SGSException(Catalog.GetString("Unexpected key length."), this);
			
			parameter.ByteArray = key;
			this.WriteParameter(parameter, 5000);
		}
		
		public override byte[] ReadMasterKey(byte[] challenge)
		{
			Protocol.Parameter parameter = new Protocol.Parameter(Protocol.ReadMasterKey);
			parameter.ByteArray    = challenge;
			parameter.QueryHasData = true;
			return this.ReadByteArrayParameter(parameter, 5000);
		}
		
		public override void WriteIndividualKey(byte[] maintenance_key, byte[] key)
		{
			this.SetMaintenanceMode(maintenance_key);

			Protocol.Parameter parameter = new Protocol.Parameter(Protocol.WriteIndividualKey);
			
			if (key.Length != parameter.Count)
				throw new SGSException(Catalog.GetString("Unexpected key length."), this);
			
			parameter.ByteArray = key;
			this.WriteParameter(parameter, 5000);
		}
		
		public override byte[] ReadIndividualKey(byte[] challenge)
		{
			Protocol.Parameter parameter = new Protocol.Parameter(Protocol.ReadIndividualKey);
			parameter.ByteArray    = challenge;
			parameter.QueryHasData = true;
			return this.ReadByteArrayParameter(parameter, 5000);
		}
		
		protected override 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);
				
			if (this.CurrentConfig.Timeout == 0 && !this.CurrentConfig.AutoTimeout)
				timeout = 0;
			
			//this.NotifyStateChanges();

			byte[] tx_buf = parameter.QueryHasData ? parameter.Word : parameter.Query;
			
			this.PrintCommandInfo(tx_buf, true);
				
			uint result = this.Write(tx_buf);
			
			this.SetState(State.Idle);
				
			if (result != tx_buf.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);
			parameter.CheckResponse(response, true);
			
			return response;
		}

		protected override bool WriteParameter(Protocol.Parameter parameter, int timeout, IdleTaskHandler idle_task)
		{
			try
			{
				if (this.CurrentConfig.Timeout == 0 && !this.CurrentConfig.AutoTimeout)
					timeout = 0;
				
				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);

				Console.WriteLine("sav_state: " + sav_state.ToString());
				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, typeof(byte));

				return parameter.CheckResponse(response, false);
			}
			catch (Exception ex)
			{
				StackFrame frame  = new StackFrame(2, true);
				MethodBase method = frame.GetMethod();
				this.ExceptionHandler(method.Name + ": " + 
				                      Catalog.GetString("Cannot transmit parameter:") + " " +
				                      ex.Message, ex);
				return false;
			}
		}
	}
}