using System;
using System.Collections.Generic;
using System.Reflection;
using Decal.Adapter.Wrappers;

namespace Decal.Adapter;

/// <summary>
/// Base class used to create Decal Plugins
/// </summary>
[CLSCompliant(true)]
public abstract class PluginBase : Extension, IViewHandler
{
	private PluginHost myHost;

	private EventHandler myGraphicsReset;

	private BindingFlags myBindingFlags;

	private Dictionary<string, ViewWrapper> myViews;

	/// <summary>
	/// Wrapper Object to the Host. 
	/// Similar to IPluginSite
	/// </summary>
	protected PluginHost Host => myHost;

	/// <summary>
	/// The Default view
	/// </summary>
	public ViewWrapper DefaultView => myViews["Default"];

	/// <summary>
	/// BindingFlags for internal scanning
	/// </summary>
	public BindingFlags BindingFlags => myBindingFlags;

	[CLSCompliant(false)]
	protected event EventHandler<NetworkMessageEventArgs> ServerDispatch
	{
		add
		{
			if (base.Core.EchoFilter != null)
			{
				base.Core.EchoFilter.ServerDispatch += value;
			}
		}
		remove
		{
			if (base.Core.EchoFilter != null)
			{
				base.Core.EchoFilter.ServerDispatch -= value;
			}
		}
	}

	[CLSCompliant(false)]
	protected event EventHandler<NetworkMessageEventArgs> ClientDispatch
	{
		add
		{
			if (base.Core.EchoFilter != null)
			{
				base.Core.EchoFilter.ClientDispatch += value;
			}
		}
		remove
		{
			if (base.Core.EchoFilter != null)
			{
				base.Core.EchoFilter.ClientDispatch -= value;
			}
		}
	}

	protected event EventHandler GraphicsReset
	{
		add
		{
			myGraphicsReset = (EventHandler)Delegate.Combine(myGraphicsReset, value);
			if (myHost != null)
			{
				myHost.Render.DeviceLost += value;
			}
		}
		remove
		{
			myGraphicsReset = (EventHandler)Delegate.Remove(myGraphicsReset, value);
			if (myHost != null)
			{
				myHost.Render.DeviceLost -= value;
			}
		}
	}

	/// <summary>
	/// Default Constructor for the Base class. Should be called by all decendants. 
	/// </summary>
	protected PluginBase()
		: base(DecalExtensionType.Plugin)
	{
		myBindingFlags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;
	}

	/// <summary>
	/// Called by the base class to Wire Up events. 
	/// </summary>
	internal override void Wireup()
	{
		if (myGraphicsReset != null)
		{
			Host.Render.DeviceLost += myGraphicsReset;
		}
		scanBaseEvents(connect: true);
		myViews = new Dictionary<string, ViewWrapper>();
		PluginHost.LoadViewHandler(this);
	}

	/// <summary>
	/// Called by the base class to Unwire events
	/// </summary>
	internal override void UnWire()
	{
		if (myGraphicsReset != null)
		{
			Host.Render.DeviceLost -= myGraphicsReset;
		}
		scanBaseEvents(connect: false);
		foreach (ViewWrapper value in myViews.Values)
		{
			value.Dispose();
		}
		myViews.Clear();
	}

	/// <summary>
	/// Used for internal wiring up of base-class variables.
	/// Called by PluginProxy
	/// </summary>
	/// <param name="newHost">Host (pluginsite) object</param>
	internal void SetHost(PluginHost newHost)
	{
		myHost = newHost;
	}

	private void scanBaseEvents(bool connect)
	{
		Type type = GetType();
		Type type2 = base.Core.GetType();
		if (!Attribute.IsDefined(type, typeof(WireUpBaseEventsAttribute)))
		{
			return;
		}
		MethodInfo[] methods = type.GetMethods(BindingFlags);
		foreach (MethodInfo methodInfo in methods)
		{
			if (!Attribute.IsDefined(methodInfo, typeof(BaseEventAttribute)))
			{
				continue;
			}
			Attribute[] customAttributes = Attribute.GetCustomAttributes(methodInfo, typeof(BaseEventAttribute));
			for (int j = 0; j < customAttributes.Length; j++)
			{
				BaseEventAttribute baseEventAttribute = (BaseEventAttribute)customAttributes[j];
				object obj;
				Type type3;
				if (baseEventAttribute.FilterName == string.Empty)
				{
					obj = this;
					type3 = type;
				}
				else
				{
					PropertyInfo property = type2.GetProperty(baseEventAttribute.FilterName, BindingFlags.Instance | BindingFlags.Public);
					if (property == null)
					{
						continue;
					}
					obj = property.GetValue(base.Core, null);
					type3 = obj.GetType();
				}
				EventInfo eventInfo = null;
				while ((eventInfo = type3.GetEvent(baseEventAttribute.EventName, BindingFlags)) == null && type3 == type)
				{
					type3 = type2;
					obj = base.Core;
				}
				if (!(eventInfo == null))
				{
					if (connect)
					{
						eventInfo.GetAddMethod(nonPublic: true).Invoke(obj, new object[1] { Delegate.CreateDelegate(eventInfo.EventHandlerType, this, methodInfo.Name) });
					}
					else
					{
						eventInfo.GetRemoveMethod(nonPublic: true).Invoke(obj, new object[1] { Delegate.CreateDelegate(eventInfo.EventHandlerType, this, methodInfo.Name) });
					}
				}
			}
		}
	}

	/// <summary>
	/// Loads a new view into the internal list (should only be called internally)
	/// </summary>
	/// <param name="name">view name</param>
	/// <param name="resource">resource path</param>
	public void LoadView(string name, string resource)
	{
		myViews.Add(name, Host.LoadViewResource(resource, GetType().Assembly));
	}

	/// <summary>
	/// Retrieves a view from the internal list
	/// </summary>
	/// <param name="name">view name</param>
	/// <returns>the specified view or null</returns>
	public ViewWrapper GetView(string name)
	{
		if (myViews.TryGetValue(name, out var value))
		{
			return value;
		}
		return null;
	}
}
