//                           UNCLASSIFIED
/*-------------------------------------------------------------------------
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.

THIS PROPRIETARY NOTICE IS NOT APPLICABLE IF DELIVERED TO THE U.S.
GOVERNMENT.

UNPUBLISHED WORK - COPYRIGHT RAYTHEON COMPANY.
-------------------------------------------------------------------------*/

using System;
using NLog;
using Raytheon.Common;

namespace Raytheon.Instruments
{
   /// <summary>
   /// Class for controlling a Commtech SUPERFSCC/4-PCIE-11
   /// </summary>
   public class CommDeviceSuperFscc422 : ICommDevice, IDisposable
   {
      #region PrivateClassMembers

      private const uint _DEFAULT_READ_TIMEOUT = 10;
      private const uint _RX_FIFO_BUFFER_SIZE = 8192;
      private const uint _TX_FIFO_BUFFER_SIZE = 4096;
      // The Super FSCC can DMA can stream data at 50 Mbits/sec
      // The driver automatically transfers data from the FIFO into the MemoryCap which has a configurable size. Default to ~GB

      private uint _readTimeout;
      private Fscc.Port _fscc;
      private static object _syncObj = new Object();
      private readonly string _name;
      private SelfTestResult _selfTestResult;
      private State _state;
      private readonly uint _portNum;
      private readonly uint _clockFreq;
      private readonly bool _shallWeReceiveMultiple;
      private readonly bool _shallWeAppendStatus;
      private readonly uint _bgrRegister;
      private readonly uint _ccr0Register;
      private readonly uint _ccr0SofResetValue;
      private readonly uint _ccr1Register;
      private readonly uint _ccr2Register;
      private readonly uint _dpllrRegister;
      private readonly uint _fcrRegister;
      private readonly uint _fifotRegister;
      private readonly uint _imrRegister;
      private readonly uint _pprRegister;
      private readonly uint _ramrRegister;
      private readonly uint _rarRegister;
      private readonly uint _smrRegister;
      private readonly uint _ssrRegister;
      private readonly uint _tcrRegister;
      private readonly uint _tmrRegister;
      private readonly uint _tsrRegister;

      private readonly ILogger _logger;

      private readonly IConfigurationManager _configurationManager;
      private readonly IConfiguration _configuration;

      #endregion

      #region PrivateFuctions

      /// <summary>
      /// The Finalizer
      /// </summary>
      ~CommDeviceSuperFscc422()
      {
         Dispose(false);
      }

      /// Dispose of the resources contained by this object
      /// </summary>
      /// <param name="disposing"></param>
      protected virtual void Dispose(bool disposing)
      {
         try
         {
            if (disposing)
            {
               if (_state == State.Ready)
               {
                  _fscc.Dispose();

                  _state = State.Uninitialized;
               }
            }
         }
         catch (Exception err)
         {
            try
            {
               _logger.Error(err.Message + "\r\n" + err.StackTrace);
            }
            catch (Exception)
            {
               //Do not rethrow. Exception from error logger that has already been garbage collected
            }
         }
      }

      #endregion

      #region PublicFuctions

      /// <summary>
      /// CommDevice factory constructor
      /// </summary>
      /// <param name="deviceName"></param>
      /// <param name="configurationManager"></param>
      public CommDeviceSuperFscc422(string deviceName, IConfigurationManager configurationManager)
      {
         _name = deviceName;
         _selfTestResult = SelfTestResult.Unknown;
         _state = State.Uninitialized;

         _logger = LogManager.GetLogger($"{this.GetType().Name} - {deviceName}");

         _configurationManager = configurationManager;
         _configuration = _configurationManager.GetConfiguration(Name);

         _portNum = _configuration.GetConfigurationValue<uint>("SuperFscc422", "PortNum", 0);
         _clockFreq = _configuration.GetConfigurationValue<uint>("SuperFscc422", "ClockFreq", 0);
         _shallWeReceiveMultiple = _configuration.GetConfigurationValue("SuperFscc422", "ShallWeReceiveMultiple", false);
         _shallWeAppendStatus = _configuration.GetConfigurationValue("SuperFscc422", "ShallWeAppendStatus", false);
         _bgrRegister = _configuration.GetConfigurationValue<uint>("SuperFscc422", "BgrRegister", 0);
         _ccr0Register = _configuration.GetConfigurationValue<uint>("SuperFscc422", "Ccr0Register", 0);
         _ccr0SofResetValue = _configuration.GetConfigurationValue<uint>("SuperFscc422", "Ccr0SofResetValue", 0);
         _ccr1Register = _configuration.GetConfigurationValue<uint>("SuperFscc422", "Ccr1Register", 0);
         _ccr2Register = _configuration.GetConfigurationValue<uint>("SuperFscc422", "Ccr2Register", 0);
         _dpllrRegister = _configuration.GetConfigurationValue<uint>("SuperFscc422", "DpllrRegister", 0);
         _fcrRegister = _configuration.GetConfigurationValue<uint>("SuperFscc422", "FcrRegister", 0);
         _fifotRegister = _configuration.GetConfigurationValue<uint>("SuperFscc422", "FifotRegister", 0);
         _imrRegister = _configuration.GetConfigurationValue<uint>("SuperFscc422", "ImrRegister", 0);
         _pprRegister = _configuration.GetConfigurationValue<uint>("SuperFscc422", "PprRegister", 0);
         _ramrRegister = _configuration.GetConfigurationValue<uint>("SuperFscc422", "RamrRegister", 0);
         _rarRegister = _configuration.GetConfigurationValue<uint>("SuperFscc422", "RarRegister", 0);
         _smrRegister = _configuration.GetConfigurationValue<uint>("SuperFscc422", "SmrRegister", 0);
         _ssrRegister = _configuration.GetConfigurationValue<uint>("SuperFscc422", "SsrRegister", 0);
         _tcrRegister = _configuration.GetConfigurationValue<uint>("SuperFscc422", "TcrRegister", 0);
         _tmrRegister = _configuration.GetConfigurationValue<uint>("SuperFscc422", "TmrRegister", 0);
         _tsrRegister = _configuration.GetConfigurationValue<uint>("SuperFscc422", "TsrRegister", 0);

         _readTimeout = _DEFAULT_READ_TIMEOUT;

         // created in Initialize()
         _fscc = null;
      }


      /// <summary>
      /// Opens the port and initializes the registers
      /// </summary>
      /// <param name="portNum"></param>
      /// <param name="clockFreq"></param>
      /// <param name="shallWeReceiveMultiple"></param>
      /// <param name="shallWeAppendStatus"></param>
      /// <param name="bgrRegister"></param>
      /// <param name="ccr0Register"></param>
      /// <param name="ccr0SofResetValue"></param>
      /// <param name="ccr1Register"></param>
      /// <param name="ccr2Register"></param>
      /// <param name="dpllrRegister"></param>
      /// <param name="fcrRegister"></param>
      /// <param name="fifotRegister"></param>
      /// <param name="imrRegister"></param>
      /// <param name="pprRegister"></param>
      /// <param name="ramrRegister"></param>
      /// <param name="rarRegister"></param>
      /// <param name="smrRegister"></param>
      /// <param name="ssrRegister"></param>
      /// <param name="tcrRegister"></param>
      /// <param name="tmrRegister"></param>
      /// <param name="tsrRegister"></param>
      public CommDeviceSuperFscc422(string deviceName, uint portNum, uint clockFreq, bool shallWeReceiveMultiple, bool shallWeAppendStatus, uint bgrRegister, uint ccr0Register,
       uint ccr0SofResetValue, uint ccr1Register, uint ccr2Register, uint dpllrRegister, uint fcrRegister, uint fifotRegister,
       uint imrRegister, uint pprRegister, uint ramrRegister, uint rarRegister, uint smrRegister, uint ssrRegister, uint tcrRegister, uint tmrRegister, uint tsrRegister)
      {
         _name = deviceName;
         _portNum = portNum;
         _clockFreq = clockFreq;
         _shallWeReceiveMultiple = shallWeReceiveMultiple;
         _shallWeAppendStatus = shallWeAppendStatus;
         _bgrRegister = bgrRegister;
         _ccr0Register = ccr0Register;
         _ccr0SofResetValue = ccr0SofResetValue;
         _ccr1Register = ccr1Register;
         _ccr2Register = ccr2Register;
         _dpllrRegister = dpllrRegister;
         _fcrRegister = fcrRegister;
         _fifotRegister = fifotRegister;
         _imrRegister = imrRegister;
         _pprRegister = pprRegister;
         _ramrRegister = ramrRegister;
         _rarRegister = rarRegister;
         _smrRegister = smrRegister;
         _ssrRegister = ssrRegister;
         _tcrRegister = tcrRegister;
         _tmrRegister = tmrRegister;
         _tsrRegister = tsrRegister;

         _readTimeout = _DEFAULT_READ_TIMEOUT;

         // created in Initialize()
         _fscc = null;

         _logger = LogManager.GetLogger($"{this.GetType().Name} - {deviceName}");

         _selfTestResult = SelfTestResult.Unknown;
         _state = State.Uninitialized;
      }

      /// <summary>
      /// 
      /// </summary>
      /// <returns></returns>
      public bool ClearErrors()
      {
         throw new NotImplementedException();
      }

      /// <summary>
      /// 
      /// </summary>
      public bool DisplayEnabled
      {
         get
         {
            throw new NotImplementedException();
         }

         set
         {
            throw new NotImplementedException();
         }
      }

      /// <summary>
      /// 
      /// </summary>
      public string DetailedStatus
      {
         get
         {
            return "This is a FSCC 422 Device called " + _name;
         }
      }

      /// <summary>
      /// Dispose of the resources contained by this object
      /// </summary>
      public void Dispose()
      {
         lock (_syncObj)
         {
            try
            {
               Dispose(true);

               GC.SuppressFinalize(this);
            }
            catch (Exception err)
            {
               try
               {
                  _logger.Error(err.Message + "\r\n" + err.StackTrace);
               }
               catch (Exception)
               {
                  //Do not rethrow. Exception from error logger that has already been garbage collected
               }
            }
         }
      }

      /// <summary>
      /// 
      /// </summary>
      public bool FrontPanelEnabled
      {
         get
         {
            throw new NotImplementedException();
         }

         set
         {
            throw new NotImplementedException();
         }
      }

      /// <summary>
      /// 
      /// </summary>
      public InstrumentMetadata Info
      {
         get
         {
            throw new NotImplementedException();
         }
      }

      /// <summary>
      /// 
      /// </summary>
      public void Initialize()
      {
         lock (_syncObj)
         {
            if (_state == State.Uninitialized)
            {
               _fscc = new Fscc.Port(_portNum);

               // false means that each read will return a single sdlc packet
               // true means that each read will return everything in the buffer (multiple packets)
               _fscc.RxMultiple = _shallWeReceiveMultiple;

               // if this to true...extra 2 bytes arrive at the end of every frame (first two bytes of STAR)
               // The class processing the data needs to be aware of the extra two bytes
               _fscc.AppendStatus = _shallWeAppendStatus;

               // ignore timeouts
               _fscc.IgnoreTimeout = true;

               // purge the port
               _fscc.Purge(true, true);

               // set the registers
               _fscc.Registers.BGR = _bgrRegister;
               _fscc.Registers.CCR0 = _ccr0Register;
               _fscc.Registers.CCR1 = _ccr1Register;
               _fscc.Registers.CCR2 = _ccr2Register;
               _fscc.Registers.DPLLR = _dpllrRegister;
               _fscc.Registers.FCR = _fcrRegister;
               _fscc.Registers.FIFOT = _fifotRegister;
               _fscc.Registers.IMR = _imrRegister;
               _fscc.Registers.PPR = _pprRegister;
               _fscc.Registers.RAMR = _ramrRegister;
               _fscc.Registers.RAR = _rarRegister;
               _fscc.Registers.SMR = _smrRegister;
               _fscc.Registers.SSR = _ssrRegister;
               _fscc.Registers.TCR = _tcrRegister;
               _fscc.Registers.TMR = _tmrRegister;
               _fscc.Registers.TSR = _tsrRegister;

               // false means that each read will return a single sdlc packet
               // true means that each read will return everything in the buffer (multiple packets)
               _fscc.RxMultiple = _shallWeReceiveMultiple;

               _fscc.ClockFrequency = _clockFreq;

               // if this to true...extra 2 bytes arrive at the end of every frame (first two bytes of STAR)
               // The class processing the data needs to be aware of the extra two bytes
               _fscc.AppendStatus = _shallWeAppendStatus;

               _state = State.Ready;
            }
            else
            {
               throw new Exception("expected the state to be Uninitialized, state was: " + _state.ToString() + " on device " + _name);
            }
         }
      }

      /// <summary>
      /// 
      /// </summary>
      public string Name
      {
         get
         {
            return _name;
         }
      }

      /// <summary>
      /// 
      /// </summary>
      /// <returns></returns>
      public SelfTestResult PerformSelfTest()
      {
         lock (_syncObj)
         {
            throw new NotImplementedException();
         }
      }

      /// <summary>
      /// Read data from the device.
      /// </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)
      {
         lock (_syncObj)
         {
            uint numBytesRead = _fscc.Read(dataRead, (uint)dataRead.Length, _readTimeout);

            return numBytesRead;
         }
      }

      /// <summary>
      /// Soft reset procedure as suggested by the vendor
      /// </summary>
      public void Reset()
      {
         lock (_syncObj)
         {
            _fscc.Registers.CCR0 = _ccr0SofResetValue;

            _fscc.Purge(true, true);

            _fscc.Registers.CCR0 = _ccr0Register;
         }
      }

      /// <summary>
      /// 
      /// </summary>
      /// <param name="timeoutMs"></param>
      public void SetReadTimeout(uint timeoutMs)
      {
         lock (_syncObj)
         {
            _readTimeout = timeoutMs;
         }
      }

      /// <summary>
      /// 
      /// </summary>
      public SelfTestResult SelfTestResult
      {
         get
         {
            return _selfTestResult;
         }
      }

      /// <summary>
      /// 
      /// </summary>
      public State Status
      {
         get
         {
            return _state;
         }
      }

      /// <summary>
      /// 
      /// </summary>
      public void Shutdown()
      {
         lock (_syncObj)
         {
            if (_state == State.Ready)
            {
               _fscc.Dispose();

               _state = State.Uninitialized;
            }
         }
      }

      /// <summary>
      /// Write data to the device
      /// </summary>
      /// <param name="dataToSend">The data to write</param>
      /// <param name="numBytesToWrite">The number of bytes to write</param>
      /// <returns>THe number of bytes that were written</returns>
      public uint Write(byte[] dataToSend, uint numBytesToWrite)
      {
         lock (_syncObj)
         {
            uint numWritten = _fscc.Write(dataToSend, numBytesToWrite);

            if (numWritten != numBytesToWrite)
            {
               throw new Exception("num written " + numWritten + " not as expected: " + numBytesToWrite);
            }

            return numWritten;
         }
      }

      public void Close()
      {
         throw new NotImplementedException();
      }

      public void Open()
      {
         throw new NotImplementedException();
      }

      #endregion
   }
}
