using System;
using System.ComponentModel;
using System.IO;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Xml;
using Decal.Interop.Core;
using Decal.Interop.Inject;
using Decal.Interop.Render;

namespace Decal.Adapter.Wrappers;

[CLSCompliant(true)]
public sealed class PluginHost : HostBase
{
	private IPluginSite2 mySite;

	private IPluginSite myOldSite;

	[CLSCompliant(false)]
	[EditorBrowsable(EditorBrowsableState.Never)]
	public IPluginSite2 Underlying => mySite;

	/// <summary>
	/// Wrapped Decal OM
	/// </summary>
	public DecalWrapper Decal
	{
		get
		{
			if (base.MyDecal == null)
			{
				base.MyDecal = new DecalWrapper(mySite.Decal);
			}
			return base.MyDecal;
		}
	}

	/// <summary>
	/// Wrapped version of the ACHooks interface
	/// </summary>
	public HooksWrapper Actions => CoreManager.Current.Actions;

	/// <summary>
	/// Wrapped version of the DecalRenderService
	/// </summary>
	public RenderServiceWrapper Render
	{
		get
		{
			if (base.MyRender == null)
			{
				base.MyRender = new RenderServiceWrapper((RenderService)GetObject("services\\DecalRender.RenderService"));
			}
			return base.MyRender;
		}
	}

	internal PluginHost(PluginSite2 pSite)
	{
		mySite = pSite;
		base.MyDecal = new DecalWrapper(mySite.Decal);
		myOldSite = (IPluginSite)mySite.PluginSite;
	}

	protected override void Dispose(bool disposing)
	{
		try
		{
			_ = base.IsDisposed;
			if (myOldSite != null)
			{
				Marshal.ReleaseComObject(myOldSite);
				myOldSite = null;
			}
			if (mySite != null)
			{
				Marshal.ReleaseComObject(mySite);
				mySite = null;
			}
		}
		finally
		{
			base.Dispose(disposing);
		}
	}

	/// <summary>
	/// Function to get a COM object by path from Decal. 
	/// </summary>
	/// <param name="path">The decal services path to resolve the COM object</param>
	/// <returns>instance of the object requested</returns>
	public object GetObject(string path)
	{
		return mySite.get_Object(path);
	}

	/// <summary>
	/// Get Mapped Keyboard Key
	/// </summary>
	/// <param name="name">Name to retrive mapping for. </param>
	/// <returns>Mapped Key</returns>
	public int GetKeyboardMapping(string name)
	{
		return myOldSite.QueryKeyboardMap(name);
	}

	/// <summary>
	/// Initializes an already exisint viewhandler (plugins)
	/// </summary>
	/// <param name="handler">the handler to init</param>
	internal static void LoadViewHandler(IViewHandler handler)
	{
		if (ViewWrapper.ScanViews(handler))
		{
			ViewWrapper.ScanControls(handler);
			ViewWrapper.ScanReferences(handler);
		}
	}

	/// <summary>
	/// Load a view handler of the specified type
	/// </summary>
	/// <param name="handlerType">type of handler to load</param>
	/// <returns>the new handler</returns>
	public ViewHandler LoadViewHandler(Type handlerType)
	{
		ViewHandler obj = (ViewHandler)Activator.CreateInstance(handlerType, this);
		LoadViewHandler(obj);
		obj.LoadComplete();
		return obj;
	}

	/// <summary>
	/// Load a view in the current assembly using the specified resource
	/// </summary>
	/// <param name="resourcePath">path of the embedded view xml resource</param>
	/// <returns>the new view</returns>
	public ViewWrapper LoadViewResource(string resourcePath)
	{
		Assembly callingAssembly = Assembly.GetCallingAssembly();
		return LoadViewResource(resourcePath, callingAssembly);
	}

	/// <summary>
	/// Load a view in the specified assembly using the specified resource
	/// </summary>
	/// <param name="resourcePath">path of the embedded view xml resource</param>
	/// <param name="resourceAssembly">assembly containing the resource</param>
	/// <returns>the new view</returns>
	public ViewWrapper LoadViewResource(string resourcePath, Assembly resourceAssembly)
	{
		if (null == resourceAssembly)
		{
			throw new ArgumentNullException("resourceAssembly");
		}
		Stream manifestResourceStream = resourceAssembly.GetManifestResourceStream(resourcePath);
		XmlDocument xmlDocument = new XmlDocument();
		xmlDocument.Load(manifestResourceStream);
		return LoadView(xmlDocument.OuterXml);
	}

	/// <summary>
	/// Load a view from the specified XML element
	/// </summary>
	/// <param name="viewSchema">XmlElement containing the view schema</param>
	/// <returns>the new view</returns>
	public ViewWrapper LoadView(XmlElement viewSchema)
	{
		if (viewSchema == null)
		{
			throw new ArgumentNullException("viewSchema");
		}
		return LoadView(viewSchema.OuterXml);
	}

	/// <summary>
	/// Load a view from the specified XML string
	/// </summary>
	/// <param name="viewSchema">string containing the view schema</param>
	/// <returns>the new view</returns>
	public ViewWrapper LoadView(string viewSchema)
	{
		View view = myOldSite.LoadView(viewSchema);
		if (view != null)
		{
			return new ViewWrapper(view);
		}
		return null;
	}

	/// <summary>
	/// Get a COM based filter object. 
	/// (Similar to GetObject, but requires only ProgID instead of full path)
	/// </summary>
	/// <param name="progId"></param>
	/// <returns></returns>
	public object ComFilter(string progId)
	{
		return mySite.get_Object("services\\DecalNet.NetService\\" + progId);
	}
}
