//                           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 System.Threading;
using NLog;
using Raytheon.Common;

namespace Raytheon.Instruments
{
   /// <summary>
   /// An interface to sending/receiving data over comm devices
   /// </summary>
   internal class CommDeviceNode : ICommDevice, IDisposable
   {
      #region PrivateClassMembers
      private ICommDevice _commDevice;
      private IWorkerInterface _socketReadWorker;
      private Thread _socketReadThread;
      private readonly string _name;
      private SelfTestResult _selfTestResult;
      private State _state;

      private uint _commReadWorkerBufferSize;
      private uint _readWorkerRestTimeInMs;
      private MsgDevice _msgHandler;
      private readonly ILogger _logger;
      #endregion

      #region PrivateFuctions
      /// <summary>
      /// The finalizer. Necessary for quitting the read thread
      /// </summary>
      ~CommDeviceNode()
      {
         Dispose(false);
      }


      /// <summary>
      /// Quit the threads associated with the node
      /// </summary>
      /// <param name="disposing"></param>
      protected virtual void Dispose(bool disposing)
      {
         if (disposing)
         {
            State initialState = _state;

            // close the socket and threads
            if (initialState == State.Ready)
            {
               Shutdown();
               _commDevice.Shutdown();
            }

            if (initialState == State.Ready)
            {
               _socketReadWorker.Dispose();
               _state = State.Uninitialized;
            }
         }
      }

      #endregion

      #region PublicFuctions

      /// <summary>
      /// The constructor
      /// </summary>
      /// <param name="name">The name of this instance</param>
      /// <param name="commDevice">The communication device</param>
      /// <param name="msgHandler">The message handler for this interface</param>
      /// <param name="commReadWorkerBufferSize">The number of bytes for the buffer internal to this class. Each individual read will read upto this buffer size (or until the timeout happens)</param>
      /// <param name="readWorkerRestTimeInMs">Number of ms to reset between read calls on the comm interface</param>
      public CommDeviceNode(string name, ICommDevice commDevice, MsgDevice msgHandler, uint commReadWorkerBufferSize = 100000, uint readWorkerRestTimeInMs = 10)
      {
         _logger = LogManager.GetCurrentClassLogger();
         _name = name;
         _commDevice = commDevice;

         _commReadWorkerBufferSize = commReadWorkerBufferSize;
         _readWorkerRestTimeInMs = readWorkerRestTimeInMs;
         _msgHandler = msgHandler;

         Open();
      }

      /// <summary>
      /// Starts communication thread
      /// </summary>
      public void Open()
      {
         _socketReadWorker = new CommReadWorker(this, _msgHandler, _commReadWorkerBufferSize, _readWorkerRestTimeInMs);
         _socketReadThread = new Thread(_socketReadWorker.DoWork);

         // start the read thread
         _socketReadThread.Start();

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


      /// <summary>
      /// there is no error msg repository
      /// </summary>
      /// <returns></returns>
      public bool ClearErrors()
      {
         return false;
      }

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

      /// <summary>
      /// 
      /// </summary>
      public string DetailedStatus
      {
         get
         {
            return "This is a Comm Sim Device Node " + _name;
         }
      }

      /// <summary>
      /// 
      /// </summary>
      public void Dispose()
      {
         Dispose(true);

         GC.SuppressFinalize(this);
      }

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

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

      /// <summary>
      /// 
      /// </summary>
      public void Initialize()
      {
         _commDevice.Initialize();
         _state = State.Ready;
      }

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

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

      /// <summary>
      /// Read data from the socket
      /// </summary>
      /// <param name="dataRead">The data that was read</param>
      /// <returns>the number of bytes read</returns>
      public uint Read(ref byte[] dataRead)
      {
         return _commDevice.Read(ref dataRead);
      }

      /// <summary>
      /// Resets communications
      /// </summary>
      public void Reset()
      {
         Close();
         Open();
      }

      /// <summary>
      /// 
      /// </summary>
      /// <param name="readTimeout"></param>
      public void SetReadTimeout(uint readTimeout)
      {
         _commDevice.SetReadTimeout(readTimeout);
      }

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

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

      /// <summary>
      /// Close communications
      /// </summary>
      public void Close()
      {
         Shutdown();
      }

      /// <summary>
      /// Close communications
      /// </summary>
      public void Shutdown()
      {
         if (_state == State.Ready)
         {
            const int THREAD_QUIT_TIMEOUT_MS = 3000;

            // tell the thread to quit
            _socketReadWorker.QuitWork();

            // close the socket which the thread might be blocked on
            _commDevice.Shutdown();

            if (_socketReadThread.IsAlive)
            {
               bool didThreadQuit = _socketReadThread.Join(THREAD_QUIT_TIMEOUT_MS);

               if (didThreadQuit == false)
               {
                  _logger.Debug("Logging Thread did not quit as expected, aborting it");
                  _socketReadThread.Abort();
               }
               else
               {
                  _logger.Debug("Logging Thread quit successfully after join");
               }
            }
            else
            {
               _logger.Debug("Logging Thread quit successfully");
            }
         }

         _state = State.Uninitialized;
      }

      /// <summary>
      /// Send data on the socket
      /// </summary>
      /// <param name="dataToSend">The data to send</param>
      /// <param name="numBytesToWrite">The number of bytes to write from the dataToSend buffer</param>
      /// <returns>The number of bytes sent</returns>
      public uint Write(byte[] dataToSend, uint numBytesToWrite)
      {
         return _commDevice.Write(dataToSend, numBytesToWrite);
      }

      #endregion
   }
}
