using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Runtime.Remoting;
using QC.QsprShared.Config;
using QC.QsprShared.GlobalData;

namespace QC.QTMApplication.Plugin.Common;

public class PluginHandler : MarshalByRefObject
{
	public static TimeSpan loadPluginTypesTimer = default(TimeSpan);

	private static string pluginDirectory;

	public static string PluginDirectory
	{
		get
		{
			return pluginDirectory;
		}
		set
		{
			pluginDirectory = value;
		}
	}

	private PluginHandler()
	{
	}

	public static Type[] GetMatchingPluginTypes(List<Type> pluginTypes, ArrayList PluginDetails)
	{
		DateTime now = DateTime.Now;
		string path = pluginDirectory;
		List<string> pluginExclusionList = GetPluginExclusionList(PluginDetails);
		AppDomain appDomain = AppDomain.CreateDomain("TempDom" + Guid.NewGuid().ToString());
		string fullName = Assembly.GetExecutingAssembly().FullName;
		appDomain.Load(fullName);
		BindingFlags bindingAttr = BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.CreateInstance;
		ObjectHandle objectHandle = appDomain.CreateInstance(fullName, typeof(PluginHandler).ToString(), ignoreCase: false, bindingAttr, null, null, null, null);
		PluginHandler pluginHandler = (PluginHandler)objectHandle.Unwrap();
		try
		{
			string[] matchingPluginNames = pluginHandler.GetMatchingPluginNames(path, pluginTypes, pluginExclusionList);
			return GetMatchingPluginTypes(matchingPluginNames, pluginTypes, pluginExclusionList);
		}
		catch (ReflectionTypeLoadException)
		{
			return new Type[0];
		}
		finally
		{
			AppDomain.Unload(appDomain);
			loadPluginTypesTimer += DateTime.Now - now;
		}
	}

	public static object CreateInstance(Type type, object[] args = null)
	{
		try
		{
			return Activator.CreateInstance(type, args);
		}
		catch (TargetInvocationException ex)
		{
			throw ex.InnerException;
		}
	}

	private static bool PluginShouldLoad(string filename, List<string> exclusionList)
	{
		string[] array = filename.Split('\\');
		string text = array[array.Length - 1];
		text = text.Substring(0, text.Length - 4);
		foreach (string exclusion in exclusionList)
		{
			if (text.ToUpper() == exclusion.ToUpper())
			{
				return false;
			}
		}
		return true;
	}

	private static List<string> GetPluginExclusionList(ArrayList PluginDetails)
	{
		List<string> list = new List<string>();
		foreach (PluginInfo PluginDetail in PluginDetails)
		{
			if (!PluginDetail.Load)
			{
				if (PluginDetail.Name.ToLower().EndsWith(".dll"))
				{
					list.Add(PluginDetail.Name.Substring(0, PluginDetail.Name.Length - 4));
				}
				else
				{
					list.Add(PluginDetail.Name);
				}
			}
		}
		if (GlobalStatus.AutomationMode)
		{
			if (list.Contains("QSPRAutomationPlugin"))
			{
				list.Remove("QSPRAutomationPlugin");
			}
		}
		else if (!list.Contains("QSPRAutomationPlugin"))
		{
			list.Add("QSPRAutomationPlugin");
		}
		if (!list.Contains("QTMInterface"))
		{
			list.Add("QTMInterface");
		}
		return list;
	}

	private static Type[] GetMatchingPluginTypes(string[] assemblyNames, List<Type> pluginTypes, List<string> exclusionList)
	{
		List<Type> list = new List<Type>();
		foreach (string text in assemblyNames)
		{
			if (!PluginShouldLoad(text, exclusionList))
			{
				continue;
			}
			Assembly assembly = Assembly.LoadFrom(text);
			Type[] exportedTypes = assembly.GetExportedTypes();
			Type[] array = exportedTypes;
			foreach (Type type in array)
			{
				if (pluginTypes.Contains(type.BaseType))
				{
					list.Add(type);
				}
				else if (TypeShouldBeIncluded(type, pluginTypes))
				{
					list.Add(type);
				}
			}
		}
		return list.ToArray();
	}

	private string[] GetMatchingPluginNames(string path, List<Type> pluginTypes, List<string> exclusionList)
	{
		path = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, path);
		string[] array = Directory.GetFiles(path, "*.dll");
		if (array.Length != 0)
		{
			List<string> list = new List<string>();
			string[] array2 = array;
			foreach (string text in array2)
			{
				Console.WriteLine(text);
				if (!PluginShouldLoad(text, exclusionList))
				{
					continue;
				}
				Assembly assembly = Assembly.LoadFrom(text);
				Type[] types = assembly.GetTypes();
				Type[] array3 = types;
				foreach (Type type in array3)
				{
					if (TypeShouldBeIncluded(type, pluginTypes))
					{
						list.Add(text);
						break;
					}
				}
			}
			array = list.ToArray();
		}
		return array;
	}

	private static bool TypeShouldBeIncluded(Type type, List<Type> pluginTypes)
	{
		foreach (Type pluginType in pluginTypes)
		{
			if (!pluginType.IsClass || pluginType.IsSealed)
			{
				throw new ArgumentException("Invalid type!");
			}
			if (pluginType.IsAssignableFrom(type))
			{
				return true;
			}
		}
		return false;
	}
}
