//                           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.Collections.Generic;

namespace Raytheon.Common
{
	/// <summary>
	/// Singleton for holding onto message objects
	/// </summary>
	public class AutomationRxMsgBuffer
	{
		// class variables
		private static AutomationRxMsgBuffer _AutomationRxMsgBufferInstance;
		private static object _syncObj = new object();
		private Dictionary<uint, List<AutomationMessage>> _msgList;
		private Dictionary<uint, AutoResetEvent> _receivedMsgEvents;
		private List<uint> _msgIds;

		/// <summary>
		/// The way to get access to this singleton
		/// </summary>
		/// <returns>the instance to this class</returns>
		public static AutomationRxMsgBuffer Instance(List<uint> msgIds = null)
		{
			if (_AutomationRxMsgBufferInstance == null)
			{
				_AutomationRxMsgBufferInstance = new AutomationRxMsgBuffer(msgIds);
			}

			return _AutomationRxMsgBufferInstance;
		}

		/// <summary>
		/// Add a message to this buffer
		/// </summary>
		/// <param name="msg">The message to add</param>
		/// <param name="shouldWeDeleteOthers">flag for if the other messages of this type should be deleted from the buffer</param>
		public void AddMsg(AutomationMessage msg, bool shouldWeDeleteOthers = true)
		{
			lock (_syncObj)
			{
				uint msgId = msg.GetMessageId();

				if (shouldWeDeleteOthers == true)
				{
					ClearList(msgId);
				}

				if (_msgList.ContainsKey(msgId) == false)
				{
					_msgList[msgId] = new List<AutomationMessage>();
				}

				_msgList[msgId].Add(msg);

				_receivedMsgEvents[msgId].Set();
			}
		}

		/// <summary>
		/// Remove all messages from the buffer
		/// </summary>
		public void ClearAllMsgs()
		{
			lock (_syncObj)
			{
				foreach (uint id in _msgList.Keys)
				{
					ClearList(id);
				}
			}
		}

		/// <summary>
		/// Remove all messages of the command type from the buffer
		/// </summary>
		/// <param name="id">The message id to remove</param>
		public void ClearList(uint id)
		{
			lock (_syncObj)
			{
				if (_msgList.ContainsKey(id) == true)
				{
					_msgList[id].Clear();

					_msgList.Remove(id);
				}
			}
		}

		/// <summary>
		/// Gets the oldest message in the buffer
		/// </summary>
		/// <param name="id">The id of the message to get</param>
		/// <returns>The oldest message in the buffer</returns>
		public AutomationMessage GetOldestMessage(uint id)
		{
			lock (_syncObj)
			{
				if (_msgList.ContainsKey(id))
				{
					List<AutomationMessage> list = _msgList[id];

					AutomationMessage oldestMsg = list[0];

					list.RemoveAt(0);

					return oldestMsg;
				}
				else
				{
					throw new Exception("no message exists with id: " + id.ToString());
				}
			}
		}

		/// <summary>
		/// Gets the most recent message from the buffer
		/// </summary>
		/// <param name="id">The id of the message to get</param>
		/// <param name="shouldWeDeleteOthers">flag controlling if the other messages of this type should be deleted</param>
		/// <returns>The message</returns>
		public AutomationMessage GetNewestMessage(uint id, bool shouldWeDeleteOthers = true)
		{
			lock (_syncObj)
			{
				if (_msgList.ContainsKey(id))
				{
					List<AutomationMessage> list = _msgList[id];

					AutomationMessage newestMsg = list[list.Count - 1];

					list.RemoveAt(list.Count - 1);

					if (shouldWeDeleteOthers == true)
					{
						ClearList(id);
					}

					return newestMsg;
				}
				else
				{
					throw new Exception("no message exists with id: " + id.ToString());
				}
			}
		}

		/// <summary>
		/// Get the number of messages of this type in the buffer
		/// </summary>
		/// <param name="id">The id of the message to get</param>
		/// <returns>the number of messages of this type in the buffer</returns>
		public int GetNumMsgsInQueue(uint id)
		{
			lock (_syncObj)
			{
				if (_msgList.ContainsKey(id) == true)
				{
					return _msgList[id].Count;
				}
				else
				{
					return 0;
				}
			}
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="id"></param>
		public void ResetRxEvent(uint id)
		{
			lock (_syncObj)
			{
				_receivedMsgEvents[id].Reset();
			}
		}

		/// <summary>
		/// Wait for a message to get added to the buffer
		/// </summary>
		/// <param name="id">The message id to wait for</param>
		/// <param name="timeoutMs">The amount of time in ms to wait</param>
		/// <returns>true if the message arrived, false if it did not</returns>
		public bool WaitForRspMsg(uint id, int timeoutMs)
		{
			return _receivedMsgEvents[id].WaitOne(timeoutMs);
		}

		/// <summary>
		/// The constructor
		/// </summary>
		private AutomationRxMsgBuffer(List<uint> msgIds)
		{
			_msgList = new Dictionary<uint, List<AutomationMessage>>();

			_msgIds = msgIds;

			// create an event for each msg.
			_receivedMsgEvents = new Dictionary<uint, AutoResetEvent>();

			foreach (uint id in _msgIds)
			{
				_receivedMsgEvents[id] = new AutoResetEvent(false);
			}
		}
	}
}
