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

namespace MeasurementManagerLib
{
   /// <summary>
   /// This class manages ISwitch instruments (That act as Relays) and provides an abstraction
   /// </summary>
   public class RelayMeasurementManager : IDisposable
	{
		#region PrivateClassMembers
		public enum InitialState
		{
			OPEN,
			CLOSED
		};

		[Serializable]
		public class RelayInfo
		{
			public string RelayModule { get; set; }
			public string Relay { get; set; }

			public List<string> Relays { get; set; } = new List<string>();

			public InitialState InitialState { get; set; }

            public RelayInfo()
            {
                InitialState = InitialState.OPEN;
				RelayModule = string.Empty;
				Relay = string.Empty;
            }

            public RelayInfo(string relayModule, string relay, InitialState initialState)
			{
				RelayModule = relayModule;
				Relay = relay;
				InitialState = initialState;
			}
		};

		private static readonly object _syncObj = new Object();
		private readonly Dictionary<string, ISwitch> _relayCards;
		private readonly Dictionary<string, RelayInfo> _relayMeasurementMap;

		private const string RELAY_MEASUREMENTS = "RelayMeasurements";

		//const char COMMA_DELIM = ',';
		private const char FIELD_DELIM = '|';
		private const char DASH_DELIM = '-';

      private static NLog.ILogger _logger;

      #endregion

      #region PrivateClassFunctions

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


		/// <summary>
		/// Confirm each relay entry in the measurement file refers back to a card in the instrument file
		/// </summary>
		private void CheckForValidRelayFormat()
		{
			foreach (KeyValuePair<string, RelayInfo> entry in _relayMeasurementMap)
			{
				RelayInfo relays = entry.Value;

				if (_relayCards.ContainsKey(relays.RelayModule) == false)
				{
					throw new Exception("relay card field does not exist: " + relays.RelayModule);
				}
			}
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="disposing"></param>
		protected virtual void Dispose(bool disposing)
		{
			try
			{
				if (disposing)
				{
					foreach (KeyValuePair<string, ISwitch> relayCard in _relayCards)
					{
						if (_relayCards[relayCard.Key] != null)
						{
							_relayCards[relayCard.Key].Shutdown();
						}
					}
				}
			}
			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>
		/// Parses the ini file for measurement definitions
		/// Populates the Dictionaries with the enum names and ini file data
		/// </summary>
		private void ParseMeasurementDef(string defFile)
		{
			IniFile iniReader = new IniFile(defFile);

			// read in all of the sections
			List<string> keys = iniReader.ReadAllKeys(RELAY_MEASUREMENTS);

			foreach (string key in keys)
			{
				string value = iniReader.ReadValue(RELAY_MEASUREMENTS, key);

				List<string> field = value.Split(FIELD_DELIM).ToList();

				if (field.Count != 2)
				{
					throw new Exception("expected two fields on line: " + value);
				}

				string relayItem = field[0];

				List<string> relayDef = relayItem.Split(DASH_DELIM).ToList();

				if (relayDef.Count != 2)
				{
					throw new Exception("Relay Entry : " + relayItem + " did not have two tokens as expected");
				}

				string relayCard = relayDef[0].Trim().ToUpper();
				string relay = relayDef[1].Trim();

				InitialState initState = (InitialState)Enum.Parse(typeof(InitialState), field[1], true);

				RelayInfo relayInfo = new RelayInfo(relayCard, relay, initState);

				_relayMeasurementMap[key.ToUpper()] = relayInfo;
			}
		}
		#endregion

		#region PublicClassFunctions

		/// <summary>
		/// constructor that uses instrument manager for building instruments
		/// </summary>
		/// <param name="instrumentManager"></param>
		/// <param name="instrumentNames"></param>
		/// <param name="logFileName"></param>
		public RelayMeasurementManager(IInstrumentManager instrumentManager, List<string> instrumentNames, string configFileName = "")
		{
         _logger = LogManager.GetCurrentClassLogger();
			
			_relayMeasurementMap = new Dictionary<string, RelayInfo>();
			_relayCards = new Dictionary<string, ISwitch>();

			foreach (string name in instrumentNames)
			{
				_relayCards[name] = instrumentManager.GetInstrument<ISwitch>(name);
				_relayCards[name]?.Initialize();
			}

			if(string.IsNullOrEmpty(configFileName))
			{
				configFileName = "RelayMeasurementManager.xml";
			}

			ConfigurationFile configurationFile = new ConfigurationFile(configFileName);

			List<RelayInfo> relayInfoList = configurationFile.ReadList(RELAY_MEASUREMENTS, "Relays", new List<RelayInfo>());

			foreach (var relayInfo in relayInfoList)
			{
				_relayMeasurementMap[relayInfo.RelayModule] = relayInfo;
			}

			CheckForValidRelayFormat();
		}

		/// <summary>
		/// Dispose of this objects resources
		/// </summary>
		public void Dispose()
		{
			try
			{
				lock (_syncObj)
				{
					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>
		/// 
		/// </summary>
		/// <param name="measurementName"></param>
		public void ConnectRelay(string measurementName)
		{
			lock (_syncObj)
			{
				if (_relayMeasurementMap.ContainsKey(measurementName.ToUpper()) == false)
				{
					throw new Exception("could not find measurement: " + measurementName);
				}

				//get the card and relay
				RelayInfo relayInfo = _relayMeasurementMap[measurementName];

				//close all defined relays for measurement
				if (relayInfo.Relays.Any())
				{
					foreach (var relay in relayInfo.Relays) 
					{
						_relayCards[relayInfo.RelayModule.ToUpper()].Connect(relay);
					}
				}

				//close the relay
				if (!string.IsNullOrEmpty(relayInfo.Relay))
				{
					_relayCards[relayInfo.RelayModule.ToUpper()].Connect(relayInfo.Relay);
				}
			}
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="measurementName"></param>
		public void DisconnectRelay(string measurementName)
		{
			lock (_syncObj)
			{
				if (_relayMeasurementMap.ContainsKey(measurementName.ToUpper()) == false)
				{
					throw new Exception("could not find measurement: " + measurementName);
				}

				//get the card and relay
				RelayInfo relayInfo = _relayMeasurementMap[measurementName];

				//open all defined relays for measurement
				if (relayInfo.Relays.Any())
				{
					foreach (var relay in relayInfo.Relays)
					{
						_relayCards[relayInfo.RelayModule.ToUpper()].Disconnect(relay);
					}
				}

				//open the relays
				if (!string.IsNullOrEmpty(relayInfo.Relay))
				{
					_relayCards[relayInfo.RelayModule.ToUpper()].Disconnect(relayInfo.Relay);
				}
			}
		}

		/// <summary>
		/// Return a list of relay measurements
		/// </summary>
		/// <returns>A list of relays</returns>
		public List<string> GetRelayMeasurementList()
		{
			lock (_syncObj)
			{
				List<string> relayList = new List<string>();

				foreach (KeyValuePair<string, RelayInfo> name in _relayMeasurementMap)
				{
					relayList.Add(name.Key);
				}

				return relayList;
			}
		}

		/// <summary>
		/// 
		/// </summary>
		public void ResetToInitialState()
		{
			lock (_syncObj)
			{
				foreach (KeyValuePair<string, RelayInfo> item in _relayMeasurementMap)
				{
					InitialState initState = item.Value.InitialState;
					string relayItem = item.Key;

					if (initState == InitialState.CLOSED)
					{
						ConnectRelay(relayItem);
					}
					else
					{
						DisconnectRelay(relayItem);
					}
				}
			}
		}

		/// <summary>
		/// Command a self test on the Relay instrument
		/// </summary>
		/*public uint SelfTest()
		{
		   lock (_syncObj)
		   {
			  SelfTestResult result = _dio.PerformSelfTest();

			  return (uint)result;
		   }
		}*/

		#endregion
	}
}
