/*-------------------------------------------------------------------------
//                           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.Collections.Generic;
using System.ComponentModel.Composition;
using Raytheon.Communication;
using Raytheon.Communication.Rpc;
using Raytheon.Logging;
using Raytheon.Composition;
using Raytheon.Common;
using System.Xml.Linq;
using Microsoft.Win32;
using System.IO;
using System.ServiceProcess;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Collections.ObjectModel;
using System.Reflection;
using System.IO.Ports;

namespace Raytheon.Instruments
{
	public enum Mode
	{
		/// <summary>
		/// instruments must be managed by the service (like RINSS)
		/// </summary>
		Service,
		/// <summary>
		/// ignore the service and initialize instruments directly
		/// </summary>
		StandAlone,
		/// <summary>
		/// dynamically identify if instrument service is running
		/// </summary>
		Auto
	}

	/// <summary>
	/// hybrid implementation of the instrument manager interface
	/// will check if 
	/// </summary>
	public class GeneralInstrumentManager : IInstrumentManager, IPartImportsSatisfiedNotification
	{
		#region Private Fields

		private readonly bool _haveService;

		private IUms _umsHost;
		private IUmsClient<IInstrumentManager> _instrumentManager;
		private IUmsClient<IRpcInstrumentManagerHost> _rpcInstrumentManagerHost;

		private bool _partsLoaded { get; set; }

		/// <summary>
		/// PartsLocation - where the instrument manager should get it's MEF components
		/// </summary>
		public string _partsLocation { get; set; }
		/// <summary>
		/// ConfigLocation - where the configuration manager stores config files
		/// specifically Instruments.xml
		/// </summary>
		public string _configLocation { get; set; }

		private List<RpcInstrumentDescriptor> _availableInstruments = new List<RpcInstrumentDescriptor>();
		private readonly Dictionary<string, IInstrumentProxyFactory> _factoryMap = new Dictionary<string, IInstrumentProxyFactory>();

		private readonly Dictionary<string, object> _instruments = new Dictionary<string, object>();
		private readonly HashSet<Type> _instrumentTypes = new HashSet<Type>();

      // simulation
      private readonly bool _isThereHardware;

      #endregion

      #region Constants

      private const string NO_SERVER = "Client for communication to the server has not been setup";
		private const string SECTION = "RpcClient";

		private const string RegistryValue = @"ConsumerInstrumentManagerPartsDirectory";

		private const string RegistryValueNoRINSS = @"InstrumentManagerPartsDirectory";

		private const string RegistryKey = @"HKEY_LOCAL_MACHINE\SOFTWARE\Raytheon";

		private const string DefaultName = "Default";
		private const string DefaultIP = "127.0.0.1";
		private const string DefaultPort = "8006";
		private const string DefaultMedia = "Tcp";
		private const string DefaultSerializer = "Fusion";

		#endregion

		#region Imports

		[ImportMany(typeof(IInstrumentFactory))]
		private Lazy<IInstrumentFactory, IInstrumentFactoryInfo>[] InstrumentFactories { get; set; }

		[Import(typeof(IUmsFactory))]
		private Lazy<IUmsFactory> LazyUmsFactory { get; set; }
		private IUmsFactory UmsFactory { get { return LazyUmsFactory?.Value; } }

		[Import(typeof(IConfigurationManager))]
		private Lazy<IConfigurationManager> LazyConfigManager { get; set; }
		private IConfigurationManager ConfigManager { get { return LazyConfigManager?.Value; } }

		[Import(typeof(IUmsClientFactory))]
		private Lazy<IUmsClientFactory> LazyUmsClientFactory { get; set; }
		private IUmsClientFactory UmsClientFactory { get { return LazyUmsClientFactory?.Value; } }

		[Import(typeof(ILogFactory), AllowDefault = true)]
		private Lazy<ILogFactory> LazyLogFactory { get; set; }
		private ILogFactory LogFactory { get { return LazyLogFactory?.Value; } }


		[ImportMany(typeof(IInstrumentProxyFactory))]
		private Lazy<IInstrumentProxyFactory>[] ProxyFactories { get; set; }

		#endregion

		#region Logging
		private static ILogger _logger;

		private static ILogger GetLogger()
		{
			FusionLogManager.Changed += l => _logger = l.GetLogger();
			return _logger;
		}
		#endregion

		/// <summary>
		/// default constructor
		/// in Auto mode the Instrument Manager will use RINSS based on the service running status
		/// in Service mode the Instrument Manager must have the RINSS Service running
		/// in StandAlone mode the Instrument Manager will ignore RINSS
		/// </summary>
		public GeneralInstrumentManager(Mode mode = Mode.Auto)
		{
			_haveService = CheckServiceRunningStatus("RINSS");
			if (mode == Mode.Service && !_haveService)
         {
            throw new Exception("RINSS Service is not running for Service mode");
         }

         if (mode == Mode.StandAlone)
         {
            _haveService = false;
         }

         _logger = GetLogger();
		}
		
		/// <summary>
		/// constructor to be used when no RINSS available to set parts location
		/// </summary>
		/// <param name="partsLocation"></param>
		public GeneralInstrumentManager(string partsLocation, Mode mode = Mode.Auto)
			: this(mode)
		{
			_partsLocation = partsLocation;
		}

		/// <summary>
		/// constructor to be used when no RINSS available to set parts location and configuration location
		/// </summary>
		/// <param name="partsLocation"></param>
		/// <param name="configLocation"></param>
		/// <param name="mode"></param>
		public GeneralInstrumentManager(string partsLocation, string configLocation, bool isThereHardware = true, Mode mode = Mode.Auto)
			: this(partsLocation, mode)
		{
			_configLocation= configLocation;
			_isThereHardware = isThereHardware;

      }

		/// <summary>
		/// Initializes this instance.
		/// </summary>
		public void Initialize()
		{
			try
			{
  			    //Setup the instrument part path
				if (!string.IsNullOrWhiteSpace(_partsLocation) && !Directory.Exists(_partsLocation))
				{
					_logger.Error($"Unable to id parts in this location: {_partsLocation}");
					_partsLocation = string.Empty;
				}

				SetupPath();

				//load the instruments
				if (!_partsLoaded)
				{
					LoadParts();
				}

				if(_haveService)
				{
					// 1. Create the Ums Client
					CreateUmsClient();
					_logger.Debug("created client");

					// 2. Find out what instruments are available
					_availableInstruments = new List<RpcInstrumentDescriptor>(_rpcInstrumentManagerHost.Contract.EnumRpcInstruments());
					_logger.Debug("geting list of availible instruments");

					// 3. Find all the instrument interfaces supported by proxy factories
					InitializeFactories();
					_logger.Debug("initialized all the factories");
				}
				else
				{
					//configure all the instruments found
					ConfigureInstruments();
				}
			}
			catch (CompositionException ex)
			{
				_logger.ErrorException(ex, "Error in composition during Consumer Instrument Manager initialization");
				throw;
			}
			catch (Exception ex)
			{
				_logger.ErrorException(ex, "Error initializing the instrument manager");
				throw;
			}
		}

		/// <summary>
		/// InitializeInstruments - init all the instruments
		/// </summary>
		public void InitializeInstruments()
		{
			_logger.Info("Instrument initialization complete");
		}

      /// <summary>
      /// InitializeInstrument - inits a specific instrument
      /// </summary>
      /// <param name="instName">instrument's unique name</param>
      public void InitializeInstrument(string instName)
      {
      }

      /// <summary>
      /// implementation for IPartImportsSatisfiedNotification interface
      /// </summary>
      public void OnImportsSatisfied()
		{
			if (LogFactory != null && LogFactory != null)
			{
				FusionLogManager.Current = LogFactory;
			}
		}

		/// <summary>
		/// Gets the generic instrument.
		/// </summary>
		/// <param name="name">The name.</param>
		/// <returns></returns>
		public IInstrument GetGenericInstrument(string name)
		{
			_logger.Trace("In ConsumerInstrumentManager in method GetGenericInstrument with name: {0} ", name);
			return GetInstrument<IInstrument>(name);
		}

		/// <summary>
		/// gets a specific instrument by name
		/// the name should match one of the names in Instruments.xml file
		/// </summary>
		/// <typeparam name="T"></typeparam>
		/// <param name="name"></param>
		/// <returns></returns>
		public T GetInstrument<T>(string name) where T : class
		{
			_logger.Trace($"Starting GetInstrument with name: {name}");

			object inst = null;
			if (_haveService)
			{
				try
				{
					string interfaceName = typeof(T).FullName;
					if (typeof(IInstrument).FullName == interfaceName)
					{
						_logger.Trace($"GetInstrument with typeof(IInstrument).FullName == interfaceName: {interfaceName}");

						//get the real interface behind the scenes
						//first find the appropriate factory
						RpcInstrumentDescriptor correctDesc = _availableInstruments.FirstOrDefault((desc) => 0 == string.Compare(name, desc.InstrumentName, true));
						if (null != correctDesc)
						{
							_logger.Trace($"GetInstrument with correctDesc.name: {correctDesc.InstrumentName}");

							string temp = correctDesc.InstrumentInterfaces.FirstOrDefault();
							if (!string.IsNullOrWhiteSpace(temp))
							{
								interfaceName = temp;
							}
						}

						_logger.Debug("Requested generic instrument, found {0} to be the correct interface", interfaceName);
					}

					if (InstrumentIsAvailable(name) && FactoryIsAvailable(interfaceName))
					{
						_logger.Trace($"GetInstrument with InstrumentIsAvailable(name) && FactoryIsAvailable(interfaceName) name: {name}, interfaceName: {interfaceName}");

						IInstrumentProxyFactory factory = _factoryMap.Where((t) => 0 == string.Compare(t.Key, interfaceName, true))
							.Select(t => t.Value)
							.FirstOrDefault();

						if (null != factory)
						{
							inst = factory.GetInstrument(name);
							_logger.Trace($"GetInstrument got an instrument (name: {name}) from factory: {factory}, interfaceName: {interfaceName}");
						}
						else
						{
							_logger.Warn($"Could not find factory for interface: {interfaceName}, instrument: {name}");
						}
					}
				}
				catch (InstrumentException ex)
				{
					_logger.WarnException(ex, ex.Message);
				}
			}
			else
			{
				_instruments.TryGetValue(name.ToLower(), out inst);
			}

			_logger.Trace($"GetInstrument returning with inst: {inst}");

			return inst as T;
		}

		/// <summary>
		/// returns a collection of instrument names
		/// </summary>
		/// <returns></returns>
		public ICollection<string> GetInstrumentNames()
		{
			_logger.Trace("Returning instrument list");

			if (_haveService)
			{
				return _instrumentManager.Contract.GetInstrumentNames();
			}
			else
			{
				return new ReadOnlyCollection<string>(_instruments.Keys.ToList());
			}
		}

		public string[] GetInstrumentNamesArray()
		{
			_logger.Debug("Getting Instrument Names Array");
			return GetInstrumentNames().ToArray();
		}

		/// <summary>
		/// Gets instruments collection
		/// </summary>
		/// <returns></returns>
		public ICollection<object> GetInstruments()
		{
			_logger.Debug("GetInstruments with null");
			return GetInstruments(null);
		}

		/// <summary>
		/// Gets instruments collection by type
		/// </summary>
		/// <param name="type"></param>
		/// <returns></returns>
		public ICollection<object> GetInstruments(Type type)
		{
			_logger.Debug($"Entering GetInstruments with type {type}");

			var instruments = new List<object>();

			if (_haveService)
			{
				if (null == _rpcInstrumentManagerHost)
				{
					_logger.Warn(NO_SERVER);
				}
				else
				{
					//gather all the instruments of the requested type
					if (null == type)
					{
						type = typeof(IInstrument);
					}

					_logger.Debug("GetInstruments geting collection");

					var collection = from avail in _availableInstruments
									 where avail.InstrumentInterfaces.Contains(type.FullName)
									 select avail;

					_logger.Debug("GetInstruments got collection count = " + collection.Count());

					MethodInfo method = typeof(GeneralInstrumentManager).GetMethod("GetInstrument");
					MethodInfo generic = method.MakeGenericMethod(type);

					foreach (var item in collection)
					{
						object[] objs = { item.InstrumentName };
						IInstrument created = generic.Invoke(this, objs) as IInstrument;
						instruments.Add(created);
					}
				}
			}
			else
			{
				if(type == null)
				{
					instruments.AddRange(_instruments.Values);
				}
				else
				{
					foreach (var inst in _instruments.Values)
					{
						if (type.IsAssignableFrom(inst.GetType()))
						{
							instruments.Add(inst);
						}
					}
				}
			}

			return new ReadOnlyCollection<object>(instruments);
		}

		/// <summary>
		/// returns instrument array of specific type
		/// </summary>
		/// <param name="type"></param>
		/// <returns></returns>
		/// <exception cref="ArgumentNullException"></exception>
		public object[] GetInstrumentsArray(Type type)
		{
			if (null == type)
			{
				throw new ArgumentNullException("type", "GetInstrumentsArray, cannot get null instrument types");
			}

			_logger.Debug($"GetInstrumentsArray with type {type}");

			return GetInstruments(type).ToArray();
		}

		/// <summary>
		/// returns instrument array
		/// </summary>
		/// <returns></returns>
		public object[] GetInstrumentsArray()
		{
			_logger.Debug("Get Instruments Array");
			return GetInstruments().ToArray();
		}

		/// <summary>
		/// Shuts down this instance.
		/// Close out the RPC host connection. Does not close / shutdown RINSS or any 
		/// </summary>
		public void Shutdown()
		{
			_logger.Info("Shutting down instruments");

			if (_haveService)
			{
				_rpcInstrumentManagerHost.Close();
			}
			else
			{
				ShutdownInstruments();
			}
			_logger.Info("Instrument shut down complete");
		}

		/// <summary>
		/// Shuts down the instruments.
		/// </summary>
		public void ShutdownInstruments()
		{
		}

		/// <summary>
		/// Shutdowns the instrument.
		/// </summary>
		/// <param name="instName">Name of the inst.</param>
		public void ShutdownInstrument(string instName)
		{
		}

		#region Private Functions
		
		/// <summary>
		/// for the Auto mode will check if RINSS available
		/// </summary>
		/// <param name="serviceName"></param>
		/// <returns></returns>
		private bool CheckServiceRunningStatus(string serviceName)
		{
			if(!DoesServiceExist(serviceName))
				return false;

			ServiceController sc = new ServiceController(serviceName);
			return sc.Status == ServiceControllerStatus.Running;
		}
		private static bool DoesServiceExist(string serviceName)
		{
			return ServiceController.GetServices().Any(serviceController => serviceController.ServiceName.Equals(serviceName));
		}

		/// <summary>
		/// Load parts with RINSS running or without RINSS
		/// </summary>
		private void LoadParts()
		{
			_logger.Debug($"Loading Parts from path {_partsLocation}");

			//install we are assuming will put the needed parts into a directory ./Parts just off the
			//directory where the assembly is
			MefHelper helper = new MefHelper(false, false);
			try
			{
				helper.AddCatalog(_partsLocation, true);
				helper.Container.ComposeParts(this);

				if(_haveService)
				{
					if (null == ConfigManager || null == UmsClientFactory || null == UmsFactory)
					{
						throw new CompositionException($"General Instrument Manager has null MEF components, please check MEF plugin location: {_partsLocation}");
					}
				}
				else
				{
					if (null == ConfigManager || null == InstrumentFactories)
					{
						throw new CompositionException($"Error during MEF composition, check MEF Part Location: {_partsLocation}");
					}
				}

				_partsLoaded = true;
				if(!string.IsNullOrEmpty(_configLocation))
					ConfigManager.ConfigurationStoragePath = _configLocation;
			}
			catch (CompositionException ex)
			{
				_logger.ErrorException(ex, ex.Message);
			}
		}

		/// <summary>
		/// will set up path where either proxy instruments are (in case when RINSS running)
		/// or where the actual instruments are (in case RINSS not running)
		/// </summary>
		/// <param name="defaultPathWinNew"></param>
		/// <param name="defaultPathWinOld"></param>
		/// <param name="keyName"></param>
		/// <param name="valueName"></param>
		private void SetupPath()
		{
         _logger.Trace("Setting up Instrument Manager parts location");
			if (string.IsNullOrEmpty(_partsLocation))
			{
            string pf86 = Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86);
            string pf = Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles);

            //ATEasy and other apps are messing with the previously used ./Parts locations
            //so we have moved to using a registry entry that is created on install and refers
            //to the directory installed to, the PartsLocation property still allows for custom directories
            //to be used for the Consumer Instrument Manager
            string defaultPath = _haveService ? $"{pf86}\\Raytheon\\RINSS-ConsumerIM\\Parts" : $"{pf86}\\Raytheon\\RINSS\\Parts\\CommonModuleInstruments";
				if (Environment.OSVersion.Version.Major < 6)
				{
					//if we are windows xp, then we need to look in a different default directory
					defaultPath = _haveService ? $"{pf}\\Raytheon\\RINSS-ConsumerIM\\Parts" : $"{pf}\\Raytheon\\RINSS\\Parts\\CommonModuleInstruments";
				}

				var keyName = RegistryKey;
				var valueName = _haveService ? RegistryValue : RegistryValueNoRINSS;

				string path = (string)Registry.GetValue(keyName, valueName, defaultPath);

				if (string.IsNullOrWhiteSpace(path) || !Directory.Exists(path))
				{
					path = defaultPath;
				}

				_partsLocation = path;
			}
			_logger.Trace($"Mef parts load Location is: {_partsLocation}");
		}

		/// <summary>
		/// creates Umc client used in remote procedure calls
		/// </summary>
		/// <exception cref="ArgumentNullException"></exception>
		private void CreateUmsClient()
		{
			_logger.Trace("Loading Proxy Instrument Configuration Settings");

			string configName = "ProxyInstruments";
			IConfiguration config = ConfigManager.GetConfiguration(configName);

			if (null == config)
			{
				string errorMessage = string.Format(@"Could not get configuration file, check configuration location for file: {0}", configName);
				_logger.Error(errorMessage);
				throw new ArgumentNullException(errorMessage);
			}

			string rpcName = config.GetConfigurationValue(SECTION, "Name", string.Empty);

			//if the configuration element does not exist, then create the defaults
			if (string.IsNullOrWhiteSpace(rpcName))
			{
				_logger.Info("No Rpc Client Instrument Manager configuration found, creating default");
				config.SetConfigurationValue(SECTION, "Name", DefaultName);
				config.SetConfigurationValue(SECTION, "IPAddress", DefaultIP);
				config.SetConfigurationValue(SECTION, "Port", DefaultPort);
				config.SetConfigurationValue(SECTION, "MediaType", DefaultMedia);
				config.SetConfigurationValue(SECTION, "SerializerType", DefaultSerializer);
			}

			_logger.Trace("Consumer Instrument Manager is creating Ums Client");

			if (null == UmsClientFactory || null == UmsFactory)
			{
				var ex = new ArgumentNullException(string.Format("Ums components are null, please check MEF plugin location: {0}", _partsLocation));
				_logger.ErrorException(ex, ex.Message);
				throw ex;
			}

			string name = config.GetConfigurationValue(SECTION, "Name", DefaultName);
			string ipAddress = config.GetConfigurationValue(SECTION, "IPAddress", DefaultIP);
			string hostPort = config.GetConfigurationValue(SECTION, "Port", DefaultPort);
			string mediaType = config.GetConfigurationValue(SECTION, "MediaType", DefaultMedia);
			string serializerType = config.GetConfigurationValue(SECTION, "SerializerType", DefaultSerializer);

			//we already checked for null above!
			_umsHost = UmsFactory.GetInstance(name, ipAddress, hostPort, UmsInstanceType.Client, mediaType, serializerType);

			//open a client for the instrument manager
			_instrumentManager = UmsClientFactory.GetClient<IInstrumentManager>(_umsHost);
			_instrumentManager.Open();

			//open a client for the host server
			_rpcInstrumentManagerHost = UmsClientFactory.GetClient<IRpcInstrumentManagerHost>(_umsHost);
			_rpcInstrumentManagerHost.Open();
		}

		/// <summary>
		/// will initialize factories for instuments managed under RINSS
		/// </summary>
		private void InitializeFactories()
		{
			_logger.Debug($"InitializeFactories ProxyFactories count = {ProxyFactories.Count()}");

			foreach (var fact in ProxyFactories)
			{
				_logger.Debug("InitializeFactories fact=" + fact.Value);
				//initialize each factory so it can create the clients
				fact.Value.Initialize(_umsHost, UmsClientFactory);

				//map out the factories with their interface names
				var availableInterfaces = fact.Value.GetSupportedInterfaces();
				_logger.Debug($"InitializeFactories availableInterfaces count = {availableInterfaces.Count}");

				foreach (var face in availableInterfaces)
				{
					_logger.Debug($"InitializeFactories Interface = {face}");
					_factoryMap.Add(face.ToLower(), fact.Value);
				}
			}
		}

		/// <summary>
		/// will configure instruments when not managed by Service
		/// </summary>
		/// <exception cref="ConfigurationException"></exception>
		private void ConfigureInstruments()
		{
			_logger.Info("Configuring Instruments");
			IConfiguration config = ConfigManager.GetConfiguration("Instruments");

			if (null == config)
			{
				throw new ConfigurationException("could not find configuration file");
			}

			string rawConfig = config.GetXmlConfiguration("Instruments");
			if (string.IsNullOrWhiteSpace(rawConfig))
			{
				_logger.Warn("No RPC configuration found, creating default 'Instruments'");
				config.SetXmlConfiguration("Instruments", new XElement("Instrument",
					new XElement("Name", "Sample"),
					new XElement("Factory", "Sample")).ToString());
				rawConfig = config.GetXmlConfiguration("Instruments");
			}

			_instrumentTypes.Add(typeof(IInstrument));
			if (0 == InstrumentFactories.Count())
			{
				_logger.Warn($"There are no instrument factories registered using the following path: {Path.GetFullPath(_partsLocation)}");
			}

			XElement xmlConfig = XElement.Parse(rawConfig);
			if (null == xmlConfig)
			{
				throw new ConfigurationException("could not parse configuration file");
			}

			var decendants = xmlConfig.Descendants("Instrument");
			if (null != decendants)
			{
				foreach (var instrument in decendants)
				{
					var instName = instrument.Element("Name").Value;
					var instFactory = instrument.Element("Factory").Value;
					_logger.Trace($"In ConfigureInstruments before factory call with XML name: {instName} and XML factory: {instFactory}");
					var factory = (from f in InstrumentFactories
								   where !string.IsNullOrWhiteSpace(f.Metadata.ModelNumber) && f.Metadata.ModelNumber.Equals(instFactory, StringComparison.OrdinalIgnoreCase)
								   select f).FirstOrDefault();

					if (factory != null)
					{
						object inst = null;
						try
						{
							// instantiate the applicable instrument
                     inst = factory.Value.GetInstrument(instName, !_isThereHardware);
                     _logger.Info($"Creating instrument '{instName}'");
                  }
						catch (Exception ex)
						{
							_logger.WarnException(ex, ex.Message);
							inst = null;
						}

						if (null == inst)
						{
							_logger.Warn($"Could not create the instrument: {instName}");
						}
						else
						{
							var supported = factory.Value.GetSupportedInterfaces();
							if (null != supported)
							{
								//add the instrument
								string txt = $"Adding instrument: Name - {instName}, Type - {inst.GetType()}";
								_instruments.Add(instName.ToLower(), inst);
								foreach (var supportedInterface in supported)
								{
									_instrumentTypes.Add(supportedInterface);
									_logger.Info($"{txt}, Interface - {supportedInterface}");
								}
							}
							else
							{
								_logger.Warn($"Did not see any supported interfaces for: {instName}");
							}
						}
					}
					else
					{
						_logger.Warn($"{instName} did not have a matching factory with supported type {instFactory}");

						if (InstrumentFactories.Count() > 0)
						{
							var factories = new StringBuilder();
							factories.Append("Available Factory types loaded: ");
							foreach (var kvp in InstrumentFactories)
							{
								factories.AppendLine();
								factories.AppendFormat("     {0}", kvp.Metadata.ModelNumber);
							}
							_logger.Info(factories.ToString());
						}
					}
				}
			}
			else
			{
				_logger.Warn("could not find instrument section of configuration");
			}
		}
		
		/// <summary>
		/// checks if instrument is available
		/// </summary>
		/// <param name="name"></param>
		/// <returns></returns>
		private bool InstrumentIsAvailable(string name)
		{
			//check if our host has a instrument by this name available to us
			if (!_availableInstruments.Any(t => 0 == string.Compare(t.InstrumentName, name, true)))
			{
				_logger.Warn($"{name} instrument not available");
				return false;
			}
			return true;
		}

		/// <summary>
		/// checks if instrument factory is available
		/// </summary>
		/// <param name="interfaceName"></param>
		/// <returns></returns>
		private bool FactoryIsAvailable(string interfaceName)
		{
			bool bReturn = false;

			if (typeof(IInstrument).FullName == interfaceName)
			{
				_logger.Debug($"In GeneralInstrumentManager in method FactoryIsAvailable with typeof(IInstrument).FullName == interfaceName : {interfaceName}");
				bReturn = true;
			}
			else
			{
				//check if we have a factory that supports this interface
				var sb = new StringBuilder();
				foreach (KeyValuePair<string, IInstrumentProxyFactory> x in _factoryMap)
				{
					sb.Append("FactoryMap IInstrumentProxyFactory: key = ");
					sb.Append(x.Key);
					sb.Append(" value = ");
					sb.Append(x.Value);
					sb.Append(" supportedInterfaces = ");
					sb.Append(x.Value.GetSupportedInterfaces());
					sb.Append(Environment.NewLine);
				}

				_logger.Debug(sb.ToString());

				var kvp = _factoryMap.Where((t) => 0 == string.Compare(t.Key, interfaceName, true))
					.Select(t => new { t.Key, t.Value })
					.FirstOrDefault();

				if (null == kvp)
				{
					_logger.Warn($"{interfaceName} interface proxy factory not found");
				}
				bReturn = kvp != null;
			}
			return bReturn;
		}

		#endregion

	}
}
