// **********************************************************************************************************
//    CommDeviceSerialAsync.cs
//    4/3/2024
//    NGI - Next Generation Interceptor
//    
//    Contract No. HQ0856-21-C-0003/1022000209
//    
//    THIS DOCUMENT DOES NOT CONTAIN TECHNOLOGY OR TECHNICAL DATA CONTROLLED UNDER EITHER THE U.S. 
//    INTERNATIONAL TRAFFIC IN ARMS REGULATIONS OR THE U.S. EXPORT ADMINISTRATION REGULATIONS.
//    
//    RAYTHEON PROPRIETARY: THIS DOCUMENT CONTAINS DATA OR INFORMATION PROPRIETARY TO RAYTHEON 
//    COMPANY AND IS RESTRICTED TO USE ONLY BY PERSONS AUTHORIZED BY RAYTHEON COMPANY IN WRITING TO USE IT. 
//    DISCLOSURE TO UNAUTHORIZED PERSONS WOULD LIKELY CAUSE SUBSTANTIAL COMPETITIVE HARM TO RAYTHEON 
//    COMPANY'S BUSINESS POSITION. NEITHER SAID DOCUMENT NOR ITS CONTENTS SHALL BE FURNISHED OR DISCLOSED 
//    TO OR COPIED OR USED BY PERSONS OUTSIDE RAYTHEON COMPANY WITHOUT THE EXPRESS WRITTEN APPROVAL OF 
//    RAYTHEON COMPANY.
//    
//    UNPUBLISHED WORK - COPYRIGHT RAYTHEON COMPANY.
//    
//    DESTRUCTION NOTICE: FOR CLASSIFIED DOCUMENTS FOLLOW THE PROCEDURES IN DOD 5220.22-M, 
//    NATIONAL INDUSTRIAL SECURITY PROGRAM OPERATING MANUAL, FEBRUARY 2006, 
//    INCORPORATING CHANGE 1, MARCH 28, 2013, CHAPTER 5, SECTION 7, OR DODM 5200.01-VOLUME 3, 
//    DOD INFORMATION SECURITY PROGRAM: PROTECTION OF CLASSIFIED INFORMATION, ENCLOSURE 3, 
//    SECTION 17. FOR CONTROLLED UNCLASSIFIED INFORMATION FOLLOW THE PROCEDURES IN DODM 5200.01-VOLUME 4, 
//    INFORMATION SECURITY PROGRAM: CONTROLLED UNCLASSIFIED INFORMATION.
//    
//    CONTROLLED BY: MISSILE DEFENSE AGENCY
//    CONTROLLED BY: GROUND-BASED MIDCOURSE DEFENSE PROGRAM OFFICE
//    CUI CATEGORY: CTI 
//    DISTRIBUTION/DISSEMINATION CONTROL: F 
//    POC: Alex Kravchenko (1118268)
// **********************************************************************************************************
using System;
using System.IO.Ports;
using NLog;
using Raytheon.Common;

namespace Raytheon.Instruments
{
   /// <summary>
   /// A sim communication device
   /// </summary>
   public class CommDeviceSerial : ICommDevice
   {
      #region PrivateClassMembers

      private int _defaultReadTimeout;
      private int _defaultReadBufferSize;
      private object _syncObj = new object();

      private SerialPort _serialPort;

      private string _comPortName;
      private int _baudRate;
      private Parity _parity;
      private int _dataBits;
      private StopBits _stopBits;

      private readonly string _name;
      private State _state;

      private readonly ILogger _logger;

      private readonly IConfigurationManager _configurationManager;
      private readonly IConfiguration _configuration;

      #endregion

      public bool ClearErrors() => false;
      public bool FrontPanelEnabled { get => false; set => throw new NotImplementedException(); }
      public bool DisplayEnabled { get => false; set => throw new NotImplementedException(); }
      public string DetailedStatus => $"This is a TCP/IP Device called {_name}";
      public InstrumentMetadata Info => throw new NotImplementedException();
      public State Status => _state;
      public string Name => _name;
      public SelfTestResult PerformSelfTest() => SelfTestResult;
      public SelfTestResult SelfTestResult => SelfTestResult.Unknown;

      public void Close() => Shutdown();
      public void Reset()
      {
         Close();
         Open();
      }

      #region Public Functions

      /// <summary>
      /// CommDevice factory constructor
      /// </summary>
      /// <param name="deviceName"></param>
      /// <param name="configurationManager"></param>
      public CommDeviceSerial(string deviceName, IConfigurationManager configurationManager)
      {
         _name = deviceName;
         _configurationManager = configurationManager;
         _configuration = _configurationManager.GetConfiguration(deviceName);
         _logger = LogManager.GetLogger($"{this.GetType().Name} - {deviceName}");

         _comPortName = _configuration.GetConfigurationValue(_name, "COMPortName");
         int.TryParse(_configuration.GetConfigurationValue(_name, "BaudRate"), out _baudRate);
         Enum.TryParse(_configuration.GetConfigurationValue(_name, "Parity"), true, out _parity);
         int.TryParse(_configuration.GetConfigurationValue(_name, "DataBits"), out _dataBits);
         Enum.TryParse(_configuration.GetConfigurationValue(_name, "StopBits"), true, out _stopBits);

         int.TryParse(_configuration.GetConfigurationValue(_name, "ReadTimeout"), out _defaultReadTimeout);
         int.TryParse(_configuration.GetConfigurationValue(_name, "BufferSize"), out _defaultReadBufferSize);

         _state = State.Uninitialized;
      }

      /// <summary>
      /// Destructor
      /// </summary>
      ~CommDeviceSerial()
      {
         Shutdown();
      }

      /// <summary>
      /// initialize instrument
      /// </summary>
      public void Initialize()
      {
         Open();
      }

      /// <summary>
      /// Opens COM serial port for communications
      /// </summary>
      public void Open()
      {
         if (_state == State.Uninitialized)
         {
            _serialPort = new SerialPort(_comPortName, _baudRate, _parity, _dataBits, _stopBits);
            _serialPort.Open();
            _serialPort.ReadTimeout = _defaultReadTimeout;
            _state = State.Ready;
         }
      }

      /// <summary>
      /// shuts down the device
      /// </summary>
      public void Shutdown()
      {
         if (_serialPort != null)
            _serialPort.Close();

         _state = State.Uninitialized;
      }

      /// <summary>
      /// Read data from the device asynchronously.
      /// </summary>
      /// <param name="dataRead">The buffer to put the data in</param>
      /// <returns>The number of bytes read</returns>
      public uint Read(ref byte[] dataRead)
      {
         if (dataRead == null)
            dataRead = new byte[_defaultReadBufferSize];

         var bytesRead = _serialPort.Read(dataRead, 0, dataRead.Length);
         return (uint)bytesRead;
      }

      /// <summary>
      /// Sets the read timeout
      /// </summary>
      /// <param name="timeoutMs"></param>
      public void SetReadTimeout(uint timeoutMs)
      {
         if (timeoutMs > 0)
            _serialPort.ReadTimeout = (int)timeoutMs;
         else
            _serialPort.ReadTimeout = _defaultReadTimeout;
      }

      /// <summary>
      /// Write data to the device asynchronously
      /// </summary>
      /// <param name="dataToSend"></param>
      /// <param name="numBytesToWrite"></param>
      /// <returns></returns>
      public uint Write(byte[] dataToSend, uint numBytesToWrite)
      {
         _serialPort.Write(dataToSend, 0, (int)numBytesToWrite);

         return 0;
      }

      #endregion
   }
}
