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

namespace Hiperscan.SGS.SgsNt
{

    internal partial class DeviceNt1_2
    {
        protected override void InitPeriphery()
        {
            Protocol.PeripheralInfoReadData? peripheral_info = this.ReadPeripheralInfo();
            if (peripheral_info != null)
            {
                this.is_finder = true;
                this.FinderVersion = new Benchtop.FinderSetup.FinderVersion(((Protocol.PeripheralInfoReadData)peripheral_info).VersionBytes);
                this.SetFinderPosition(FinderWheelPosition.Initial, null);
                this.ReadLightSourceVoltages();
                this.ResetLightSourceStats();
            }
            else
            {
                this.is_finder = false;
            }
        }

        public override void SetFinderPosition(int position, IdleTaskHandler idle_task, bool no_wait = false)
        {
            if (!this.IsFinder)
                return;

            Protocol.Command command = new Protocol.Command(new Protocol.Command(Protocol.PeripheralSetFinderPosition))
            {
                ByteValue = (byte)position,
            };

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

            int timeout_ms = (position == MotorPositions.Initial) ? 20000 : 10000;
            Protocol.Response response = this.WriteCommand(command, timeout_ms, idle_task);

            if (response == null)
                throw new SgsException(Catalog.GetString("Cannot set motor position."), this);

            this.FinderPosition = position;

            if (no_wait == false)
            {
                this.SetState(Spectrometer.State.Waiting);
                idle_task?.Invoke();
                Thread.Sleep(200);
                this.SetState(Spectrometer.State.Idle);
            }
        }

        public override void ResetLightSourceStats()
        {
            this.CheckForExistingPeriphery();

            LightSourceConfig config = new LightSourceConfig();
            config.LampSelection = LampSelection.Lamp1;
            config.SetLampVoltage_mV = this.LightSourceSettings.GetVoltageByLamp(config.LampSelection);
            this.SetLightSourceConfig(config);
        }

        public override void SetLightSourceConfig(LightSourceConfig config)
        {
            this.CheckForExistingPeriphery();

            this.SetFinderLightSource(false, null);

            Protocol.LightSourceConfigWriteData write_data = new Protocol.LightSourceConfigWriteData();
            write_data.LampActSelection = config.LampSelection;
            write_data.LampSetVoltage = config.SetLampVoltage_mV;

            Protocol.Command command = new Protocol.Command(Protocol.WriteLightSourceConfig)
            {
                TransferStructValue = Protocol.TransferStruct.GetBytes(write_data),
            };

            Protocol.Response response = this.WriteCommand(command, this.current_config.TimeoutMs);

            if (response == null)
                throw new SgsException(Catalog.GetString("Cannot set light source config."), this);

            this.ReadLightSourceState();
        }

        public override void ReadLightSourceState()
        {
            this.CheckForExistingPeriphery();

            Protocol.Command command = new Protocol.Command(Protocol.ReadLightSourceState);

            Protocol.Response response = this.WriteCommand(command, this.current_config.TimeoutMs);

            if (response == null)
                throw new SgsException(Catalog.GetString("Cannot read light source state."), this);

            Protocol.LightSourceStateReadData read_data =
                (Protocol.LightSourceStateReadData)Protocol.TransferStruct.FromBuffer(response.PayloadData,
                                                                                      typeof(Protocol.LightSourceStateReadData));

            this.CurrentLightSourceState = new LightSourceQuery()
            {
                LampSelection = read_data.LampActSelection,
                State = read_data.State,
                SetLampVoltage_mV = read_data.LampSetVoltage,
                ActLampVoltag_mV = read_data.LampActVoltage,
                ActLampCurrent_mA = read_data.LampCurrent,
            };

        }

        public override void ReadLightSourceVoltages()
        {
            this.CheckForExistingPeriphery();

            Protocol.Command command = new Protocol.Command(Protocol.ReadLightSourceVoltage);

            Protocol.Response response = this.WriteCommand(command, this.current_config.TimeoutMs);

            if (response == null)
                throw new SgsException(Catalog.GetString("Cannot read light source voltages."), this);

            Protocol.LightSourceVoltage read_data = 
                (Protocol.LightSourceVoltage)Protocol.TransferStruct.FromBuffer(response.PayloadData, 
                                                                                typeof(Protocol.LightSourceVoltage));

            this.LightSourceSettings = new LightSourceSettings()
            {
                Lamp1Voltage_mV = read_data.Lamp1Voltage,
                Lamp2Voltage_mV = read_data.Lamp2Voltage,
                Lamp3Voltage_mV = read_data.Lamp3Voltage,
                Reserved = read_data.Reserve
            };
        }

        public override void WriteLightSourceVoltages(LightSourceSettings light_voltages)
        {
            this.CheckForExistingPeriphery();

            Protocol.LightSourceVoltage voltages = new Protocol.LightSourceVoltage(light_voltages);

            Protocol.Command command = new Protocol.Command(Protocol.WriteLightSourceVoltage)
            {
                TransferStructValue = Protocol.TransferStruct.GetBytes(voltages),
            };

            Protocol.Response response = this.WriteCommand(command, this.current_config.TimeoutMs);

            if (response == null)
                throw new SgsException(Catalog.GetString("Cannot write light source voltages."), this);

            this.ReadLightSourceVoltages();
        }

        public override void ReadFinderDataExtended()
        {
            this.CheckForExistingPeriphery();

            Protocol.Command command = new SgsNt.Protocol.BaseProtocol.Command(Protocol.ReadFinderStatus);

            Protocol.Response response = this.WriteCommand(command, this.current_config.TimeoutMs);

            if (response == null)
                throw new SgsException(Catalog.GetString("Cannot read Finder status data."), this);

            SgsNt.Protocol.BaseProtocol.FinderReadDataStatus read_status = 
                (SgsNt.Protocol.BaseProtocol.FinderReadDataStatus)Protocol.TransferStruct.FromBuffer(response.PayloadData,
                                               typeof(SgsNt.Protocol.BaseProtocol.FinderReadDataStatus));

            this.AddDeviceDatasExtended(read_status);
            this.ReadDetectorState();
        }

        public override void ReadLightSourceOperatingTimes()
        {
            this.CheckForExistingPeriphery();

            Protocol.Command command = new Protocol.Command(Protocol.ReadLightSourceOperatingTimeCounter);

            Protocol.Response response = this.WriteCommand(command, this.current_config.TimeoutMs);

            if (response == null)
                throw new SgsException(Catalog.GetString("Cannot write light source voltages."), this);

            Protocol.LightSourceOperatingTimeCounter counter = 
                (Protocol.LightSourceOperatingTimeCounter)Protocol.TransferStruct
                                                                  .FromBuffer(response.PayloadData, 
                                                                              typeof(Protocol.LightSourceOperatingTimeCounter));
            this.LightSourceOperatingTimes = counter.GetLightSourceOperatingTimes();
        }

        public override void ResetLightSourceOperatingTimes()
        {
            this.CheckForExistingPeriphery();

            Protocol.Command command = new Protocol.Command(Protocol.ResetLightSourceOperatingTimeCounter);

            Protocol.Response response = this.WriteCommand(command, this.current_config.TimeoutMs);

            if (response == null)
                throw new SgsException(Catalog.GetString("Cannot write light source voltages."), this);

            this.ReadLightSourceOperatingTimes();
        }

        public override SgsNt.Protocol.BaseProtocol.PositionDetectionSensorReadData ReadSamplePositionDetectionSensors()
        {
            this.CheckForExistingPeriphery();

            Protocol.Command command = new SgsNt.Protocol.BaseProtocol.Command(Protocol.ReadSamplePositionDetectionSensors);
            Protocol.Response response = this.WriteCommand(command, this.current_config.TimeoutMs);

            if (response == null)
                throw new SgsException(Catalog.GetString("Cannot read sample position detection."), this);

            SgsNt.Protocol.BaseProtocol.PositionDetectionSensorReadData detection =
                     (Protocol.PositionDetectionSensorReadData)Protocol.TransferStruct.FromBuffer(response.PayloadData,
                                                                                          typeof(Protocol.PositionDetectionSensorReadData));

            return detection;
        }

        protected void CheckForExistingPeriphery()
        {
            if (this.IsFinder == false)
                throw new NotSupportedException(Catalog.GetString("Not supported by the hardware."));
        }

        public override FinderPositionType FinderPositionType { get; protected set; } = FinderPositionType.MotorPositions;
    }
}
