using System;
using System.ComponentModel;
using System.Drawing;
using System.Runtime.InteropServices;
using Decal.Adapter.Support;
using Decal.Interop.Core;
using Decal.Interop.Inject;
using Decal.Interop.Render;

namespace Decal.Adapter.Wrappers;

/// <summary>
/// RenderServiceWrapper is a wrapper for RenderService
/// </summary>
[CLSCompliant(true)]
public class RenderServiceWrapper : MarshalByRefObject, IDisposable
{
	private RenderService internalRender;

	private bool isDisposed;

	private EventHandler myDeviceLost;

	[EditorBrowsable(EditorBrowsableState.Never)]
	public object UnsafeDevice => internalRender.Device;

	internal event EventHandler DeviceLost
	{
		add
		{
			myDeviceLost = (EventHandler)Delegate.Combine(myDeviceLost, value);
		}
		remove
		{
			myDeviceLost = (EventHandler)Delegate.Combine(myDeviceLost, value);
		}
	}

	internal RenderServiceWrapper(RenderService render)
	{
		internalRender = render;
		internalRender.DeviceLost += internalRender_DeviceLost;
	}

	~RenderServiceWrapper()
	{
		Dispose(disposing: false);
	}

	private void internalRender_DeviceLost()
	{
		if (myDeviceLost != null)
		{
			myDeviceLost(this, new EventArgs());
		}
	}

	private void myHUD_Disposing(object sender, EventArgs e)
	{
		if (sender is Hud hud)
		{
			RemoveHud(hud);
			hud.Disposing -= myHUD_Disposing;
		}
	}

	/// <summary>
	/// Create a new Background object to be used as a background in HUDS
	/// </summary>
	/// <param name="region">The rectangle to use for the size of the background</param>
	/// <returns>Newly created and wrapped RenderService.HUDBackground</returns>
	public Background CreateBackground(Rectangle region)
	{
		return UnsafeCreateBackground(Util.toTagRECT(region));
	}

	/// <summary>
	/// Create a new HUD. New huds need to be made visible before they render. 
	/// </summary>
	/// <param name="region">Size and location of where the HUD should be rendered</param>
	/// <returns>Newly created and wrapped RenderService.HUDView</returns>
	public Hud CreateHud(Rectangle region)
	{
		return UnsafeCreateHud(Util.toTagRECT(region));
	}

	/// <summary>
	/// Remove a HUD from the render pipeline
	/// </summary>
	/// <param name="hud">HUD To remove</param>
	public void RemoveHud(Hud hud)
	{
		if (hud == null)
		{
			throw new ArgumentNullException("hud");
		}
		internalRender.RemoveHUD(hud.Underlying);
	}

	[CLSCompliant(false)]
	[EditorBrowsable(EditorBrowsableState.Never)]
	public Background UnsafeCreateBackground(tagRECT pRegion)
	{
		return new Background(internalRender.CreateBackground(ref pRegion));
	}

	[CLSCompliant(false)]
	[EditorBrowsable(EditorBrowsableState.Never)]
	public Hud UnsafeCreateHud(tagRECT pRegion)
	{
		Hud hud = new Hud(internalRender.CreateHUD(ref pRegion));
		hud.Disposing += myHUD_Disposing;
		return hud;
	}

	[CLSCompliant(false)]
	[EditorBrowsable(EditorBrowsableState.Never)]
	public Background EncapsulateBackground(tagPOINT coordinates, object surface)
	{
		return new Background(internalRender.EncapsulateBackground(ref coordinates, surface));
	}

	[CLSCompliant(false)]
	[EditorBrowsable(EditorBrowsableState.Never)]
	public Hud EncapsulateHud(tagPOINT coordinates, object surface)
	{
		return new Hud(internalRender.EncapsulateHUD(ref coordinates, surface));
	}

	public void Dispose()
	{
		Dispose(disposing: true);
		GC.SuppressFinalize(this);
	}

	protected virtual void Dispose(bool disposing)
	{
		if (!isDisposed && disposing)
		{
			internalRender.DeviceLost -= internalRender_DeviceLost;
		}
		if (internalRender != null)
		{
			Marshal.ReleaseComObject(internalRender);
		}
		internalRender = null;
		isDisposed = true;
	}
}
