// Created with MonoDevelop
//
//    Copyright (C) 2011 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 Hiperscan.SGS.Benchtop;
using Hiperscan.SGS.Common;
using Hiperscan.SGS.SerialInterface;
using Hiperscan.Unix;


namespace Hiperscan.SGS.Sgs1900
{
    internal class Device0_16 : Device0_15
    {
        protected new class Protocol : Device0_15.Protocol
        {
            public static readonly Parameter ReadTemperatureControlInput  = new Parameter('B', 'R', typeof(byte),  sizeof(byte));
            public static readonly Parameter WriteTemperatureControlInput = new Parameter('B', 'W', typeof(byte),  sizeof(byte));
            public static readonly Parameter WriteTemperatureControlInfo  = new Parameter('H', 'W', typeof(byte[]), 4);
            public static readonly Parameter ReadTemperatureControlInfo   = new Parameter('H', 'R', typeof(byte[]), 0);
            public static readonly Parameter Connect                      = new Parameter('S', 'C', typeof(byte),   0);
        }
        
        public Device0_16(DeviceInfo info,  VersionInfo version_info) : base (info, version_info)
        {
        }

        public override void Open(bool auto_init)
        {
            lock (this.opening_mutex)
            {
                this.IgnoreSgsExceptions = false;

                try
                {
                    if (this.GetState() == Spectrometer.State.Disconnected)
                    {
                        // initialize usb device
                        try
                        {
                            this.serial_interface.OpenBySerialNumber(this.info.SerialNumber);
                        }
                        catch (System.Runtime.Remoting.RemotingException ex)
                        {
                            throw ex;
                        }
                        catch (System.Exception ex)
                        {
                            PlatformID osversion = Environment.OSVersion.Platform;
                            if (osversion == PlatformID.Win32NT      ||
                                osversion == PlatformID.Win32S       ||
                                osversion == PlatformID.Win32Windows ||
                                osversion == PlatformID.WinCE)
                            {
                                Console.WriteLine("Exception while opening usb device: " + ex.Message);
                                Console.WriteLine("\t-> try once more in 4 seconds...");
                                System.Threading.Thread.Sleep(4000);
                                this.serial_interface.OpenBySerialNumber(this.info.SerialNumber);
                            }
                            else
                            {
                                throw ex;
                            }
                        }
                        
                        this.serial_interface.ResetDevice();
                        this.serial_interface.SetTimeouts();
                        this.serial_interface.SetLatency();
                        
                        // read buffers empty (since purge does not work for some reason!)
                        try
                        {
                            serial_interface.ReadBlocking(5000, 10, null);
                        }
                        catch (TimeoutException) { }

                        this.SetState(Spectrometer.State.Idle);

                        if (auto_init == false)
                            return;

                        this.Connect();
                        
                        try
                        {                        
                            this.current_config = this.ReadConfig();
                        }
                        catch (System.TimeoutException)
                        {
                            Console.WriteLine("Exception while ReadConfig() -> try once more");
                            this.serial_interface.ResetDevice();
                            this.serial_interface.SetTimeouts();
                            this.serial_interface.SetLatency();
                            
                            this.SetState(Spectrometer.State.Idle);
                            this.current_config = this.ReadConfig();
                        }
                        
                        this.current_config.Samples = 5000;
                        this.current_config.Average = 500;
                        this.current_config.Collect = false;
                        this.WriteConfig(this.CurrentConfig);
                        
                        Console.WriteLine("Current config:");
                        Console.WriteLine("\tAmplitude:\t\t"  + current_config.Amplitude);
                        Console.WriteLine("\tDeviation:\t\t"  + current_config.Deviation);
                        Console.WriteLine("\tLambdaCenter:\t" + current_config.LambdaCenter);
                        
                        this.firmware_version  = this.ReadFirmwareVersion();
                        this.firmware_revision = this.ReadFirmwareRevision();
                        this.lut_timestamp     = this.ReadLutTimestamp();
                        this.clock_frequency   = this.ReadClockFrequency();
                        this.is_finder = this.FinderPresent();

                        Console.WriteLine("Read system parameter:");
                        Console.WriteLine("Firmware version:\t" + this.firmware_version);
                        Console.WriteLine("Firmware revision:\t" + this.firmware_revision);
                        Console.WriteLine("LUT timestamp:\t" + this.lut_timestamp.ToString());
                        Console.WriteLine("Clock frequency:\t" + this.clock_frequency.ToString());
                        Console.WriteLine("Is Finder:\t\t" + this.is_finder.ToString());

                        if (this.is_finder)
                        {
                            this.SetFinderWheel(FinderWheelPosition.Initial, null);
                            this.SetFinderLightSource(false, null);
                        }

                        Console.WriteLine("Is Finder: " + this.is_finder.ToString());
                    
                        this.ReadAdcCorrection();

                        if (this.IsFinder)
                            this.ReadFinderHardwareVersion();
                    }
                }
                catch (System.Runtime.Remoting.RemotingException)
                {
                    this.SetState(Spectrometer.State.Disconnected);
                    throw;
                }
                catch (Exception ex)
                {
                    if (this.IsUnprogrammed)
                    {
                        this.current_config = new Spectrometer.Config(this);
                        this.firmware_version  = this.ReadFirmwareVersion();
                        this.firmware_revision = this.ReadFirmwareRevision();
                        this.SetState(Spectrometer.State.Unprogrammed);
                    }
                    else
                    {
                        this.SetState(Spectrometer.State.Disconnected);
                        try
                        {
                            this.serial_interface.Close();
                        }
#pragma warning disable RECS0022 // catch-Klausel, die System.Exception abfängt und keinen Text aufweist
                        catch { }
#pragma warning restore RECS0022 // catch-Klausel, die System.Exception abfängt und keinen Text aufweist
                    }
                    throw ex;
                }
                finally
                {
                    this.is_initialized = true;
                }
            }
        }

        public override double ReadStatus()
        {
            short temperature = this.ReadShortParameter(Protocol.ReadStatus);
            
            if (temperature == short.MaxValue)
                throw new NotSupportedException(Catalog.GetString("Not supported by hardware."));
            
            return this.LastTemperature = (double)temperature / 100.0;
        }

        public override double ReadFinderTemperature ()
        {
            if (!this.IsFinder)
                throw new NotSupportedException(Catalog.GetString("Not supported by hardware."));
            
            short temperature = this.ReadShortParameter(Protocol.ReadFinderTemperature);

            if (temperature == short.MaxValue)
                throw new NotSupportedException(Catalog.GetString("Not supported by hardware."));
            
            return (double)temperature / 100.0;
        }
        
        public override void WriteTemperatureControlInput(byte[] key, byte val)
        {
            if (!this.IsFinder)
                return;

            this.SetMaintenanceMode(key);

            Protocol.Parameter parameter = new Protocol.Parameter(Protocol.WriteTemperatureControlInput)
            {
                ByteValue = val
            };

            if (this.WriteParameter(parameter) == false)
                throw new SgsException(Catalog.GetString("Operation failed."), this);
        }
        
        public override byte ReadTemperatureControlInput ()
        {
            if (!this.IsFinder)
                return 0;

            return this.ReadByteParameter(Protocol.ReadTemperatureControlInput);
        }
        
        public override void WriteTemperatureControlInfo(byte[] key, byte version, byte control_value, double target_temperature)
        {
            if (this.IsFinder == false)
                throw new NotSupportedException(Catalog.GetString("Not supported by hardware."));
            
            this.SetMaintenanceMode(key);

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

            byte[] tmp = BitConverter.GetBytes((Int16)(target_temperature * 100.0));
            
            byte[] buf = new byte[parameter.Count];
            buf[0] = version;
            buf[1] = control_value;
            Array.Copy(tmp, 0, buf, 2, tmp.Length);
            
            parameter.ByteArray = buf;
            
            if (!this.WriteParameter(parameter, 5000))
                throw new SgsException(Catalog.GetString("Cannot write temperature control info."), this);
        }
        
        public override void ReadTemperatureControlInfo (out byte version, out byte control_value, out double target_temperature)
        {
            if (!this.IsFinder)
                throw new NotSupportedException(Catalog.GetString("Not supported by hardware."));
            
            byte[] buf = this.ReadByteArrayParameter(Protocol.ReadTemperatureControlInfo);
            
            version            = buf[0];
            control_value      = buf[1];
            target_temperature = (double)BitConverter.ToInt16(buf, 2) / 100.0;
        }
        
        protected override void Connect()
        {
            if (this.WriteParameter(Protocol.Connect, 30000) == false)
                throw new SgsException(Catalog.GetString("Cannot connect device."), this);
        }
    }
}
