//                           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 System.Net.Sockets;
using Raytheon.Instruments;
using Raytheon.Common;

namespace Raytheon.Instruments
{
   /// <summary>
   /// Takes raw data from an ICommDevice and throws it in a buffer
   /// </summary>
   internal class CommReadWorker : IWorkerInterface
   {
      #region PrivateClassMembers
      private ICommDevice _commNode;
      private MsgDevice _msgHandler;
      private bool _threadQuitControl;
      private AutoResetEvent _quitEvent;
      private byte[] _dataRead;
      private readonly uint _timeToRestBetweenReadsInMs;
      #endregion

      #region PublicFuctions

      /// <summary>
      /// Constructor
      /// </summary>
      /// <param name="commNode">The communication interface</param>
      /// <param name="msghandler">The message handler for received data</param>
      /// <param name="bufferSize">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="timeToRestBetweenReadsInMs">Number of ms to rest after a read call</param>
      public CommReadWorker(ICommDevice commNode, MsgDevice msghandler, uint bufferSize, uint timeToRestBetweenReadsInMs)
      {
         _commNode = commNode;
         _threadQuitControl = false;
         _msgHandler = msghandler;
         _quitEvent = new AutoResetEvent(false);
         _dataRead = new byte[bufferSize];
         _timeToRestBetweenReadsInMs = timeToRestBetweenReadsInMs;
      }

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

      /// <summary>
      /// Dispose of this object. Needed for releasing thread/comm resources
      /// </summary>
      public void Dispose()
      {
         try
         {
            Dispose(true);

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

      /// <summary>
      /// Reads the socket and puts data into a buffer
      /// </summary>
      public void DoWork()
      {
         try
         {
            while (_threadQuitControl == false)
            {
               try
               {
                  uint numBytesRead = _commNode.Read(ref _dataRead);

                  // add into buffer
                  if (numBytesRead > 0)
                  {
                     _msgHandler.AddData(_dataRead, numBytesRead);
                  }

                  // not using timeToRestBetweenReadsInMs. Just going to wait 1 ms and get back to the read
                  if (_quitEvent.WaitOne(1))
                  {
                     ErrorLogger.Instance().Write("CommReadWorker::DoWork() - received signal to quit", ErrorLogger.LogLevel.INFO);
                     _threadQuitControl = true;
                  }
               }
               catch (SocketException e)
               {
                  if (e.SocketErrorCode == SocketError.TimedOut)
                  {
                     //expected
                  }
                  else
                  {
                     ErrorLogger.Instance().Write("CommReadWorker::DoWork() - " + e.Message, ErrorLogger.LogLevel.ERROR);
                  }
               }
               catch (Exception e)
               {
                  ErrorLogger.Instance().Write("CommReadWorker::DoWork() - " + e.Message, ErrorLogger.LogLevel.ERROR);
               }
            }

            ErrorLogger.Instance().Write("CommReadWorker::DoWork() - exiting", ErrorLogger.LogLevel.INFO);
         }
         catch (Exception err)
         {
            ErrorLogger.Instance().Write(err.Message + "\r\n" + err.StackTrace);
         }
      }

      /// <summary>
      /// Commands the worker to stop
      /// </summary>
      public void QuitWork()
      {
         _quitEvent.Set();
         _threadQuitControl = true;
      }

      /// <summary>
      /// Dispose of this object
      /// </summary>
      /// <param name="disposing"></param>
      protected virtual void Dispose(bool disposing)
      {
         try
         {
            if (disposing)
            {
               _quitEvent.Dispose();
            }
         }
         catch (Exception err)
         {
            try
            {
               ErrorLogger.Instance().Write(err.Message + "\r\n" + err.StackTrace);
            }
            catch (Exception)
            {
               //Do not rethrow. Exception from error logger that has already been garbage collected
            }
         }
      }
      #endregion
   }
}
