// Ignore Spelling: Geu Sdlc

using System;
using Raytheon.GuidedElectronicsUnit;
using System.Threading;
using Raytheon.Common;
using NLog;

namespace Raytheon.Instruments
{
	public class CommDeviceGeuSdlc : ICommDevice, IDisposable
	{
		#region PrivateClassMembers

		private GuidedElectronicsUnit.GuidedElectronicsUnit _guidanceElectronicsUnit;
		private readonly bool _idQuery = false;
		private readonly bool _reset = false;
		private readonly SelfTestResult _selfTestResult;
		private State _state;
		private readonly string _instrumentDriverSetup;
		private readonly int _pollingRate;
		private readonly string _resourceName;
		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;

		#endregion

		#region PrivatelassFunctions

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

		/// <summary>
		/// Dispose of the resources contained by this object
		/// </summary>
		/// <param name="disposing"></param>
		protected virtual void Dispose(bool disposing)
		{
			if (disposing)
			{
				// close resources
				try
				{
					if (_state == State.Ready)
					{
						_guidanceElectronicsUnit.Close();
						_guidanceElectronicsUnit.Dispose();
						_state = State.Uninitialized;
					}
				}
				catch (Exception)
				{
					try
					{
					}
					catch (Exception)
					{
						//Do not rethrow. Exception from error logger that has already been garbage collected
					}
				}
			}
		}

		/// <summary>
		/// Some HSS corrections
		/// </summary>
		/// <param name="data"></param>
		/// <param name="numDWords"></param>
		private static unsafe void PerformHssSwirl(ref byte[] data, uint numDWords)
		{
			fixed (byte* pBytePtr = &data[0])
			{
				for (int i = 0; i < numDWords; i++)
				{
					// swap the first word
					ushort* pWord1 = (ushort*)pBytePtr + (i * 2);
					*pWord1 = Util.Swap(*pWord1);

					//swap the second word
					ushort* pWord2 = (ushort*)pBytePtr + ((i * 2) + 1);
					*pWord2 = Util.Swap(*pWord2);

					// now swap the dword
					uint* pDWord = (uint*)pBytePtr + i;
					*pDWord = Util.SwapHighAndLowBytes(*pDWord);
				}
			}
		}

		#endregion

		#region PublicClassFunctions

		bool IInstrument.DisplayEnabled { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }
		bool IInstrument.FrontPanelEnabled { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }
		InstrumentMetadata IInstrument.Info => throw new NotImplementedException();

		/// <summary>
		/// CommDevice factory constructor
		/// </summary>
		/// <param name="deviceName"></param>
		/// <param name="configurationManager"></param>
		public CommDeviceGeuSdlc(string deviceName, IConfigurationManager configurationManager, ILogger logger)
		{
			_resourceName = deviceName;
			_guidanceElectronicsUnit = null;
			_selfTestResult = SelfTestResult.Unknown;
			_state = State.Uninitialized;

			_logger = logger;

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

			_idQuery = _configuration.GetConfigurationValue("CommDeviceGeuSdlc", "IdQuery", true);
			_reset = _configuration.GetConfigurationValue("CommDeviceGeuSdlc", "Reset", true);
			_instrumentDriverSetup = _configuration.GetConfigurationValue("CommDeviceGeuSdlc", "InstrumentDriverSetup", "");
			_pollingRate = _configuration.GetConfigurationValue("CommDeviceGeuSdlc", "PollingRate", 10);
		}

		/// <summary>
		/// The constructor. Does not initialize anything. Use Initialize() to create the handle to the hardware
		/// </summary>
		/// <param name="resourceName"></param>
		/// <param name="idQuery"></param>
		/// <param name="reset"></param>
		public CommDeviceGeuSdlc(string resourceName, bool idQuery, bool reset, string instrumentDriverSetup = "", int pollingRate = 10)
		{
			_resourceName = resourceName;
			_idQuery = idQuery;
			_reset = reset;
			_guidanceElectronicsUnit = null;
			_selfTestResult = SelfTestResult.Unknown;
			_state = State.Uninitialized;
			_instrumentDriverSetup = instrumentDriverSetup;
			_pollingRate = pollingRate;
			_logger = LogManager.GetCurrentClassLogger();
		}

		/// <summary>
		/// 
		/// </summary>
		/// <returns></returns>
		bool IInstrument.ClearErrors()
		{
			throw new NotImplementedException();
		}

		/// <summary>
		/// 
		/// </summary>
		public string DetailedStatus
		{
			get
			{
				return "This is a HSS GEU SDLC Device called " + _resourceName;
			}
		}

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

				GC.SuppressFinalize(this);
			}
			catch (Exception err)
			{
				_logger.Error(err.Message + "\r\n" + err.StackTrace);
			}
		}

		/// <summary>
		/// Create a handle to the hardware
		/// </summary>
		public void Initialize()
		{
			const uint AUTO_FWDING_ADDR = 0x00080100;
			const uint WRITE_OFFSET = 0x00200000;
			const uint ENABLE_AUTO_FWDING = 0b0000_0001;
			const uint ENABLE_TLP_HDR = 0b0000_0010;
			//const uint INSERT_MSG_CNT = 0b0000_0100;

			_guidanceElectronicsUnit = new GuidedElectronicsUnit.GuidedElectronicsUnit(_resourceName, _idQuery, _reset,
			   $"QueryInstrStatus=true, Simulate=false, DriverSetup= {_instrumentDriverSetup}, PollingInterval={_pollingRate}");

			_guidanceElectronicsUnit.LowLevel.HSSub9100.EnableIO = ControlState.Enabled;

			_guidanceElectronicsUnit.LowLevel.HSSub9100.WriteRegister(AUTO_FWDING_ADDR + WRITE_OFFSET, ENABLE_AUTO_FWDING | ENABLE_TLP_HDR);

			_state = State.Ready;
		}

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

		/// <summary>
		/// 
		/// </summary>
		/// <returns></returns>
		SelfTestResult IInstrument.PerformSelfTest()
		{
			throw new NotImplementedException();
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="dataRead"></param>
		/// <returns></returns>
		uint ICommDevice.Read(ref byte[] dataRead)
		{

			if (_guidanceElectronicsUnit == null)
			{
				return 0;
			}

			byte[] sdlcMsgs = new byte[0];
			lock (_syncObj)
			{
				// read all of the data that is available
				sdlcMsgs = _guidanceElectronicsUnit.GsKwSyncronousDataLinkControl.FetchMessageData();
			}

            if (sdlcMsgs.Length > dataRead.Length)
            {
                throw new Exception("The data buffer that the host provided is: " + dataRead.Length + " bytes, there are: " + sdlcMsgs.Length + " bytes of SDLC data. Need to increase the host buffer size");
            }
		
			Buffer.BlockCopy(sdlcMsgs, 0, dataRead, 0, sdlcMsgs.Length);

			return (uint)sdlcMsgs.Length;
		}

		/// <summary>
		/// 
		/// </summary>
		void IInstrument.Reset()
		{
			lock (_syncObj)
			{
				_guidanceElectronicsUnit.Close();
				_state = State.Uninitialized;
				Thread.Sleep(500);
				Initialize();
			}
		}

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

		/// <summary>
		/// 
		/// </summary>
		/// <param name="timeoutMs"></param>
		void ICommDevice.SetReadTimeout(uint timeoutMs)
		{
			throw new NotImplementedException();
		}

		/// <summary>
		/// 
		/// </summary>
		void IInstrument.Shutdown()
		{
			lock (_syncObj)
			{
				if (_guidanceElectronicsUnit != null)
				{
					_guidanceElectronicsUnit.LowLevel.HSSub9100.EnableIO = ControlState.Disabled;

					_guidanceElectronicsUnit.Close();
					_guidanceElectronicsUnit.Dispose();
					_state = State.Uninitialized;
				}
			}
		}

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

		/// <summary>
		/// 
		/// </summary>
		/// <param name="data"></param>
		/// <param name="numBytesToWrite"></param>
		/// <returns></returns>
		uint ICommDevice.Write(byte[] data, uint numBytesToWrite)
		{
			lock (_syncObj)
			{
				if (numBytesToWrite % 4 != 0)
				{
					throw new Exception("Data is not dword aligned");
				}

				// do all of the HSS Tx only byte order corrections
				PerformHssSwirl(ref data, numBytesToWrite / 4);

				var tempArr = new uint[data.Length / 4];
				Buffer.BlockCopy(data, 0, tempArr, 0, (int)numBytesToWrite);
				_guidanceElectronicsUnit.GsKwSyncronousDataLinkControl.SendMessage(tempArr);
			}
			return numBytesToWrite;
		}

		public void Close()
		{
		}

		public void Open()
		{
		}

		#endregion
	}
}
