//                           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 NLog;
using Raytheon.Instruments;
using System;
using System.Collections.Generic;
using System.IO;

namespace MeasurementManagerLib
{
   /// <summary>
   /// This class manages IVideoRecorder instruments and provides an abstraction
   /// </summary>
   public class VideoRecorderMeasurementManager : IDisposable
	{
		#region PublicClassMembers
		public enum AttributeType
		{
			STRING,
			INT,
			FLOAT
		};

		public struct NcdfAttribute
		{
			public AttributeType type;
			public string key;
			public string value;
		};
		#endregion

		#region PrivateClassMembers
		private IVideoRecorder _videoRecorder;
      private static NLog.ILogger _logger;
      #endregion

      #region PrivateClassFunctions

      /// <summary>
      /// 
      /// </summary>
      /// <param name="disposing"></param>
      protected virtual void Dispose(bool disposing)
		{
			if (disposing)
			{
				if (_videoRecorder != null)
				{
					try
					{
						_videoRecorder.Shutdown();
					}
					catch (Exception err)
					{
						_logger?.Error(err.Message + "\r\n" + err.StackTrace);
					}
				}
			}
		}
		#endregion

		#region PublicClassFunctions

		public VideoRecorderMeasurementManager(IInstrumentManager instrumentManager, string deviceName)
		{
         _logger = LogManager.GetCurrentClassLogger();

			_videoRecorder = instrumentManager.GetInstrument<IVideoRecorder>(deviceName);
			_videoRecorder?.Initialize();
		}

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

		/// <summary>
		/// Read the attributes from attributeFile(Created by CreateAttributeFile()) and insert them into videoFile
		/// </summary>
		/// <param name="videoFile"></param>
		/// <param name="attributeFile"></param>
		public void AddNcdfAttributes(string videoFile, string attributeFile)
		{
			_videoRecorder.AddNcdfAttributes(videoFile, attributeFile);
		}

		/// <summary>
		/// Connect to the video recorder
		/// </summary>
		public void Connect()
		{
			_videoRecorder.Connect();
		}

		/// <summary>
		/// Create an attribute file that the video recorders can consume
		/// </summary>
		/// <param name="fileName">The name of the file to create</param>
		/// <param name="attributeList">A list of attributes to place in the file</param>
		public static void CreateAttributeFile(string fileName, List<NcdfAttribute> attributeList)
		{
			const char DELIM = '\t';

			// Create a file to write to.
			using (StreamWriter writer = File.CreateText(fileName))
			{
				foreach (NcdfAttribute attribute in attributeList)
				{
					string type = Convert.ToInt32(attribute.type).ToString();
					string lineToWrite = type + DELIM + attribute.key + DELIM + attribute.value;
					writer.WriteLine(lineToWrite);
				}
			}
		}

		/// <summary>
		/// Disconnect from the video recorder
		/// </summary>
		public void Disconnect()
		{
			_videoRecorder.Disconnect();
		}

		/// <summary>
		/// Dispose of this object
		/// </summary>
		public void Dispose()
		{
			try
			{
				Dispose(true);

				GC.SuppressFinalize(this);
			}
			catch (Exception err)
			{
				try
				{
					_logger?.Error(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 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 collect</param>
		/// <param name="saveFormat">0 for binary, 1 for ncdf</param>
		public void GrabVideoToDisk(uint numberOfFrames, uint saveFormat)
		{
			var saveFormatTemp = VideoSaveFormat.BIN;

			if ((VideoSaveFormat)saveFormat == VideoSaveFormat.BIN)
			{
				saveFormatTemp = VideoSaveFormat.BIN;
			}
			else if ((VideoSaveFormat)saveFormat == VideoSaveFormat.NCDF)
			{
				saveFormatTemp = VideoSaveFormat.NCDF;
			}
			else
			{
				throw new Exception("saveFormat must be 0 or 1: " + saveFormat.ToString());
			}

			_videoRecorder.GrabVideoToDisk(numberOfFrames, saveFormatTemp);
		}

		/// <summary>
		/// Query the video system for its hard drive status
		/// </summary>
		/// <param name="driveSpace1">The number of free Gigabytes</param>
		/// <param name="driveSpace2">The number of free Gigabytes</param>
		/// <param name="driveSpace3">The number of free Gigabytes</param>
		/// <param name="driveSpace4">The number of free Gigabytes</param>
		public void QueryHardDrive(ref float driveSpace1, ref float driveSpace2, ref float driveSpace3, ref float driveSpace4)
		{
			_videoRecorder.QueryHardDrive(ref driveSpace1, ref driveSpace2, ref driveSpace3, ref driveSpace4);
		}

		/// <summary>
		/// Move a file to new location/new name
		/// </summary>
		/// <param name="fromFile">The abs location of the file to move</param>
		/// <param name="toFile">The abs location to move to</param>
		/// <param name="moveControl">0 to move the file (via a rename on the recording system), 1 to copy the file and then delete the orginal (may take a long time if file is large)</param>
		public void MoveFile(string fromFile, string toFile, uint moveControl)
		{
			MoveControl moveControlTemp = MoveControl.MOVE;

			if ((MoveControl)moveControl == MoveControl.MOVE)
			{
				moveControlTemp = MoveControl.MOVE;
			}
			else if ((MoveControl)moveControl == MoveControl.COPY_AND_DELETE)
			{
				moveControlTemp = MoveControl.COPY_AND_DELETE;
			}
			else
			{
				throw new Exception("moveControl must be 0 or 1: " + moveControlTemp.ToString());
			}

			_videoRecorder.MoveFile(fromFile, toFile, moveControlTemp);
		}

		/// <summary>
		/// Command the video recorder to display live video
		/// </summary>
		public void ShowLiveVideo()
		{
			_videoRecorder.ShowLiveVideo();
		}

		/// <summary>
		/// Command the video recorder to stop live video display
		/// </summary>
		public void StopLiveVideo()
		{
			_videoRecorder.StopLiveVideo();
		}

		/// <summary>
		/// After calling GrabVideoToDisk(), use this function to know when the grab is complete
		/// </summary>
		/// <param name="timeoutms">The number of milliseconds to wait for the grab complete message</param>
		/// <returns></returns>
		public string WaitForGrabToComplete(int timeoutms)
		{
			return _videoRecorder.WaitForGrabToComplete(timeoutms);
		}

		#endregion
	}
}
