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


namespace Hiperscan.SGS.CalibrationDevice
{
    internal class CalibrationDevice0_21 : CalibrationDevice
    {
        protected new class Protocol : CalibrationDevice.Protocol
        {
        }

        public CalibrationDevice0_21(DeviceInfo info, VersionInfo version_info) : base(info, version_info)
        {
        }

        public override void SetFilterPosition(int position, IdleTaskHandler idle_task)
        {
            Protocol.Parameter parameter = new Protocol.Parameter(Protocol.FinderWheel)
            {
                ByteValue = (byte)position
            };

            this.FinderPosition = FinderWheelPosition.Moving;
            lock (this)
                this.state_changed = true;

            if (!this.WriteParameter(parameter, 15000, idle_task))
            {
                if (!this.SetState(Spectrometer.State.Unknown))
                    throw new SgsException(Catalog.GetString("Device is not ready."), this);

                this.FinderPosition = FinderWheelPosition.Unknown;
                throw new SgsException(Catalog.GetString("Cannot set filter position: Device returned error."), this);
            }

            this.FinderPosition = position;
            idle_task?.Invoke();
        }

        public override void SetLightSource(bool on, IdleTaskHandler idle_task)
        {
            if (this.GetState() == Spectrometer.State.Unknown)
                return;

            Console.WriteLine("Switching light source: " + on);

            Protocol.Parameter parameter = new Protocol.Parameter(Protocol.FinderLight);
            if (on)
                parameter.ByteValue = 1;
            else
                parameter.ByteValue = 0;

            this.SetState(Spectrometer.State.Waiting);
            if (this.WriteParameter(parameter) == false)
                throw new SgsException(Catalog.GetString("Cannot switch light source: Device returned error."), this);

            this.FinderLightSourceState = on;
            lock (this)
                this.state_changed = true;

            idle_task?.Invoke();

            if (on)
            {
                this.LightSourceActivationTime = DateTime.Now;
                Thread.Sleep((int)this.LightSourceDelay);
            }

            this.SetState(Spectrometer.State.Idle);
        }

        public override void SetLaser(bool on)
        {
            base.SetLaser(on);
            if (on)
                this.LaserActivationTime = DateTime.Now;
        }

        public override void SetStatusLed(bool green)
        {
            base.SetStatusLed(green);
        }

        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)
                        {
                            throw;
                        }
                        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;
                            }
                        }

                        this.serial_interface.ResetDevice();
                        this.serial_interface.SetTimeouts();
                        this.serial_interface.SetLatency();

                        // FIXME: 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;

                        if (this.FinderPresent() == false)
                            throw new NotSupportedException("Cannot find Peripheral PCB.");

                        this.Connect();

                        this.firmware_version  = this.ReadFirmwareVersion();
                        this.firmware_revision = this.ReadFirmwareRevision();

                        this.current_config = new Spectrometer.Config(this);

                        Console.WriteLine("Read system parameter:");
                        Console.WriteLine("Firmware version:\t" + this.firmware_version);
                        Console.WriteLine("Firmware revision:\t" + this.firmware_revision);

                        this.is_finder = true;

                        this.SetStatusLed(true);
                        this.SetLightSource(true, null);
                        this.SetLaser(true);
                        this.SetFilterPosition(CalibrationFilterPosition.Initial, null);
                        base.ReadFinderHardwareVersion();
                    }
                }
                catch (System.Runtime.Remoting.RemotingException)
                {
                    this.SetState(Spectrometer.State.Disconnected);
                    throw;
                }
                catch (Exception)
                {
                    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();
                        }
                        catch { }
                    }
                    throw;
                }
                finally
                {
                    this.is_initialized = true;
                }
            }
        }

        public override void Close()
        {
#pragma warning disable RECS0022
            try { this.SetLightSource(false, null); } catch { }
            try { this.SetLaser(false);             } catch { }
            try { this.SetStatusLed(false);         } catch { }
#pragma warning restore RECS0022
            base.Close();
        }
    }
}
