using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.IO;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Threading;
using Decal.Adapter.IDQueue;
using Decal.Adapter.Messages;
using Decal.Adapter.Support;
using Decal.Adapter.Wrappers;
using Decal.Interop.Core;
using Decal.Interop.D3DService;
using Decal.Interop.DHS;
using Decal.Interop.Filters;
using Decal.Interop.Inject;
using Decal.Interop.Render;
using Microsoft.Win32;

namespace Decal.Adapter;

/// <summary>
/// Lifetime Service required for the .NET Surrogate to function. 
/// </summary>
[CLSCompliant(true)]
public sealed class CoreManager : MarshalByRefObject
{
	private struct sAssemblyPreloadData
	{
		public string AssemblyName;

		public string Path;
	}

	private static bool myServiceRunning;

	private static CoreManager myService;

	private DecalCoreClass myDecal;

	private ACHooksClass myHooks;

	private PluginSite mySitev1;

	private DecalWrapper myWrappedDecal;

	private HooksWrapper myWrappedHooks;

	private global::Decal.Adapter.Wrappers.HotkeySystem myDHS;

	private CharacterFilter myCharacterFilter;

	private WorldFilter myWorldFilter;

	private global::Decal.Adapter.Wrappers.EchoFilter2 myEchoFilter;

	private D3DService myD3DService;

	private RenderServiceWrapper myRenderService;

	private FairIDQueue myIDQueue;

	private Collection<string> myPaths = new Collection<string>();

	private Dictionary<AppDomain, Dictionary<string, Assembly>> myAssemblies = new Dictionary<AppDomain, Dictionary<string, Assembly>>();

	private int myAssemblyPreloadCounter = 1;

	private bool myAssemblyPreloadStarted;

	private Dictionary<Type, FilterBase> myFilters = new Dictionary<Type, FilterBase>();

	private Dictionary<string, FilterBase> myNamedFilters = new Dictionary<string, FilterBase>();

	private Dictionary<Type, ServiceBase> myServices = new Dictionary<Type, ServiceBase>();

	private Dictionary<string, ServiceBase> myNamedServices = new Dictionary<string, ServiceBase>();

	private Dictionary<string, PluginBase> myPlugins = new Dictionary<string, PluginBase>();

	private EventHandler<ItemSelectedEventArgs> myItemSelected;

	private EventHandler<ItemDestroyedEventArgs> myItemDestroyed;

	private EventHandler<ChatParserInterceptEventArgs> myChatParserIntercept;

	private EventHandler<RegionChange3DEventArgs> myRegionChange3D;

	private EventHandler<ChatTextInterceptEventArgs> myChatTextIntercept;

	private EventHandler<StatusTextInterceptEventArgs> myStatusTextIntercept;

	private EventHandler<MessageProcessedEventArgs> myMessageProcessed;

	private EventHandler<ContainerOpenedEventArgs> myContainerOpened;

	private EventHandler<ChatClickInterceptEventArgs> myChatClickIntercept;

	private EventHandler<WindowMessageEventArgs> myWindowMessage;

	private EventHandler<EventArgs> myFilterInitComplete;

	private EventHandler<EventArgs> myServiceInitComplete;

	private EventHandler<EventArgs> myPluginInitComplete;

	private EventHandler<EventArgs> myFilterTermComplete;

	private EventHandler<EventArgs> myServiceTermComplete;

	private EventHandler<EventArgs> myPluginTermComplete;

	private EventHandler<EventArgs> myRender_PreUI;

	private EventHandler<AdapterMessageEventArgs> myExtensionMessage;

	private AppDomain myPluginDomain;

	private AppDomain myDomain;

	/// <summary>
	/// Returns if the service was initialized by Decal. 
	/// </summary>
	public static bool ServiceRunning => myServiceRunning;

	/// <summary>
	/// Returns the Singleton instance of the Service
	/// (Initializes if necessary)
	/// </summary>
	public static CoreManager Current
	{
		get
		{
			if (myService == null)
			{
				myService = new CoreManager();
			}
			return myService;
		}
	}

	/// <summary>
	/// Tracing level for Decal.Adapter, and all loaded plugins. 
	/// </summary>
	public static int TracingLevel => Util.TraceLevel;

	/// <summary>
	/// FileService (Decal.FileService)
	/// </summary>
	public FilterBase FileService => Filter("Decal.FileService");

	/// <summary>
	/// Decal Hotkey System
	/// </summary>
	public global::Decal.Adapter.Wrappers.HotkeySystem HotkeySystem => myDHS;

	/// <summary>
	/// Character Filter
	/// </summary>
	[CLSCompliant(false)]
	public CharacterFilter CharacterFilter => myCharacterFilter;

	[CLSCompliant(false)]
	public WorldFilter WorldFilter => myWorldFilter;

	/// <summary>
	/// Direct3D Service
	/// </summary>
	[CLSCompliant(false)]
	public global::Decal.Adapter.Wrappers.EchoFilter2 EchoFilter => myEchoFilter;

	[CLSCompliant(false)]
	public D3DService D3DService => myD3DService;

	[CLSCompliant(false)]
	public HooksWrapper Actions => myWrappedHooks;

	[CLSCompliant(false)]
	public DecalWrapper Decal => myWrappedDecal;

	public RenderServiceWrapper RenderService => myRenderService;

	public FairIDQueue IDQueue => myIDQueue;

	internal AppDomain PluginDomain => myDomain;

	[CLSCompliant(false)]
	public event EventHandler<ItemSelectedEventArgs> ItemSelected
	{
		add
		{
			myItemSelected = (EventHandler<ItemSelectedEventArgs>)Delegate.Combine(myItemSelected, value);
		}
		remove
		{
			myItemSelected = (EventHandler<ItemSelectedEventArgs>)Delegate.Remove(myItemSelected, value);
		}
	}

	[CLSCompliant(false)]
	public event EventHandler<ItemDestroyedEventArgs> ItemDestroyed
	{
		add
		{
			myItemDestroyed = (EventHandler<ItemDestroyedEventArgs>)Delegate.Combine(myItemDestroyed, value);
		}
		remove
		{
			myItemDestroyed = (EventHandler<ItemDestroyedEventArgs>)Delegate.Remove(myItemDestroyed, value);
		}
	}

	[CLSCompliant(false)]
	public event EventHandler<ChatParserInterceptEventArgs> CommandLineText
	{
		add
		{
			myChatParserIntercept = (EventHandler<ChatParserInterceptEventArgs>)Delegate.Combine(myChatParserIntercept, value);
		}
		remove
		{
			myChatParserIntercept = (EventHandler<ChatParserInterceptEventArgs>)Delegate.Remove(myChatParserIntercept, value);
		}
	}

	[CLSCompliant(false)]
	public event EventHandler<StatusTextInterceptEventArgs> StatusBoxMessage
	{
		add
		{
			myStatusTextIntercept = (EventHandler<StatusTextInterceptEventArgs>)Delegate.Combine(myStatusTextIntercept, value);
		}
		remove
		{
			myStatusTextIntercept = (EventHandler<StatusTextInterceptEventArgs>)Delegate.Remove(myStatusTextIntercept, value);
		}
	}

	[CLSCompliant(false)]
	public event EventHandler<RegionChange3DEventArgs> RegionChange3D
	{
		add
		{
			myRegionChange3D = (EventHandler<RegionChange3DEventArgs>)Delegate.Combine(myRegionChange3D, value);
		}
		remove
		{
			myRegionChange3D = (EventHandler<RegionChange3DEventArgs>)Delegate.Remove(myRegionChange3D, value);
		}
	}

	[CLSCompliant(false)]
	public event EventHandler<ChatTextInterceptEventArgs> ChatBoxMessage
	{
		add
		{
			myChatTextIntercept = (EventHandler<ChatTextInterceptEventArgs>)Delegate.Combine(myChatTextIntercept, value);
		}
		remove
		{
			myChatTextIntercept = (EventHandler<ChatTextInterceptEventArgs>)Delegate.Remove(myChatTextIntercept, value);
		}
	}

	[CLSCompliant(false)]
	public event EventHandler<MessageProcessedEventArgs> MessageProcessed
	{
		add
		{
			myMessageProcessed = (EventHandler<MessageProcessedEventArgs>)Delegate.Combine(myMessageProcessed, value);
		}
		remove
		{
			myMessageProcessed = (EventHandler<MessageProcessedEventArgs>)Delegate.Remove(myMessageProcessed, value);
		}
	}

	[CLSCompliant(false)]
	public event EventHandler<ContainerOpenedEventArgs> ContainerOpened
	{
		add
		{
			myContainerOpened = (EventHandler<ContainerOpenedEventArgs>)Delegate.Combine(myContainerOpened, value);
		}
		remove
		{
			myContainerOpened = (EventHandler<ContainerOpenedEventArgs>)Delegate.Remove(myContainerOpened, value);
		}
	}

	[CLSCompliant(false)]
	public event EventHandler<ChatClickInterceptEventArgs> ChatNameClicked
	{
		add
		{
			myChatClickIntercept = (EventHandler<ChatClickInterceptEventArgs>)Delegate.Combine(myChatClickIntercept, value);
		}
		remove
		{
			myChatClickIntercept = (EventHandler<ChatClickInterceptEventArgs>)Delegate.Remove(myChatClickIntercept, value);
		}
	}

	public event EventHandler<EventArgs> FilterInitComplete
	{
		add
		{
			myFilterInitComplete = (EventHandler<EventArgs>)Delegate.Combine(myFilterInitComplete, value);
		}
		remove
		{
			myFilterInitComplete = (EventHandler<EventArgs>)Delegate.Remove(myFilterInitComplete, value);
		}
	}

	public event EventHandler<EventArgs> ServiceInitComplete
	{
		add
		{
			myServiceInitComplete = (EventHandler<EventArgs>)Delegate.Combine(myServiceInitComplete, value);
		}
		remove
		{
			myServiceInitComplete = (EventHandler<EventArgs>)Delegate.Remove(myServiceInitComplete, value);
		}
	}

	public event EventHandler<EventArgs> PluginInitComplete
	{
		add
		{
			myPluginInitComplete = (EventHandler<EventArgs>)Delegate.Combine(myPluginInitComplete, value);
		}
		remove
		{
			myPluginInitComplete = (EventHandler<EventArgs>)Delegate.Remove(myPluginInitComplete, value);
		}
	}

	public event EventHandler<EventArgs> FilterTermComplete
	{
		add
		{
			myFilterTermComplete = (EventHandler<EventArgs>)Delegate.Combine(myFilterTermComplete, value);
		}
		remove
		{
			myFilterTermComplete = (EventHandler<EventArgs>)Delegate.Remove(myFilterTermComplete, value);
		}
	}

	public event EventHandler<EventArgs> ServiceTermComplete
	{
		add
		{
			myServiceTermComplete = (EventHandler<EventArgs>)Delegate.Combine(myServiceTermComplete, value);
		}
		remove
		{
			myServiceTermComplete = (EventHandler<EventArgs>)Delegate.Remove(myServiceTermComplete, value);
		}
	}

	public event EventHandler<EventArgs> PluginTermComplete
	{
		add
		{
			myPluginTermComplete = (EventHandler<EventArgs>)Delegate.Combine(myPluginTermComplete, value);
		}
		remove
		{
			myPluginTermComplete = (EventHandler<EventArgs>)Delegate.Remove(myPluginTermComplete, value);
		}
	}

	public event EventHandler<EventArgs> RenderFrame
	{
		add
		{
			myRender_PreUI = (EventHandler<EventArgs>)Delegate.Combine(myRender_PreUI, value);
		}
		remove
		{
			myRender_PreUI = (EventHandler<EventArgs>)Delegate.Remove(myRender_PreUI, value);
		}
	}

	[CLSCompliant(false)]
	public event EventHandler<WindowMessageEventArgs> WindowMessage
	{
		add
		{
			myWindowMessage = (EventHandler<WindowMessageEventArgs>)Delegate.Combine(myWindowMessage, value);
		}
		remove
		{
			myWindowMessage = (EventHandler<WindowMessageEventArgs>)Delegate.Remove(myWindowMessage, value);
		}
	}

	internal event EventHandler<AdapterMessageEventArgs> ExtensionMessage
	{
		add
		{
			myExtensionMessage = (EventHandler<AdapterMessageEventArgs>)Delegate.Combine(myExtensionMessage, value);
		}
		remove
		{
			myExtensionMessage = (EventHandler<AdapterMessageEventArgs>)Delegate.Remove(myExtensionMessage, value);
		}
	}

	private CoreManager()
	{
		AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve;
		string location = System.Reflection.Assembly.GetExecutingAssembly().Location;
		AddAssemblyPath(Path.GetDirectoryName(location));
		myDomain = AppDomain.CurrentDomain;
		Util.InitializeTracing("HKEY_LOCAL_MACHINE\\Software\\Decal\\Decal.Adapter", "Tracing", 0);
	}

	private Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
	{
		AppDomain appDomain = (AppDomain)sender;
		Assembly[] assemblies = appDomain.GetAssemblies();
		string text = appDomain.ApplyPolicy(args.Name);
		if (string.IsNullOrEmpty(text))
		{
			text = args.Name;
		}
		Assembly[] array = assemblies;
		foreach (Assembly assembly in array)
		{
			if (assembly.FullName == text)
			{
				return assembly;
			}
		}
		if (!appDomain.IsDefaultAppDomain())
		{
			assemblies = myDomain.GetAssemblies();
			array = assemblies;
			foreach (Assembly assembly2 in array)
			{
				if (assembly2.FullName == text)
				{
					AssemblyName name = assembly2.GetName(copiedName: false);
					return appDomain.Load(name);
				}
			}
		}
		string text2 = ((text.IndexOf(",") <= 0) ? text : text.Substring(0, text.IndexOf(",")));
		if (!string.IsNullOrEmpty(text2))
		{
			text2 = text2.ToLowerInvariant();
			if (string.IsNullOrEmpty(Path.GetExtension(text2)))
			{
				text2 += ".dll";
			}
			foreach (string myPath in myPaths)
			{
				if (File.Exists(Path.Combine(myPath, text2)))
				{
					AssemblyName assemblyName = new AssemblyName();
					assemblyName.CodeBase = Path.Combine(myPath, text2);
					return appDomain.Load(assemblyName);
				}
			}
		}
		Util.WriteLine("Assembly resolve failed for assembly:" + args.Name);
		return null;
	}

	private void AddAssemblyPath(string path)
	{
		string item = path.ToLowerInvariant();
		if (!myPaths.Contains(item))
		{
			myPaths.Add(item);
		}
	}

	private void myDecal_InitializeComplete(eDecalComponentType myComponent)
	{
		Util.WriteLine("InitializeComplete for: " + myComponent);
		switch (myComponent)
		{
		case eDecalComponentType.eService:
			Message.Initialize(Path.Combine((string)Registry.GetValue("HKEY_LOCAL_MACHINE\\SOFTWARE\\Decal\\Agent", "AgentPath", ""), "messages.xml"));
			if (myWrappedDecal != null)
			{
				try
				{
					if (myWrappedDecal.GetObject("services\\D3DService.Service") is CService obj)
					{
						myD3DService = new D3DService(obj);
					}
					if (myWrappedDecal.GetObject("services\\DecalRender.RenderService") is RenderService render)
					{
						myRenderService = new RenderServiceWrapper(render);
					}
					object obj2 = myWrappedDecal.GetObject("services\\DecalPlugins.InjectService\\site", DecalWrapper.IID_IUNKNOWN);
					mySitev1 = obj2 as PluginSite;
				}
				catch (Exception ex)
				{
					Util.WriteLine("Unable to get service reference: " + ex.Message);
				}
			}
			else
			{
				Util.WriteLine("Not getting services, we don't have a Decal object to call GetObject on.");
			}
			try
			{
				myIDQueue = new FairIDQueue();
			}
			catch (Exception ex2)
			{
				Util.WriteLine("Service initialization exception: " + ex2.Message);
			}
			Util.SafeFireEvent(this, myServiceInitComplete, new EventArgs());
			break;
		case eDecalComponentType.eNetworkFilter:
			if (myWrappedDecal != null)
			{
				try
				{
					if (myWrappedDecal.GetObject("services\\DecalNet.NetService\\DecalFilters.World") is World obj3)
					{
						myWorldFilter = new WorldFilter(obj3);
					}
					if (myWrappedDecal.GetObject("services\\DecalNet.NetService\\DecalFilters.CharacterStats") is CharacterStats obj4)
					{
						myCharacterFilter = new CharacterFilter(obj4);
					}
					if (myWrappedDecal.GetObject("services\\DecalNet.NetService\\DecalFilters.EchoFilter2") is global::Decal.Interop.Filters.EchoFilter2 obj5)
					{
						myEchoFilter = new global::Decal.Adapter.Wrappers.EchoFilter2(obj5);
					}
				}
				catch (Exception ex3)
				{
					Util.WriteLine("Unable to get filter reference: " + ex3.Message);
				}
			}
			else
			{
				Util.WriteLine("Not getting filters, we don't have a Decal object to call GetObject on.");
			}
			Util.SafeFireEvent(this, myFilterInitComplete, new EventArgs());
			DecrementAssemblyPreloadLockCounter();
			break;
		case eDecalComponentType.ePlugin:
			if (myWrappedDecal.GetObject("plugins\\DHS.HotkeySystem") is global::Decal.Interop.DHS.HotkeySystem hks)
			{
				myDHS = new global::Decal.Adapter.Wrappers.HotkeySystem(hks);
			}
			myIDQueue.Start();
			Util.SafeFireEvent(this, myPluginInitComplete, new EventArgs());
			break;
		}
	}

	private void myDecal_TerminateComplete(eDecalComponentType myComponent)
	{
		Util.WriteLine("TerminateComplete for: " + myComponent);
		switch (myComponent)
		{
		case eDecalComponentType.eService:
			if (myD3DService != null)
			{
				myD3DService.Dispose();
				myD3DService = null;
			}
			if (myRenderService != null)
			{
				myRenderService.Dispose();
				myRenderService = null;
			}
			if (mySitev1 != null)
			{
				Marshal.ReleaseComObject(mySitev1);
			}
			myServices.Clear();
			myNamedServices.Clear();
			Util.SafeFireEvent(this, myServiceTermComplete, new EventArgs());
			break;
		case eDecalComponentType.eNetworkFilter:
			if (myWorldFilter != null)
			{
				myWorldFilter.Dispose();
				myWorldFilter = null;
			}
			if (myCharacterFilter != null)
			{
				myCharacterFilter.Dispose();
				myCharacterFilter = null;
			}
			if (myEchoFilter != null)
			{
				myEchoFilter.Dispose();
				myEchoFilter = null;
			}
			myFilters.Clear();
			myNamedFilters.Clear();
			Util.SafeFireEvent(this, myFilterTermComplete, new EventArgs());
			break;
		case eDecalComponentType.ePlugin:
			myIDQueue.Stop();
			if (myDHS != null)
			{
				myDHS.Dispose();
				myDHS = null;
			}
			myPlugins.Clear();
			if (myPluginDomain != null)
			{
				lock (myAssemblies)
				{
					if (myAssemblies.ContainsKey(myPluginDomain))
					{
						myAssemblies[myPluginDomain].Clear();
						myAssemblies.Remove(myPluginDomain);
					}
					AppDomain.Unload(myPluginDomain);
					myPluginDomain = null;
				}
			}
			Util.SafeFireEvent(this, myPluginTermComplete, new EventArgs());
			break;
		}
	}

	private void myHooks_ObjectSelected(int lGUID)
	{
		Util.SafeFireEvent(this, myItemSelected, new ItemSelectedEventArgs(lGUID));
	}

	private void myHooks_ObjectDestroyed(int lGUID)
	{
		Util.SafeFireEvent(this, myItemDestroyed, new ItemDestroyedEventArgs(lGUID));
	}

	private void myHooks_AC3DRegionChanged(int left, int top, int right, int bottom)
	{
		Util.SafeFireEvent(this, myRegionChange3D, new RegionChange3DEventArgs(left, top, right, bottom));
	}

	private void myHooks_ChatParserIntercept(string bstrText, ref bool bEat)
	{
		Util.SafeFireEvent(this, myChatParserIntercept, new ChatParserInterceptEventArgs(bstrText, bEat), ref bEat);
	}

	private void myHooks_StatusTextIntercept(string bstrText, ref bool bEat)
	{
		Util.SafeFireEvent(this, myStatusTextIntercept, new StatusTextInterceptEventArgs(bstrText, bEat), ref bEat);
	}

	private void myHooks_ChatTextIntercept(string bstrText, int lColor, int target, ref bool bEat)
	{
		Util.SafeFireEvent(this, myChatTextIntercept, new ChatTextInterceptEventArgs(bstrText, lColor, target, bEat), ref bEat);
	}

	private void myHooks_MessageProcessed(int pbData, int dwSize)
	{
		if (myMessageProcessed != null)
		{
			MessageProcessedEventArgs args = new MessageProcessedEventArgs(pbData, dwSize);
			Util.SafeFireEvent(this, myMessageProcessed, args);
		}
	}

	private void myHooks_ContainerOpened(int lGUID)
	{
		Util.SafeFireEvent(this, myContainerOpened, new ContainerOpenedEventArgs(lGUID));
	}

	private void myHooks_ChatClickIntercept(string bstrText, int id, ref bool bEat)
	{
		Util.SafeFireEvent(this, myChatClickIntercept, new ChatClickInterceptEventArgs(bstrText, id, bEat), ref bEat);
	}

	private void myHooks_RenderPreUI()
	{
		Util.SafeFireEvent(this, myRender_PreUI, new EventArgs());
	}

	internal bool FireWindowMessage(int HWND, short uMsg, int wParam, int lParam)
	{
		bool eaten = false;
		WindowMessageEventArgs args = new WindowMessageEventArgs(HWND, uMsg, wParam, lParam);
		Util.SafeFireEvent(this, myWindowMessage, args, ref eaten);
		return eaten;
	}

	internal void FireExtensionMessage(object sender, AdapterMessageEventArgs args)
	{
		args.CanAddHandlers = true;
		Util.SafeFireEvent(this, myExtensionMessage, args);
		args.CanAddHandlers = false;
	}

	public FilterBase Filter(string filterName)
	{
		try
		{
			if (myNamedFilters.ContainsKey(filterName))
			{
				return myNamedFilters[filterName];
			}
			Type type = Type.GetType(filterName);
			if (null != type && myFilters.ContainsKey(type))
			{
				return myFilters[type];
			}
		}
		catch (Exception)
		{
		}
		return null;
	}

	public T Filter<T>() where T : FilterBase
	{
		try
		{
			if (myFilters.ContainsKey(typeof(T)))
			{
				return (T)myFilters[typeof(T)];
			}
		}
		catch (Exception)
		{
		}
		return null;
	}

	public ServiceBase Service(string serviceName)
	{
		try
		{
			if (myNamedServices.ContainsKey(serviceName))
			{
				return myNamedServices[serviceName];
			}
			Type type = Type.GetType(serviceName);
			if (null != type && myServices.ContainsKey(type))
			{
				return myServices[type];
			}
		}
		catch (Exception)
		{
		}
		return null;
	}

	public T Service<T>() where T : ServiceBase
	{
		try
		{
			if (myServices.ContainsKey(typeof(T)))
			{
				return (T)myServices[typeof(T)];
			}
		}
		catch (Exception)
		{
		}
		return null;
	}

	/// <summary>
	/// Get Mapped Keyboard Key
	/// </summary>
	/// <param name="name">Name to retrive mapping for. </param>
	/// <returns>Mapped Key</returns>
	public int QueryKeyBoardMap(string name)
	{
		int result = 0;
		if (mySitev1 != null)
		{
			result = mySitev1.QueryKeyboardMap(name);
		}
		return result;
	}

	internal void AddFilter(FilterBase newFilter)
	{
		AddFilter(newFilter.ReferenceName, newFilter);
	}

	internal void AddFilter(string filterName, FilterBase newFilter)
	{
		myFilters.Add(newFilter.GetType(), newFilter);
		myNamedFilters.Add(filterName, newFilter);
	}

	internal void AddService(ServiceBase newService)
	{
		AddService(newService.ReferenceName, newService);
	}

	internal void AddService(string serviceName, ServiceBase newService)
	{
		myServices.Add(newService.GetType(), newService);
		myNamedServices.Add(serviceName, newService);
	}

	internal void AddPlugin(PluginBase newPlugin)
	{
		AddPlugin(newPlugin.ReferenceName, newPlugin);
	}

	internal void AddPlugin(string pluginName, PluginBase newPlugin)
	{
		myPlugins.Add(pluginName, newPlugin);
	}

	/// <summary>
	/// Load the Assembly requested, first checking our internal cache
	/// </summary>
	/// <param name="name">Assembly name to load</param>
	/// <param name="path">Path to the assembly</param>
	/// <returns>Loaded Assembly, from cache if already loaded</returns>
	internal Assembly Assembly(string name, string path)
	{
		return Assembly(name, path, AppDomain.CurrentDomain);
	}

	internal Assembly Assembly(string name, string path, AppDomain useDomain)
	{
		lock (myAssemblies)
		{
			string text = name.ToLowerInvariant();
			useDomain = myDomain;
			Dictionary<string, Assembly> dictionary;
			if (myAssemblies.ContainsKey(useDomain))
			{
				dictionary = myAssemblies[useDomain];
			}
			else
			{
				myAssemblies.Add(useDomain, new Dictionary<string, Assembly>());
				dictionary = myAssemblies[useDomain];
			}
			if (dictionary.ContainsKey(text))
			{
				return dictionary[text];
			}
			AddAssemblyPath(path);
			Assembly assembly = System.Reflection.Assembly.LoadFrom(Path.Combine(path, text));
			dictionary.Add(text, assembly);
			return assembly;
		}
	}

	internal void Initialize(DecalCore pDecal)
	{
		Util.WriteLine("Decal.Adapter Lifetime-service Initialize Begun");
		myServiceRunning = true;
		Util.Write("Get Decal.. ");
		myDecal = (DecalCoreClass)pDecal;
		myWrappedDecal = new DecalWrapper(myDecal);
		Util.WriteLine("Sink Decal Events.. ");
		myDecal.InitializeComplete += myDecal_InitializeComplete;
		myDecal.TerminateComplete += myDecal_TerminateComplete;
		Util.Write("Get Hooks.. ");
		myHooks = (ACHooksClass)myDecal.Hooks;
		myWrappedHooks = new HooksWrapper(myHooks);
		Util.WriteLine("Sink Hooks Events.. ");
		myHooks.ChatTextIntercept += myHooks_ChatTextIntercept;
		myHooks.StatusTextIntercept += myHooks_StatusTextIntercept;
		myHooks.ChatParserIntercept += myHooks_ChatParserIntercept;
		myHooks.AC3DRegionChanged += myHooks_AC3DRegionChanged;
		myHooks.ObjectSelected += myHooks_ObjectSelected;
		myHooks.ObjectDestroyed += myHooks_ObjectDestroyed;
		myHooks.MessageProcessed += myHooks_MessageProcessed;
		myHooks.ContainerOpened += myHooks_ContainerOpened;
		myHooks.ChatClickIntercept += myHooks_ChatClickIntercept;
		myHooks.RenderPreUI += myHooks_RenderPreUI;
		Util.WriteLine("Decal.Adapter Lifetime-service Initiliazed");
	}

	internal void Terminate()
	{
		myDecal.InitializeComplete -= myDecal_InitializeComplete;
		myDecal.TerminateComplete -= myDecal_TerminateComplete;
		myHooks.ChatTextIntercept -= myHooks_ChatTextIntercept;
		myHooks.StatusTextIntercept -= myHooks_StatusTextIntercept;
		myHooks.ChatParserIntercept -= myHooks_ChatParserIntercept;
		myHooks.AC3DRegionChanged -= myHooks_AC3DRegionChanged;
		myHooks.ObjectSelected -= myHooks_ObjectSelected;
		myHooks.ObjectDestroyed -= myHooks_ObjectDestroyed;
		myHooks.MessageProcessed -= myHooks_MessageProcessed;
		myHooks.ContainerOpened -= myHooks_ContainerOpened;
		myHooks.ChatClickIntercept -= myHooks_ChatClickIntercept;
		myHooks.RenderPreUI -= myHooks_RenderPreUI;
		myWrappedDecal.Dispose();
		myWrappedDecal = null;
		Marshal.ReleaseComObject(myDecal);
		myDecal = null;
		myHooks = null;
		myWrappedHooks.Dispose();
		myWrappedHooks = null;
		myService = null;
		myServiceRunning = false;
	}

	public void IncrementAssemblyPreloadLockCounter()
	{
		myAssemblyPreloadCounter++;
	}

	public void DecrementAssemblyPreloadLockCounter()
	{
		myAssemblyPreloadCounter--;
		if (myAssemblyPreloadCounter < 0)
		{
			myAssemblyPreloadCounter = 0;
		}
		if (myAssemblyPreloadCounter != 0 || myAssemblyPreloadStarted)
		{
			return;
		}
		myAssemblyPreloadStarted = true;
		List<sAssemblyPreloadData> list = new List<sAssemblyPreloadData>();
		Guid clsidAdvance = Guid.Empty;
		Guid guid = new Guid("{71A69713-6593-47EC-0002-0000000DECA1}");
		DecalEnum decalEnum = ((IDecalCore)Decal.Underlying).get_Configuration("Plugins", ref clsidAdvance);
		try
		{
			while (true)
			{
				decalEnum.Next();
				if (!decalEnum.Enabled || decalEnum.SurrogateClass != guid)
				{
					continue;
				}
				try
				{
					if (decalEnum.Restricted)
					{
						continue;
					}
				}
				catch
				{
					continue;
				}
				list.Add(new sAssemblyPreloadData
				{
					AssemblyName = (string)((IDecalEnum)decalEnum).get_Property("Assembly"),
					Path = (string)((IDecalEnum)decalEnum).get_Property("Path")
				});
			}
		}
		catch
		{
		}
		finally
		{
			Marshal.ReleaseComObject(decalEnum);
		}
		ThreadPool.QueueUserWorkItem(AssemblyPreloadThreadProc, list);
	}

	internal void AssemblyPreloadThreadProc(object data)
	{
		if (!(data is List<sAssemblyPreloadData> list))
		{
			return;
		}
		foreach (sAssemblyPreloadData item in list)
		{
			try
			{
				Util.WriteLine("Preloading plugin assembly {0}, {1}", item.AssemblyName, item.Path);
				Assembly(item.AssemblyName, item.Path, AppDomain.CurrentDomain);
			}
			catch
			{
			}
		}
	}
}
