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

using Hiperscan.SGS.Common;
using Hiperscan.SGS.SerialInterface;
using Hiperscan.Unix;


namespace Hiperscan.SGS.Sgs1900
{
    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[]), KEY_PAD_SIZE*sizeof(byte));
            public static readonly Parameter ReadMasterKey       = new Parameter('G', 'R', typeof(byte[]), KEY_SIZE*sizeof(byte));
            public static readonly Parameter WriteIndividualKey  = new Parameter('K', 'W', typeof(byte[]), KEY_PAD_SIZE*sizeof(byte));
            public static readonly Parameter ReadIndividualKey   = new Parameter('K', 'R', typeof(byte[]), KEY_SIZE*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 KEY_PAD_SIZE;
        }
        
        public override void SetMaintenanceMode(byte[] key)
        {
            Protocol.Parameter parameter = new Protocol.Parameter(Protocol.MaintenanceMode)
            {
                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)
            {
                ByteArray = challenge,
                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)
            {
                ByteArray = challenge,
                QueryHasData = true
            };
            return this.ReadByteArrayParameter(parameter, 5000);
        }
        
        protected override Protocol.Parameter ReadParameter(Protocol.Parameter parameter, int timeout, IdleTaskHandler idle_task)
        {
            if (this.SetState(Spectrometer.State.TxD) == false)
                throw new SgsException(Catalog.GetString("Device not ready."), this);
                
            if (this.CurrentConfig.Timeout == 0 && !this.CurrentConfig.AutoTimeout)
                timeout = 0;
            
            byte[] tx_buf = parameter.QueryHasData ? parameter.Word : parameter.Query;
            
            this.PrintCommandInfo(tx_buf, true);
                
            uint result = this.Write(tx_buf);
            
            this.SetState(Spectrometer.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;
                
                Spectrometer.State sav_state = this.GetState();
                
                if (!this.SetState(Spectrometer.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;
            }
        }
    }
}