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

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


namespace Hiperscan.SGS.Sgs1900
{
    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)
            {
                ByteArray = buf,
                QueryHasData = true
            };

            return this.ReadByteArrayParameter(parameter, 0);
        }
        
        public override void WriteFinderPCBCommand(byte[] command)
        {
            if (this.IsFinder == false)
                throw new NotSupportedException(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) == false)
                throw new SgsException(Catalog.GetString("Operation failed."), this);
        }

        public override SystemStats ReadSystemStats()
        {
            if (this.IsFinder == false)
                throw new NotSupportedException(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 == false)
                throw new NotSupportedException(Catalog.GetString("Not supported by hardware."));

            this.SetMaintenanceMode(key);

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

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

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

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

            Protocol.Parameter parameter = new Protocol.Parameter(Protocol.SetFinderStatus)
            {
                ByteValue = (byte)status
            };
            if (this.WriteParameter(parameter) == false)
                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)
                {
                    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 == false)
                return;

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

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

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

            return this.ReadUIntParameter(Protocol.ReadFinderIdleTime);
        }

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

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

            if (this.WriteParameter(parameter) == false)
                throw new SgsException(Catalog.GetString("Operation failed."), this);
        }

        public override double ReadStatus()
        {
            short[] status = this.ReadShortArrayParameter(Protocol.ReadStatus);
            if (status == null)
                throw new SgsException(Catalog.GetString("Operation failed."), this);

            short temperature = status[0];
            this.sgs_firmware_status = (SGSFirmwareStatusType)status[1];

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

            return this.LastTemperature = (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();
        }
    }
}
