// 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 Hiperscan.SGS.Benchtop.TemperatureControl;
using Hiperscan.Unix;
using static Hiperscan.SGS.SgsNt.Protocol.BaseProtocol;

namespace Hiperscan.SGS.SgsNt
{
    internal partial class DeviceNt1_2
    {
        private const int TEMPERATURE_COUNT = 2;
        private const int TEMPERATURE_INTERVAL_SEC = 300;
        private const int TEMPERATURE_TIMEOUT_MS = 2000;


        public override void InitHeaters()
        {
            this.Temperatures = new DeviceTemperatures();
            this.Temperatures.Capsule = new CapsuleTemperature(DeviceNt1_2.TEMPERATURE_COUNT,
                                                               DeviceNt1_2.TEMPERATURE_INTERVAL_SEC);

            this.Temperatures.Detector = new DetectorTemperature(DeviceNt1_2.TEMPERATURE_COUNT,
                                                               DeviceNt1_2.TEMPERATURE_INTERVAL_SEC);

            this.Temperatures.Finder = new Temperature(DeviceNt1_2.TEMPERATURE_COUNT,
                                                               DeviceNt1_2.TEMPERATURE_INTERVAL_SEC);

            this.Temperatures.ScanEngine = new Temperature(DeviceNt1_2.TEMPERATURE_COUNT,
                                                               DeviceNt1_2.TEMPERATURE_INTERVAL_SEC);

            //TODO check for is_finder
            this.ReadTemperatures();
        }

        public override void ResetTemperatures()
        {
            this.Temperatures.Detector.Reset = true;
            this.SetDetectorTemperature(false);

            this.Temperatures.Capsule.SetTemperature = 45.0;
            this.Temperatures.Capsule.SetHysteresisTemperature = 0.5;
            this.Temperatures.Capsule.Mode = CapsuleHeaterMode.Stable;
            this.Temperatures.Capsule.HeaterOn = false;
            this.Temperatures.Capsule.FanOn = false;

            this.Temperatures.Detector.SetTemperature = 50.0;
            this.Temperatures.Detector.Sensor = DetectorSensorType.Transistor;
            this.Temperatures.Detector.Reset = false;
            this.Temperatures.Detector.Enable = true;

            this.SetDetectorTemperature(false);
        }

        public override void ReadTemperatures()
        {
            this.ReadCapsuleTemperature();
            this.ReadDetectorTemperature();
            this.ReadFinderTemperature();
            this.ReadScanEngineTemperature();
            this.ReadEnvironmentDatas();
        }

        public override void WriteTemperatures(bool set_pid = false)
        {
            this.SetCapsuleTemperature();
            this.SetDetectorTemperature(set_pid);
        }
       

        public override void SetCapsuleTemperature()
        {
            if (this.Temperatures.SupportsCapsule == false)
                return;

            CapsuleHeaterWriteData write_data = new CapsuleHeaterWriteData();
            write_data.Hysteresis = (UInt16)(this.Temperatures.Capsule.SetHysteresisTemperature * 100);
            write_data.SetTemperature = (UInt16)(this.Temperatures.Capsule.SetTemperature * 100);
            write_data.Mode = this.Temperatures.Capsule.ModeAsBitMask;

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

            Protocol.Response response = this.WriteCommand(command, DeviceNt1_2.TEMPERATURE_TIMEOUT_MS);

            if (response == null)
                throw new SgsException(Catalog.GetString("Cannot write capsule state."), this);
        }

        protected virtual void ReadCapsuleTemperature()
        {
            if (this.Temperatures.SupportsCapsule == false)
                return;

            Command command = new Command(Protocol.ReadCapsuleHeaterState);

            Protocol.Response response = this.WriteCommand(command, DeviceNt1_2.TEMPERATURE_TIMEOUT_MS);

            if (response == null)
                throw new SgsException(Catalog.GetString("Cannot read capsule temperature."), this);

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

            this.Temperatures.Capsule.SetTemperature = read_data.SetTemperature / 100.0;
            this.Temperatures.Capsule.SetHysteresisTemperature = read_data.HysteresisTemperature / 100.0;
            this.Temperatures.Capsule.ConvertToMode(read_data.Mode);
            this.Temperatures.Capsule.ActualTemperature.Add(read_data.ActTemperature / 100.0);
        }
        

        public override void SetDetectorTemperature(bool set_pid)
        {
            if (this.Temperatures.SupportsCapsule == false)
                return;

            Command command = null;
            Response response = null;

            if (set_pid)
            {
                DetectorPIDWriteData pid_data = new DetectorPIDWriteData();
                pid_data.ProportionalComponent = (float)this.Temperatures.Detector.PidControl.ProportionalComponent;
                pid_data.IntegralComponent = (float)this.Temperatures.Detector.PidControl.IntegralComponent;
                pid_data.DerivativeComponent = (float)this.Temperatures.Detector.PidControl.DerivativeComponent;

                command = new Command(Protocol.WriteDetectorHeaterPid)
                {
                    TransferStructValue = TransferStruct.GetBytes(pid_data),
                };

                response = this.WriteCommand(command, DeviceNt1_2.TEMPERATURE_TIMEOUT_MS);

                if (response == null)
                    throw new SgsException(Catalog.GetString("Cannot write detector PID data."), this);
            }

            DetectorSensorWriteData sensor_data = new DetectorSensorWriteData();
            sensor_data.DestinationSensor = this.Temperatures.Detector.Sensor;

            command = new Command(Protocol.SetDetectorHeaterSensor)
            {
                TransferStructValue = TransferStruct.GetBytes(sensor_data),
            };

            response = this.WriteCommand(command, DeviceNt1_2.TEMPERATURE_TIMEOUT_MS);

            if (response == null)
                throw new SgsException(Catalog.GetString("Cannot write detector PID data."), this);


            DetectorHeaterWriteData write_data = new DetectorHeaterWriteData();
            write_data.SetTemperature = (UInt16)(this.Temperatures.Detector.SetTemperature * 100);
            write_data.Enable = Convert.ToByte(this.Temperatures.Detector.Enable);
            write_data.Reset = Convert.ToByte(this.Temperatures.Detector.Reset);

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

            response = this.WriteCommand(command, DeviceNt1_2.TEMPERATURE_TIMEOUT_MS);

            if (response == null)
                throw new SgsException(Catalog.GetString("Cannot write detector state."), this);
        }

        protected virtual void ReadDetectorTemperature()
        {
            if (this.Temperatures.SupportsDetector == false)
                return;

            Command command = new Command(Protocol.ReadDetectorHeaterParams);
            Protocol.Response response = this.WriteCommand(command, DeviceNt1_2.TEMPERATURE_TIMEOUT_MS);

            if (response == null)
                throw new SgsException(Catalog.GetString("Cannot read detector temperature."), this);

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

            this.Temperatures.Detector.SetTemperature = read_data.SetTemperature / 100.0;
            this.Temperatures.Detector.TransisitorTemperature.Add(read_data.TransistorTemperature / 100.0);
            this.Temperatures.Detector.AmplifierTemperature.Add(read_data.AmplifierTemperature / 100.0);

            this.Temperatures.Detector.PidControl.ProportionalComponent = read_data.ProportionalComponent;
            this.Temperatures.Detector.PidControl.IntegralComponent = read_data.IntegralComponent;
            this.Temperatures.Detector.PidControl.DerivativeComponent = read_data.DerivativeComponent;

            this.ReadDetectorState();
        }

        protected void ReadDetectorState()
        {
            Command command = new Command(Protocol.ReadDetectorHeaterState);
            Protocol.Response response = this.WriteCommand(command, DeviceNt1_2.TEMPERATURE_TIMEOUT_MS);

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

            DetectorStateReadData state_data = (DetectorStateReadData)TransferStruct.FromBuffer(response.PayloadData, typeof(DetectorStateReadData));

            this.Temperatures.Detector.State = state_data.State;
            this.Temperatures.Detector.Sensor = state_data.Sensor;
        }

        protected new virtual void ReadFinderTemperature()
        {
            if (this.Temperatures.SupportsFinder == false)
                return;

            Protocol.Response response = this.WriteCommand(new Protocol.Command(Protocol.ReadFinderStatus), DeviceNt1_2.TEMPERATURE_TIMEOUT_MS);

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

            StatusReadData stat = (StatusReadData)Protocol.TransferStruct.FromBuffer(response.TransferStructValue, typeof(StatusReadData));

            this.Temperatures.Finder.Add(stat.Temperature / 100.0);
        }

        protected virtual void ReadScanEngineTemperature()
        {
            if (this.Temperatures.SupportsScanEngine == false)
                return;

            Protocol.Response response = this.WriteCommand(new Protocol.Command(Protocol.ReadStatus), DeviceNt1_2.TEMPERATURE_TIMEOUT_MS);

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

            StatusReadData stat = (StatusReadData)Protocol.TransferStruct.FromBuffer(response.TransferStructValue, typeof(StatusReadData));

            this.Temperatures.ScanEngine.Add(stat.Temperature / 100.0);
        }

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

            Command command = new Command(Protocol.ReadEnvironmentParams);

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

            if (response == null)
                throw new SgsException(Catalog.GetString("Cannot read environment parameters."), this);

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

            if (this.Temperatures.SupportsEnvironment == false)
            {
                this.Temperatures.Environment = new EnvironmentQuery()
                {
                    TouchBoardTemperature = new Temperature(),
                    PeripheralBoardTemperature = new Temperature(),
                    AtmosphericPressure = read_data.AtmosphericPressure,
                    Humidity = read_data.Humidity
                };
                this.Temperatures.Environment.TouchBoardTemperature.Add(read_data.TouchBoardTemperature / 100);
                this.Temperatures.Environment.PeripheralBoardTemperature.Add(read_data.PeripheralBoardTemperature / 100);
            }
            else
            {
                this.Temperatures.Environment.PeripheralBoardTemperature.Add(read_data.PeripheralBoardTemperature / 100);
                this.Temperatures.Environment.TouchBoardTemperature.Add(read_data.TouchBoardTemperature / 100);
                this.Temperatures.Environment.AtmosphericPressure = read_data.AtmosphericPressure;
                this.Temperatures.Environment.Humidity = read_data.Humidity;
            }
        }
    }
}
