// 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.SGS.Common;
using Hiperscan.SGS.SerialInterface;
using Hiperscan.Spectroscopy;
using Hiperscan.Unix;


namespace Hiperscan.SGS.Sgs1900
{
    internal class Device0_17 : Device0_16
    {
        protected new class Protocol : Device0_16.Protocol
        {
            public static readonly     Parameter ReadAcquisitionOffset   = new Parameter('A', 'O', typeof(int),   sizeof(int));
            public static readonly     Parameter WriteHardwareVersion    = new Parameter('S', 'V', typeof(byte[]), 2);
            public static readonly     Parameter ReadDeviceProperties    = new Parameter('J', 'R', typeof(byte[]), 0);
            public static readonly     Parameter WriteHardwareProperties = new Parameter('J', 'W', typeof(byte[]), 24);
            public static readonly     Parameter WriteAdcResolution      = new Parameter('O', 'W', typeof(byte), sizeof(byte));
            public static readonly     Parameter ReadAdcResolution       = new Parameter('O', 'R', typeof(byte), 0);
            public static new readonly Parameter WriteLut                = new Parameter('L', 'W', typeof(byte[]), 10028);
        }
        
        public Device0_17(DeviceInfo info, VersionInfo version_info) : base (info, version_info)
        {
        }

        public override int ReadAcquisitionOffset()
        {
            return this.ReadIntParameter(Protocol.ReadAcquisitionOffset);
        }
        
        public override void WriteHardwareVersion(byte[] key, byte major, byte minor)
        {
            this.SetMaintenanceMode(key);

            Protocol.Parameter parameter = new Protocol.Parameter(Protocol.WriteHardwareVersion)
            {
                Count = 2
            };


            byte[] buf = new byte[parameter.Count];
            buf[0] = major;
            buf[1] = minor;
            
            parameter.ByteArray = buf;
            
            if (!this.WriteParameter(parameter, 5000))
                throw new SgsException(Catalog.GetString("Cannot write hardware version."), this);
            
        }

        public override void WriteLut(byte[] key, Spectrometer.LUT lut)
        {
            this.SetMaintenanceMode(key);
            
            Protocol.Parameter parameter = new Protocol.Parameter(Protocol.WriteLut);
            
            List<byte> buf = new List<byte>();
            buf.AddRange(BitConverter.GetBytes(lut.Timestamp.Ticks));
            buf.AddRange(BitConverter.GetBytes(lut.Offset));
            buf.AddRange(BitConverter.GetBytes(lut.AcquisitionOffset));
            buf.AddRange(BitConverter.GetBytes((UInt16)(lut.MinWavelength*10.0)));
            buf.AddRange(BitConverter.GetBytes((UInt16)(lut.MaxWavelength*10.0)));
            buf.AddRange(BitConverter.GetBytes((UInt16)(lut.Increment*10.0)));
            buf.AddRange(BitConverter.GetBytes((UInt16)(lut.ExtensionRange*10.0)));
            
            foreach (float f in lut.Data)
            {
                buf.AddRange(BitConverter.GetBytes((uint)(f*1000f)));
            }
            
            if (buf.Count != parameter.Count)
                throw new SgsException(Catalog.GetString("Unexpected LUT length."), this);
            
            parameter.ByteArray = buf.ToArray();
            
            this.CurrentConfig.ReferenceSpectrumType = Spectrometer.ReferenceSpectrumType.None;
            this.CurrentConfig.ReferenceSpectrum = null;
            this.CurrentConfig.DarkIntensityType = Spectrometer.DarkIntensityType.None;
            this.CurrentConfig.DarkIntensity = new DarkIntensity(0.0, 0.0);
            this.CurrentConfig.WavelengthCorrection = null;

            if (this.WriteParameter(parameter, 20000) == false)
                throw new SgsException(Catalog.GetString("Operation failed."), this);
            
            this.lut_timestamp = lut.Timestamp;

            try
            {
                this.WriteScanEngineMsaFactor(key, lut.MsaFactor);
            }
            catch (NotSupportedException)
            {
                Console.WriteLine("WriteScanEngineMSAFactor() is not supported by hardware.");
            }
            catch (NotImplementedException)
            {
                Console.WriteLine("WriteScanEngineMSAFactor() is not supported by firmware.");
            }
        }

        protected override void Connect()
        {
            base.Connect();
            this.ReadHardwareProperties();
        }

        public override void WriteHardwareProperties(byte[] key, HardwareProperties hardware_properties)
        {
            this.SetMaintenanceMode(key);

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

            byte[] buf = new byte[parameter.Count];

            if (hardware_properties.Type.Length == 1)
                hardware_properties.Type += " ";

            if (hardware_properties.Type.Length == 2)
            {
                buf[0] = (byte)hardware_properties.Type[0];
                buf[1] = (byte)hardware_properties.Type[1];
            }
            else
            {
                buf[0] = byte.MaxValue;
                buf[1] = byte.MaxValue;
            }
            byte[] tmp = BitConverter.GetBytes((UInt16)(hardware_properties.GridConstant));
            Array.Copy(tmp, 0, buf, 2, tmp.Length);

            tmp = BitConverter.GetBytes((UInt16)(hardware_properties.IncidenceAngle*10.0));
            Array.Copy(tmp, 0, buf, 4, tmp.Length);

            tmp = BitConverter.GetBytes((UInt16)(hardware_properties.DiffractionAngle*10.0));
            Array.Copy(tmp, 0, buf, 6, tmp.Length);

            tmp = BitConverter.GetBytes((Int16)(hardware_properties.MinTemperature*100.0));
            Array.Copy(tmp, 0, buf, 8, tmp.Length);

            tmp = BitConverter.GetBytes((Int16)(hardware_properties.MaxTemperature*100.0));
            Array.Copy(tmp, 0, buf, 10, tmp.Length);

            tmp = BitConverter.GetBytes((UInt16)(hardware_properties.SpectralResolution*10.0));
            Array.Copy(tmp, 0, buf, 12, tmp.Length);

            tmp = BitConverter.GetBytes((UInt16)(hardware_properties.WavelengthDrift*10.0));
            Array.Copy(tmp, 0, buf, 14, tmp.Length);

            tmp = BitConverter.GetBytes((UInt32)(hardware_properties.Sensitivity));
            Array.Copy(tmp, 0, buf, 16, tmp.Length);

            tmp = BitConverter.GetBytes((UInt16)(hardware_properties.SignalNoiseRatio));
            Array.Copy(tmp, 0, buf, 20, tmp.Length);

            tmp = BitConverter.GetBytes((UInt16)(hardware_properties.DynamicRange));
            Array.Copy(tmp, 0, buf, 22, tmp.Length);

            parameter.ByteArray = buf;

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

        internal void ReadHardwareProperties()
        {
            byte[] buf = this.ReadByteArrayParameter(Protocol.ReadDeviceProperties);

            if (buf.Length == 1)
            {
                if (buf[0] == 0)
                {
                    this.NotifyWarning(Catalog.GetString("Hardware properties are invalid or not set."));
                    this.HardwareProperties.SetInvalid();
                    return;
                }
                else
                {
                    throw new SgsException(Catalog.GetString("Invalid device response."), this);
                }
            }

            byte type_byte1 = buf[0];
            byte type_byte2 = buf[1];
            if (type_byte1 != byte.MaxValue)
                this.HardwareProperties.Type = string.Format("{0}{1}", (char)type_byte1, (char)type_byte2);

            this.HardwareProperties.GridConstant         = BitConverter.ToUInt16(buf, 2);
            this.HardwareProperties.IncidenceAngle       = BitConverter.ToUInt16(buf, 4) / 10.0;
            this.HardwareProperties.DiffractionAngle     = BitConverter.ToUInt16(buf, 6) / 10.0;
            this.HardwareProperties.MinTemperature       = BitConverter.ToInt16(buf, 8) / 100.0;
            this.HardwareProperties.MaxTemperature       = BitConverter.ToInt16(buf, 10) / 100.0;
            this.HardwareProperties.SpectralResolution   = BitConverter.ToUInt16(buf, 12) / 10.0;
            this.HardwareProperties.WavelengthDrift      = BitConverter.ToUInt16(buf, 14) / 10.0;
            this.HardwareProperties.Sensitivity          = BitConverter.ToUInt32(buf, 16);
            this.HardwareProperties.SignalNoiseRatio     = BitConverter.ToUInt16(buf, 20);
            this.HardwareProperties.DynamicRange         = BitConverter.ToUInt16(buf, 22);
            this.HardwareProperties.MinWavelength        = BitConverter.ToUInt16(buf, 24) / 10.0;
            this.HardwareProperties.MaxWavelength        = BitConverter.ToUInt16(buf, 26) / 10.0;
            this.HardwareProperties.WavelengthIncrement  = BitConverter.ToUInt16(buf, 28) / 10.0;
            this.HardwareProperties.WavelengthExtension  = BitConverter.ToUInt16(buf, 30) / 10.0;
            this.HardwareProperties.MinAverage           = BitConverter.ToUInt16(buf, 32);
            this.HardwareProperties.MaxAverage           = BitConverter.ToUInt16(buf, 34);

            if (buf.Length >= 38)
                this.HardwareProperties.MEMSInfo = BitConverter.ToUInt16(buf, 36);

            if (buf.Length >= 40)
                this.HardwareProperties.AdcConfigurationId = BitConverter.ToUInt16(buf, 38);
        }

        public override void WriteAdcResolution(byte[] key, byte bits)
        {
            this.SetMaintenanceMode(key);

            Protocol.Parameter parameter = new Protocol.Parameter(Protocol.WriteAdcResolution)
            {
                ByteValue = bits
            };

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

        public override byte ReadAdcResolution()
        {
            return this.ReadByteParameter(Protocol.ReadAdcResolution);
        }
    }
}
