// 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.Common;
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_15 : Device0_14
    {
        protected new class Protocol : Device0_14.Protocol
        {
            public static new readonly Parameter FinderPresent                      = new Parameter('F', 'D', typeof(byte[]), 0);
            public static     readonly Parameter WriteFinderOverrideHardwareVersion = new Parameter('F', 'W', typeof(byte[]), 4);
            public static     readonly Parameter ReadFinderHardwareVersion          = new Parameter('F', 'E', typeof(byte[]), 0);
            public static new readonly Parameter WriteI2C                           = new Parameter('I', 'W', typeof(byte[]), 0);
            public static new readonly Parameter ReadI2C                            = new Parameter('I', 'R', typeof(byte[]), 3);
        }
        
        public Device0_15(DeviceInfo info, VersionInfo version_info) : base (info, version_info)
        {
        }
        
        public override void Open(bool auto_init)
        {
            lock (this.opening_mutex)
            {
                base.Open(auto_init);
                
                if (this.IsFinder && auto_init)
                    this.ReadFinderHardwareVersion();
            }
        }
        
        // new implemented: error in firmware fixed. device answers 1 in case of success (see 0.14)
        public override void SetFinderPosition(int position, IdleTaskHandler idle_task, bool no_wait=false)
        {
            if (!this.IsFinder)
                return;

            Protocol.Parameter parameter = new Protocol.Parameter(Protocol.FinderWheel)
            {
                ByteValue = (byte)position,
            };

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

            int timeout = (position == FinderWheelPosition.Initial) ? 20000 : 10000;
            if (!this.WriteParameter(parameter, timeout, 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 reference wheel position: Device returned error."), this);
            }

            this.FinderPosition = position;
            idle_task?.Invoke();
        }
        
        internal override bool FinderPresent()
        {
            byte[] result = this.ReadByteArrayParameter(Protocol.FinderPresent);
            
            if (result == null ||
                result.Length == 0 ||
                result.Length == 1 && result[0] != 0)
                throw new SgsException(Catalog.GetString("Cannot determine finder hardware version: Unexpected device response."), this);
            else if (result.Length == 1 && result[0] == 0)
                return false;
            
            if (result.Length != 4)
                throw new SgsException(Catalog.GetString("Cannot determine finder hardware version: Unexpected device response."), this);

            this.finder_version = new FinderVersion(result);
            return true;
        }
        
        public override void WriteFinderOverrideHardwareVersion(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.WriteFinderOverrideHardwareVersion)
            {
                Count = (ushort)(versions.Length),
                ByteArray = versions
            };

            if (!this.WriteParameter(parameter, 5000) || !this.FinderPresent())
                throw new SgsException(Catalog.GetString("Operation failed."), this);
            
            this.FinderVersion = new FinderVersion(this.ReadByteArrayParameter(Protocol.FinderPresent));
        }
        
        public override byte[] ReadFinderHardwareVersion()
        {
            return this.ReadByteArrayParameter(Protocol.ReadFinderHardwareVersion);
        }
        
        public override void WriteI2C(byte[] key, I2CBusId bus_id, byte address, byte[] data)
        {
            if (data == null || data.Length == 0 || data.Length + 1 > ushort.MaxValue)
                throw new ArgumentException(Catalog.GetString("Unexpected I2C parameter number."));
            
            this.SetMaintenanceMode(key);

            Protocol.Parameter parameter = new Protocol.Parameter(Protocol.WriteI2C)
            {
                Count = (ushort)(2 + data.Length)
            };

            byte[] buf = new byte[data.Length+1];
            buf[0] = (byte)bus_id;
            buf[1] = address;
            Array.Copy(data, 0, buf, 2, data.Length);
            
            parameter.ByteArray = buf;
            
            if (this.WriteParameter(parameter, 5000) == false)
                throw new SgsException(Catalog.GetString("I2C (write) operation failed."), this);
        }
        
        public override byte[] ReadI2C(byte[] key, I2CBusId bus_id, byte address, byte count)
        {
            this.SetMaintenanceMode(key);

            Protocol.Parameter parameter = new Protocol.Parameter(Protocol.ReadI2C)
            {
                ByteArray = new byte[] { (byte)bus_id, address, count },
                QueryHasData = true
            };
            byte[] buf = this.ReadByteArrayParameter(parameter, 5000);
            
            if (buf.Length == 0)
                throw new SgsException(Catalog.GetString("I2C (read) operation failed."), this);
            
            return buf;
        }
        
        protected override void ReadSpectrum(out uint[] add_data, out double[] lambda, out double[] intensity, IdleTaskHandler idle_task)
        {
            uint count = 0;

            TimeSpan span = new TimeSpan(0, 0, 2);

            while (count++ < ReadSpectrumTryCount)
            {
                DateTime then = DateTime.Now;

                base.ReadSpectrum(out add_data, out lambda, out intensity, idle_task);
                
                if (double.IsInfinity(this.AcquisitionInfo.MirrorFreq) == false)
                {
                    if (add_data.Length > 1)
                    {
                        this.LastTemperature = (double)add_data[1] / 100.0;
                        lock (this)
                            this.state_changed = true;
                    }

                    Array.Resize(ref add_data, add_data.Length + 1);
                    add_data[add_data.Length - 1] = count;
                    return;
                }

                while (DateTime.Now - then < span)
                {
                    System.Threading.Thread.Sleep(100);
                }
                
                Console.WriteLine("Device returned invalid spectrum. Try again ({0}).", count);
            }
            
            intensity = new double[0];
            throw new InvalidSpectrumException(Catalog.GetString("Device returned an invalid spectrum."), this);
        }
    }
}
