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

namespace BitMeasurementManagerLib
{
	/// <summary>
	/// An interface to sending/receiving data over TCP sockets
	/// </summary>
	internal class BitSimCommDeviceNode : ICommDevice, IDisposable
	{
		#region PrivateClassMembers
		private ICommDevice _commDevice;
		private BitMessageIDs _messageIds;
		private IWorkerInterface _socketReadWorker;
		private Thread _socketReadThread;
		private readonly string _name;
		private SelfTestResult _selfTestResult;
		private State _state;
		#endregion

		#region PrivateFunctions
		/// <summary>
		/// The finalizer. Necessary for quitting the read thread
		/// </summary>
		~BitSimCommDeviceNode()
		{
			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
				try
				{
					if (initialState == State.Ready)
					{
						Shutdown();

						_commDevice.Shutdown();
					}
				}
				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
					}
				}

				// dispose of the resources
				try
				{
					if (initialState == State.Ready)
					{
						_socketReadWorker.Dispose();
						_state = State.Uninitialized;
					}
				}
				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

		#region PublicFuctions

		/// <summary>
		/// 
		/// </summary>
		/// <param name="name">The name of this instance</param>
		/// <param name="messageIds"></param>
		/// <param name="msgHandler"></param>
		/// <param name="commReadWorkerBufferSize"></param>
		/// <param name="readWorkerRestTimeInMs"></param>
		public BitSimCommDeviceNode(string name, ICommDevice commDevice, BitMessageIDs messageIds, MsgDevice msgHandler, uint commReadWorkerBufferSize = 100000, uint readWorkerRestTimeInMs = 10)
		{
			_name = name;

			_commDevice = commDevice;

			_messageIds = messageIds;

			_socketReadWorker = new CommReadWorker(this, msgHandler, commReadWorkerBufferSize, readWorkerRestTimeInMs);
			_socketReadThread = new Thread(_socketReadWorker.DoWork);

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

			_selfTestResult = SelfTestResult.Unknown;

			_state = State.Uninitialized;
		}

		public void Open()
		{
		}

		/// <summary>
		/// 
		/// </summary>
		/// <returns></returns>
		public bool ClearErrors()
		{
			return true;
		}

		/// <summary>
		/// 
		/// </summary>
		public bool DisplayEnabled
		{
			get
			{
				return false;
			}

			set
			{
				throw new NotImplementedException();
			}
		}

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

		public void Close()
		{
			Dispose();
		}

		/// <summary>
		/// Close the device
		/// </summary>
		/*public void Close()
		{
		   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.Close();

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

			  if (didThreadQuit == false)
			  {
				 ErrorLogger.Instance().Write("BitSimCommDeviceNode::Close() - Logging Thread did not quit as expected, aborting it");
				 _socketReadThread.Abort();
			  }
			  else
			  {
				 ErrorLogger.Instance().Write("BitSimCommDeviceNode::Close() - Logging Thread quit successfully after join", ErrorLogger.LogLevel.INFO);
			  }
		   }
		   else
		   {
			  ErrorLogger.Instance().Write("BitSimCommDeviceNode::Close() - Logging Thread quit successfully", ErrorLogger.LogLevel.INFO);
		   }
		}*/

		/// <summary>
		/// 
		/// </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>
		/// 
		/// </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()
		{
			_commDevice.Initialize();
			_state = State.Ready;
		}

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

		/// <summary>
		/// 
		/// </summary>
		/// <returns></returns>
		public SelfTestResult PerformSelfTest()
		{
			return _commDevice.SelfTestResult;
		}

		/// <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 0;
		}

		/// <summary>
		/// </summary>
		public void Reset()
		{
			Close();

			Initialize();
		}

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

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

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

		/// <summary>
		/// 
		/// </summary>
		public void Shutdown()
		{
			const int THREAD_QUIT_TIMEOUT_MS = 3000;

			if (_state == State.Ready)
			{
				// 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)
					{
						ErrorLogger.Instance().Write("BitSimCommDeviceNode::Close() - Logging Thread did not quit as expected, aborting it");
						_socketReadThread.Abort();
					}
					else
					{
						ErrorLogger.Instance().Write("BitSimCommDeviceNode::Close() - Logging Thread quit successfully after join", ErrorLogger.LogLevel.INFO);
					}
				}
				else
				{
					ErrorLogger.Instance().Write("BitSimCommDeviceNode::Close() - Logging Thread quit successfully", ErrorLogger.LogLevel.INFO);
				}
			}

			_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)
		{
			// determine the id and get create the rsp message
			IntPtr pDataPtr = Marshal.AllocHGlobal(dataToSend.Length);
			Marshal.Copy(dataToSend, 0, pDataPtr, dataToSend.Length);
			uint commandId = BitConfigurableMessageHeader.GetMessageId(pDataPtr, (uint)dataToSend.Length);
			Marshal.FreeHGlobal(pDataPtr);

			bool isThereAResponse = _messageIds.IsThereAResponseMessage(commandId);

			// if there is a rsp msg, create a dummy msg
			if (isThereAResponse == true)
			{
				uint rspId = _messageIds.GetResponseId(commandId);
				BitConfigurableMessage rspMessage = BitConfigurableMessageFactory.Instance().RetreiveMessage(rspId);
				BitMsgRxBuffer.Instance().AddMsg(rspMessage);
				//uint msgLen = rspMessage.GetEntireMsgLength();
				//byte[] simData = new byte[msgLen];
				//_msgHandler.AddData();
			}
			else
			{
			}

			return (uint)dataToSend.Length;
		}

		#endregion
	}
}
