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


// Ignore Spelling: Ncdf

using NLog;
using Raytheon.Common;
using System;
using System.Collections.Generic;

namespace Raytheon.Instruments
{
   /// <summary>
   /// The interface for the test controller to automate the VRS system
   /// </summary>
   public class VideoRecorderVrsClient : IVideoRecorder
	{
		#region PrivateClassMembers
		private readonly string _remoteAddress;
		private readonly int _remotePort;
		private readonly int _localPort;
		private readonly CommDeviceNode _udpNode;
		private readonly ICommDevice _commDevice;
		private readonly uint _parseBufferSize;
		private static readonly object _syncObj = new Object();
		/// <summary>
		/// NLog logger
		/// </summary>
		private readonly ILogger _logger;
		/// <summary>
		/// Raytheon configuration
		/// </summary>
		private readonly IConfigurationManager _configurationManager;
		private readonly IConfiguration _configuration;

		public string DetailedStatus => throw new NotImplementedException();

		public bool DisplayEnabled { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }
		public bool FrontPanelEnabled { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }

		public InstrumentMetadata Info => throw new NotImplementedException();

		public string Name { get; set; }

		public SelfTestResult SelfTestResult => throw new NotImplementedException();

		public State Status => throw new NotImplementedException();
		#endregion

		#region PrivateClassFunctions

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

		/// <summary>
		/// Dispose of this object
		/// </summary>
		/// <param name="disposing"></param>
		protected virtual void Dispose(bool disposing)
		{
			try
			{
				if (disposing)
				{
					Disconnect();
					_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
				}
			}

			try
			{
				if (disposing)
				{
					_udpNode.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

		#region PublicClassFunctions

		/// <summary>
		/// The constructor
		/// </summary>
		/// <param name="remoteAddress">The remote computer address</param>
		/// <param name="remotePort">The remote computer port</param>
		/// <param name="localPort">the local computer port</param>
		/// <param name="parseBufferSize">the parse buffer size in bytes</param>
		public VideoRecorderVrsClient(string remoteAddress, int remotePort, int localPort, uint parseBufferSize)
		{
			_logger = LogManager.GetCurrentClassLogger();

			// init error logger singleton
			ErrorLogger.Instance().Write("beginning", ErrorLogger.LogLevel.INFO);

			_remoteAddress = remoteAddress;

			_remotePort = remotePort;

			_localPort = localPort;

			_parseBufferSize = parseBufferSize;

			List<uint> msgIds = new List<uint>();

			foreach (uint id in Enum.GetValues(typeof(VrsAutomationMsgIds)))
			{
				msgIds.Add(id);
			}

			AutomationRxMsgBuffer.Instance(msgIds);

			VrsClientMsgHandler msghandler = new VrsClientMsgHandler(_parseBufferSize);

			_commDevice = new CommDeviceUdp("CommDeviceUdp", _localPort, _remotePort, _remoteAddress);

			_udpNode = new CommDeviceNode("CommDeviceNode", _commDevice, msghandler);

			msghandler.SetCommInterface(_udpNode);
		}

		/// <summary>
		/// updated constructor
		/// </summary>
		/// <param name="name"></param>
		/// <param name="configurationManager"></param>
		/// <param name="logger"></param>
		public VideoRecorderVrsClient(string name, IConfigurationManager configurationManager, ILogger logger)
		{
			Name = name;
			_logger = logger;

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

			_remoteAddress = _configuration.GetConfigurationValue("VideoRecorderVrsClient", "RemoteAddress", "127.0.0.1");
			_remotePort = _configuration.GetConfigurationValue("VideoRecorderVrsClient", "RemotePort", 0);
			_localPort = _configuration.GetConfigurationValue("VideoRecorderVrsClient", "LocalPort", 0);
			_parseBufferSize = _configuration.GetConfigurationValue<uint>("VideoRecorderVrsClient", "LocalPort", 1024);

			List<uint> msgIds = new List<uint>();
			foreach (uint id in Enum.GetValues(typeof(VrsAutomationMsgIds)))
			{
				msgIds.Add(id);
			}

			AutomationRxMsgBuffer.Instance(msgIds);

			VrsClientMsgHandler msghandler = new VrsClientMsgHandler(_parseBufferSize);

			_commDevice = new CommDeviceUdp("CommDeviceUdp", _localPort, _remotePort, _remoteAddress);
			_udpNode = new CommDeviceNode("CommDeviceNode", _commDevice, msghandler);

			msghandler.SetCommInterface(_udpNode);
		}

		/// <summary>
		/// Send a message to the VRS and command it to add ncdf attributes to a video file
		/// </summary>
		/// <param name="videoFile">The video file to add attributes to</param>
		/// <param name="attributeFile">The attribute file</param>
		public void AddNcdfAttributes(string videoFile, string attributeFile)
		{
			lock (_syncObj)
			{
				AutomationMessage addAttributesMsg = new VrsAddAttributesCmdMessage(videoFile, attributeFile);

				AutomationMessage addAttributesRspMsg = new VrsAddAttributesRspMessage(false);

				CommUtil.Instance().SendCommandGetResponse(_udpNode, addAttributesMsg, ref addAttributesRspMsg);

				VrsAddAttributesRspMessage tempRsp = (VrsAddAttributesRspMessage)addAttributesRspMsg;

				bool wasConnectMsgSuccessful = tempRsp.GetSuccessfulFlag();

				ErrorLogger.Instance().Write("init complete", ErrorLogger.LogLevel.INFO);

				if (wasConnectMsgSuccessful == false)
				{
					throw new Exception("VrsAddAttributesRspMessage returned a successful flag of false");
				}
			}
		}

		/// <summary>
		/// Connect to the VRS system
		/// </summary>
		public void Connect()
		{
			lock (_syncObj)
			{
				// send the vrs a message just to make sure the two apps are talking
				AutomationMessage connectMsg = new VrsConnectCmdMessage();

				AutomationMessage connectRspMsg = new VrsConnectRspMessage(false);

				CommUtil.Instance().SendCommandGetResponse(_udpNode, connectMsg, ref connectRspMsg);

				VrsConnectRspMessage tempRsp = (VrsConnectRspMessage)connectRspMsg;

				bool wasConnectMsgSuccessful = tempRsp.GetSuccessfulFlag();

				ErrorLogger.Instance().Write("init complete", ErrorLogger.LogLevel.INFO);

				if (wasConnectMsgSuccessful == false)
				{
					throw new Exception("VrsConnectRspMessage returned a successful flag of false");
				}
			}
		}

		/// <summary>
		/// Disconnect from the VRS
		/// </summary>
		public void Disconnect()
		{
			lock (_syncObj)
			{
				// send the vrs a message just to make sure the two apps are talking
				AutomationMessage disconnectMsg = new VrsDisconnectCmdMessage();

				AutomationMessage disconnectRspMsg = new VrsDisconnectRspMessage(false);

				CommUtil.Instance().SendCommandGetResponse(_udpNode, disconnectMsg, ref disconnectRspMsg);

				VrsDisconnectRspMessage tempRsp = (VrsDisconnectRspMessage)disconnectRspMsg;

				bool wasConnectMsgSuccessful = tempRsp.GetSuccessfulFlag();

				ErrorLogger.Instance().Write("init complete", ErrorLogger.LogLevel.INFO);

				if (wasConnectMsgSuccessful == false)
				{
					throw new Exception("VrsDisconnectRspMessage returned a successful flag of false");
				}
			}
		}

		/// <summary>
		/// Dispose of this object
		/// </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>
		/// record video to the VRS hard drive. This function returns once the grab has begun, Use WaitForGrabToComplete() to know when the grab is completed
		/// </summary>
		/// <param name="numberOfFrames">The number of frames to grab</param>
		/// <param name="format">The save file format</param>
		public void GrabVideoToDisk(uint numberOfFrames, VideoSaveFormat format)
		{
			const int RSP_TIMEOUT = 20000;

			lock (_syncObj)
			{
				//reset the event for WaitForGrabComplete
				AutomationRxMsgBuffer.Instance().ResetRxEvent((uint)VrsAutomationMsgIds.VRS_GRAB_TO_DISK_COMPLETE_RSP);

				// send the vrs a message just to make sure the two apps are talking
				AutomationMessage grabCmd = new VrsGrabToDiskCmdMessage(numberOfFrames, format);

				AutomationMessage grabRsp = new VrsGrabToDiskRspMessage(false);

				CommUtil.Instance().SendCommandGetResponse(_udpNode, grabCmd, ref grabRsp, RSP_TIMEOUT);

				VrsGrabToDiskRspMessage tempRsp = (VrsGrabToDiskRspMessage)grabRsp;

				bool wasConnectMsgSuccessful = tempRsp.GetSuccessfulFlag();

				ErrorLogger.Instance().Write("init complete", ErrorLogger.LogLevel.INFO);

				if (wasConnectMsgSuccessful == false)
				{
					throw new Exception("VrsGrabToDiskRspMessage returned a successful flag of false");
				}
			}
		}

		/// <summary>
		/// Get the number of GB free on the VRS system.
		/// </summary>
		/// <param name="driveSpace1">Drive 1 free space in GBs</param>
		/// <param name="driveSpace2">Drive 2 free space in GBs</param>
		/// <param name="driveSpace3">Drive 3 free space in GBs</param>
		/// <param name="driveSpace4">Drive 4 free space in GBs</param>
		public void QueryHardDrive(ref float driveSpace1, ref float driveSpace2, ref float driveSpace3, ref float driveSpace4)
		{
			lock (_syncObj)
			{
				// send the vrs a message just to make sure the two apps are talking
				AutomationMessage msg = new VrsQueryHardDriveCmdMessage();

				AutomationMessage rsp = new VrsQueryHardDriveRspMessage(false, 0, 0, 0, 0);

				CommUtil.Instance().SendCommandGetResponse(_udpNode, msg, ref rsp);

				VrsQueryHardDriveRspMessage tempRsp = (VrsQueryHardDriveRspMessage)rsp;

				bool wasConnectMsgSuccessful = tempRsp.GetSuccessfulFlag();

				ErrorLogger.Instance().Write("init complete", ErrorLogger.LogLevel.INFO);

				if (wasConnectMsgSuccessful == false)
				{
					throw new Exception("VrsQueryHardDriveRspMessage returned a successful flag of false");
				}

				tempRsp.GetDriveSpace(ref driveSpace1, ref driveSpace2, ref driveSpace3, ref driveSpace4);
			}
		}

		/// <summary>
		/// Moves a file from one location to another
		/// </summary>
		/// <param name="fromFile"></param>
		/// <param name="toFile"></param>
		/// <param name="control"></param>
		public void MoveFile(string fromFile, string toFile, MoveControl control)
		{
			lock (_syncObj)
			{
				// send the vrs a message just to make sure the two apps are talking
				AutomationMessage cmd = new VrsMoveFileCmdMessage(fromFile, toFile, control);

				AutomationMessage rsp = new VrsMoveFileRspMessage(false);

				CommUtil.Instance().SendCommandGetResponse(_udpNode, cmd, ref rsp);

				VrsMoveFileRspMessage tempRsp = (VrsMoveFileRspMessage)rsp;

				bool wasConnectMsgSuccessful = tempRsp.GetSuccessfulFlag();

				if (wasConnectMsgSuccessful == false)
				{
					throw new Exception("VrsMoveFileRspMessage returned a successful flag of false");
				}
			}
		}

		/// <summary>
		/// Start displaying live video on the VRS
		/// </summary>
		public void ShowLiveVideo()
		{
			lock (_syncObj)
			{
				// send the vrs a message just to make sure the two apps are talking
				AutomationMessage cmd = new VrShowLiveVideoCmdMessage();

				AutomationMessage rsp = new VrShowLiveVideoRspMessage(false);

				CommUtil.Instance().SendCommandGetResponse(_udpNode, cmd, ref rsp);

				VrShowLiveVideoRspMessage tempRsp = (VrShowLiveVideoRspMessage)rsp;

				bool wasConnectMsgSuccessful = tempRsp.GetSuccessfulFlag();

				ErrorLogger.Instance().Write("init complete", ErrorLogger.LogLevel.INFO);

				if (wasConnectMsgSuccessful == false)
				{
					throw new Exception("VrShowLiveVideoRspMessage returned a successful flag of false");
				}
			}
		}

		/// <summary>
		/// Stop displaying video on the VRS
		/// </summary>
		public void StopLiveVideo()
		{
			lock (_syncObj)
			{
				// send the vrs a message just to make sure the two apps are talking
				AutomationMessage cmd = new VrStopLiveVideoCmdMessage();

				AutomationMessage rsp = new VrStopLiveVideoRspMessage(false);

				CommUtil.Instance().SendCommandGetResponse(_udpNode, cmd, ref rsp);

				VrStopLiveVideoRspMessage tempRsp = (VrStopLiveVideoRspMessage)rsp;

				bool wasConnectMsgSuccessful = tempRsp.GetSuccessfulFlag();

				ErrorLogger.Instance().Write("init complete", ErrorLogger.LogLevel.INFO);

				if (wasConnectMsgSuccessful == false)
				{
					throw new Exception("VrStopLiveVideoRspMessage returned a successful flag of false");
				}
			}
		}

		/// <summary>
		/// A blocking function that waits for a video capture to complete
		/// </summary>
		/// <param name="timeoutms"></param>
		/// <returns></returns>
		public string WaitForGrabToComplete(int timeoutms)
		{
			lock (_syncObj)
			{
				bool didWeGetRsp = AutomationRxMsgBuffer.Instance().WaitForRspMsg((uint)VrsAutomationMsgIds.VRS_GRAB_TO_DISK_COMPLETE_RSP, timeoutms);

				if (didWeGetRsp == true)
				{
					VrsGrabToDiskCompleteRspMessage rsp = (VrsGrabToDiskCompleteRspMessage)AutomationRxMsgBuffer.Instance().GetNewestMessage((uint)VrsAutomationMsgIds.VRS_GRAB_TO_DISK_COMPLETE_RSP);

					return rsp.GetFileName();
				}
				else
				{
					throw new Exception("Timed out waiting for response");
				}
			}
		}

		public bool ClearErrors()
		{
			return false;
		}

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

		public SelfTestResult PerformSelfTest()
		{
			throw new NotImplementedException();
		}

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

			Connect();
		}

		/// <summary>
		/// 
		/// </summary>
		/// <exception cref="NotImplementedException"></exception>
		public void Shutdown()
		{
			Dispose(true);
		}

		public byte[] GetSmallVideoFrame(string name, int nFrames, bool sensor, ref uint numBytesRead, bool deleteVideoFile)
		{
			throw new NotImplementedException();
		}
		#endregion
	}
}
