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


namespace Hiperscan.SGS.Sgs1900
{
    internal class Device0_19 : Device0_18
    {
        protected new class Protocol : Device0_18.Protocol
        {
            public static readonly Parameter LightSourceReady              = new Parameter('L', 'R', typeof(Int16), 0);
            public static readonly Parameter WriteFinderPcbHardwareVersion = new Parameter('F', 'C', typeof(byte[]), 4);
        }

        public Device0_19(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 (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...");
                                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.is_finder = this.FinderPresent();

                        this.Connect();
                        
                        try
                        {                        
                            this.current_config = this.ReadConfig();
                        }
                        catch (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();

                        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.IsFinder)
                        {
                            this.ReadTemperatureControlInfo(out byte version, out byte control_value, out double target_temperature);

                            if (version == 0 || version == 255 || this.FinderVersion.Board < 3)
                            {
                                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 ex)
                {
                    this.SetState(Spectrometer.State.Disconnected);
                    throw ex;
                }
                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 short LightSourceReady()
        {
            if (Environment.GetEnvironmentVariable(Env.HCL.SET_LIGHT_SOURCE_ERROR) == "1")
                return -1;

            short result = this.ReadShortParameter(Protocol.LightSourceReady);

            if (Environment.GetEnvironmentVariable(Env.HCL.SKIP_LIGHT_SOURCE_STABILIZATION) == "1" && result > 0)
                return 0;
            
            return result;
        }

        public override void WriteFinderPcbHardwareVersion(byte[] key, byte[] versions)
        {
            if (versions == null || versions.Length != 4)
                throw new ArgumentException(Catalog.GetString("Invalid finder hardware version information."));
            
            this.SetMaintenanceMode(key);

            Protocol.Parameter parameter = new Protocol.Parameter(Protocol.WriteFinderPcbHardwareVersion)
            {
                Count = (ushort)(versions.Length),
                ByteArray = versions
            };

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