using System;
using System.Collections;
using System.Diagnostics;
using System.Reflection;
using System.Text.RegularExpressions;
using System.Threading;
using System.Windows.Forms;
using QC.QsprShared.Trace;
using QC.QTMDotNetKernel.Dialogs;
using QC.QTMDotNetKernel.Handlers;
using QC.QTMDotNetKernel.Registry;
using QC.QTMDotNetKernelInterface.Environment;
using QC.QTMInterface;

namespace QC.QTMDotNetKernel;

[Serializable]
public class DotNetKernel : MarshalByRefObject
{
	public struct TestCaseProperties
	{
		public bool canAddToTestPlan;

		public bool isSetupTest;

		public bool isTearDownTest;

		public bool isManual;
	}

	internal const char CLASS_SEP = '.';

	internal const int iPassed = 1;

	internal const int iFailed = 0;

	internal const int iException = -1;

	internal const string FALSE = "FALSE";

	public const char TEST_SEP = '\ufffd';

	public TestRegistry registry = new TestRegistry();

	public ArrayList domainList = new ArrayList();

	private QSPREnvironment Env;

	private bool useThreadPool;

	public event TestDoneEventHandler OnTestDone;

	public event TextOutEventHandler OnTextOut;

	public event PrintDebugEventHandler OnPrintDebug;

	public event ClearDebugMsgEventHandler OnClearDebugMsg;

	public event SaveDebugMsgEventHandler OnSaveDebugMsg;

	public event ClearAllDebugMsgEventHandler OnClearAllDebugMsg;

	public event SaveAllDebugMsgEventHandler OnSaveAllDebugMsg;

	public event TPL2WindowEventHandler OnTPL2WindowMsg;

	public event HTMLWindowEventHandler OnHTMLWindowMsg;

	public event GenericMsgEventHandler OnGenericMsg;

	public event GenericParamMsgEventHandler OnGenericParamMsg;

	public event CustomMsgEventHandler OnCustomMsg;

	public event SetDebugWindowInFocusEventHandler OnSetDebugWindowInFocusMsg;

	public DotNetKernel(QSPREnvironment env)
	{
		Env = env;
		registry.Environment = Env;
		Env.DebugMessage.OnTextOut += TextOut;
		Env.DebugMessage.OnPrintDebug += PrintDebug;
		Env.DebugMessage.OnSaveDebugMsg += SaveDebugMsg;
		Env.DebugMessage.OnClearDebugMsg += ClearDebugMsg;
		Env.DebugMessage.OnClearAllDebugMsg += ClearAllDebugMsg;
		Env.DebugMessage.OnSaveAllDebugMsg += SaveAllDebugMsg;
		Env.DebugMessage.OnTpl2WindowMsg += TPL2WindowMessage;
		Env.DebugMessage.OnHTMLWindowMsg += HTMLWindowMessage;
		Env.DebugMessage.OnGenericMsg += GenericMessage;
		Env.DebugMessage.OnGenericParamMsg += GenericParamMessage;
		Env.DebugMessage.OnCustomMsg += CustomMessage;
		Env.DebugMessage.OnSetDebugWindowInFocus += SetDebugWindowInFocus;
	}

	public bool LoadTestDLL(string assemblyName, ref string listOfTests, bool isExclusive, ref bool hideTests)
	{
		try
		{
			TestAssembly testAssembly;
			if (isExclusive)
			{
				testAssembly = loadTestAssembly(assemblyName);
				Env.DebugMessage.Write("Temp", "Exclusively Loading Assembly: " + assemblyName);
			}
			else
			{
				testAssembly = new TestAssembly(assemblyName);
				Env.DebugMessage.Write("Temp", "Loading Assembly: " + assemblyName);
			}
			listOfTests = GetTestList(testAssembly);
			hideTests = testAssembly.HideTests;
			return registry.Add(testAssembly);
		}
		catch (ReflectionTypeLoadException ex)
		{
			string text = "";
			Exception[] loaderExceptions = ex.LoaderExceptions;
			foreach (Exception ex2 in loaderExceptions)
			{
				if (text.Length != 0)
				{
					text += "\n";
				}
				text += ex2.Message;
			}
			MessageBox.Show(ex.Message + "\n" + text, "QSPR Message Center - Assembly Load Failed");
		}
		catch (Exception ex3)
		{
			MessageBox.Show(ex3.Message, "QSPR Message Center - Assembly Load Failed");
		}
		return false;
	}

	private string GetTestList(TestAssembly testAssembly)
	{
		string text = "";
		string text2 = "";
		try
		{
			foreach (TestClass value in testAssembly.Classes.Values)
			{
				foreach (TestMethod value2 in value.Methods.Values)
				{
					if (!value2.IsSystemMethod)
					{
						if (value2.IsTestMethod)
						{
							string text3 = text;
							text = text3 + value.FullName + "." + value2.Name + ",";
						}
						else
						{
							string text4 = text2;
							text2 = text4 + value.FullName + "." + value2.Name + ",";
						}
					}
				}
			}
		}
		catch
		{
		}
		return text + '\ufffd' + text2;
	}

	public bool UnLoadTestDLL(string assemblyName, bool isExclusive)
	{
		if (!isExclusive)
		{
			Env.DebugMessage.Write("Temp", "Unloading Assembly: " + assemblyName);
			return registry.Remove(assemblyName);
		}
		try
		{
			AppDomain domainChange = getDomainChange(assemblyName);
			if (domainChange != null)
			{
				Env.DebugMessage.Write("Temp", "Unloading Exclusive Assembly: " + assemblyName);
				domainList.Remove(domainChange);
				AppDomain.Unload(domainChange);
				return registry.Remove(assemblyName);
			}
			return false;
		}
		catch (Exception ex)
		{
			Env.DebugMessage.Write("QTM Error", ex.Message);
			return false;
		}
	}

	public void RunTest(string assemblyName, string testName, ref object objParameters, ref object objProperties, bool fastModeSkipAll = false)
	{
		TraceMessage.WriteAlways(ToString(), "ENTER: DotNetKernel.RunTest(" + testName + ")", TraceMessageType.Trace);
		try
		{
			MethodData mData = default(MethodData);
			mData.AssemblyName = assemblyName;
			mData.TestName = testName;
			mData.Parameters = objParameters;
			mData.Properties = objProperties;
			mData.FastModeSkipAll = fastModeSkipAll;
			TraceMessage.WriteAlways(ToString(), "Starting threadpool thread to run dot net test: " + testName, TraceMessageType.Trace);
			startThreadPool(mData);
		}
		catch (Exception ex)
		{
			TraceMessage.WriteAlways(ToString(), "EXCEPTION: DotNetKernel.RunTest(" + testName + ") " + ex.ToString(), TraceMessageType.Trace);
			Env.DebugMessage.Write("QTM Error", ex.Message);
		}
		TraceMessage.WriteAlways(ToString(), "EXIT: DotNetKernel.RunTest(" + testName + ")", TraceMessageType.Trace);
	}

	private void RunDotNetTest(object stateObject)
	{
		TraceMessage.WriteAlways(ToString(), "ENTER: DotNetKernel.RunDotNetTest()", TraceMessageType.Trace);
		MethodData methodData = (MethodData)stateObject;
		PropertyHandler propertyHandler = new PropertyHandler(methodData.Properties);
		try
		{
			string[] array = methodData.TestName.Split('.');
			string text = array[array.Length - 1];
			string key = methodData.TestName.Substring(0, methodData.TestName.Length - text.Length - 1);
			TestAssembly testAssembly = (TestAssembly)registry.Assemblies[methodData.AssemblyName];
			TestClass testClass;
			try
			{
				testClass = (TestClass)testAssembly.Classes[key];
			}
			catch (Exception)
			{
				registry.Remove(methodData.AssemblyName);
				testAssembly = loadTestAssembly(methodData.AssemblyName);
				testClass = (TestClass)testAssembly.Classes[key];
			}
			TestMethod testMethod = (TestMethod)testClass.Methods[text];
			testMethod.ClearSelections();
			testMethod.CreateParameterSelections(methodData.Parameters);
			object obj = testClass.Instance;
			if (!testMethod.Static && (obj == null || propertyHandler.CreateNewInstance))
			{
				object[] args = null;
				if (testClass.TestType.IsSubclassOf(typeof(QSPRTest)))
				{
					args = new object[1] { Env };
				}
				obj = (testClass.Instance = testClass.TestType.InvokeMember(null, BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.CreateInstance, null, null, args));
			}
			if (!testMethod.Static)
			{
				Hashtable hashtable = ParameterHandler.CreateInputProperties(testMethod.GetInputProperties(), methodData.Parameters);
				foreach (string key2 in hashtable.Keys)
				{
					object[] args2 = new object[1] { hashtable[key2] };
					testClass.TestType.InvokeMember(key2, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.SetProperty, null, obj, args2);
				}
			}
			object obj3 = ParameterHandler.CreateArguments(testMethod.GetArguments(virtualAlso: false), methodData.Parameters);
			ParameterHandler.SetOutputParameterLimits(testMethod.Parameters, methodData.Parameters);
			object[] array2 = (object[])obj3;
			object obj4 = null;
			if (methodData.FastModeSkipAll && CheckForFastModeSkipAllException(text))
			{
				obj4 = TestResult.Skipped;
			}
			else if (!testMethod.Static)
			{
				TraceMessage.WriteAlways(ToString(), "Running the non static method: " + text, TraceMessageType.Trace);
				obj4 = testClass.TestType.InvokeMember(text, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.InvokeMethod, null, obj, array2);
			}
			else
			{
				TraceMessage.WriteAlways(ToString(), "Running the static method: " + text, TraceMessageType.Trace);
				obj4 = testClass.TestType.InvokeMember(text, BindingFlags.Static | BindingFlags.Public | BindingFlags.InvokeMethod, null, null, array2);
			}
			ParameterHandler.SetOutputArguments(testMethod.Parameters, array2);
			ParameterHandler.SetReturnValue(testMethod.Parameters, obj4);
			if (!testMethod.Static)
			{
				Hashtable outputProperties = testMethod.GetOutputProperties();
				ParameterHandler.SetOutputProperties(outputProperties, methodData.Parameters);
				foreach (TestParameter value in outputProperties.Values)
				{
					try
					{
						if (value.ReturnValue)
						{
							value.Value = obj4.ToString();
							continue;
						}
						object obj5 = testClass.TestType.InvokeMember(value.Name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.GetProperty, null, obj, null);
						if (value.IsArray)
						{
							ParameterHandler.SetOutputProperty(value, (Array)obj5);
						}
						else if (value.IsNullable)
						{
							value.Value = obj5?.ToString();
						}
						else if (obj5 != null)
						{
							value.Value = obj5.ToString();
						}
					}
					catch
					{
					}
				}
			}
			testMethod.SetDynamicOutputParameters();
			object parameterObject = ParameterHandler.GetParameterObject(testMethod.Parameters);
			string strResult = "";
			if (obj4 != null)
			{
				strResult = obj4.ToString();
			}
			int iResult = ParseResult(strResult, propertyHandler.FailString);
			TestDone(propertyHandler.DescriptiveName, iResult, parameterObject);
		}
		catch (Exception ex2)
		{
			TraceMessage.WriteAlways(ToString(), "EXCEPTION: DotNetKernel.RunDotNetTest() " + ex2.ToString(), TraceMessageType.Trace);
			TestDone(propertyHandler.DescriptiveName, 2, ex2);
		}
		TraceMessage.WriteAlways(ToString(), "EXIT: DotNetKernel.RunDotNetTest()", TraceMessageType.Trace);
	}

	private bool CheckForFastModeSkipAllException(string name)
	{
		if (string.Compare(name, "TestPlanStartup", ignoreCase: true) == 0 || string.Compare(name, "TestPlanShutdown", ignoreCase: true) == 0)
		{
			return false;
		}
		return true;
	}

	public void StopTest()
	{
	}

	private TestAssembly loadTestAssembly(string assemblyName)
	{
		TestAssembly testAssembly;
		if (AppDomain.CurrentDomain.Id == 1)
		{
			AppDomain appDomain = getDomainChange(assemblyName);
			if (appDomain == null)
			{
				appDomain = createDomain(assemblyName);
			}
			testAssembly = initializeTestAssembly(assemblyName, appDomain);
		}
		else
		{
			testAssembly = new TestAssembly(assemblyName);
			registry.Add(testAssembly);
		}
		return testAssembly;
	}

	private void startThreadPool(MethodData mData)
	{
		TraceMessage.WriteAlways(ToString(), "ENTER: DotNetKernel.startThreadPool()", TraceMessageType.Trace);
		if (!useThreadPool)
		{
			Thread thread = new Thread(RunDotNetTest);
			thread.Priority = ThreadPriority.AboveNormal;
			thread.Name = "Running test Thread";
			thread.Start(mData);
		}
		else
		{
			ThreadPool.QueueUserWorkItem(RunDotNetTest, mData);
		}
		TraceMessage.WriteAlways(ToString(), "EXIT: DotNetKernel.startThreadPool()", TraceMessageType.Trace);
	}

	private AppDomain getDomainChange(string assemblyName)
	{
		int num = 0;
		int count = domainList.Count;
		string text = "";
		AppDomain appDomain = null;
		for (num = 0; num < count; num++)
		{
			appDomain = (AppDomain)domainList[num];
			text = appDomain.FriendlyName;
			if (text == assemblyName)
			{
				break;
			}
		}
		if (num == count)
		{
			appDomain = null;
		}
		return appDomain;
	}

	private AppDomain createDomain(string assemblyName)
	{
		AppDomainSetup appDomainSetup = new AppDomainSetup();
		appDomainSetup.ShadowCopyFiles = "true";
		AppDomain appDomain = AppDomain.CreateDomain(assemblyName, null, appDomainSetup);
		domainList.Add(appDomain);
		return appDomain;
	}

	private TestAssembly initializeTestAssembly(string assemblyName, AppDomain myDomain)
	{
		object[] args = new object[1] { assemblyName };
		return (TestAssembly)myDomain.CreateInstanceAndUnwrap(Assembly.GetExecutingAssembly().FullName, "QC.QTMDotNetKernel.Registry.TestAssembly", ignoreCase: false, BindingFlags.Default, null, args, null, null);
	}

	private string[] DebugDomain()
	{
		AppDomain currentDomain = AppDomain.CurrentDomain;
		_ = currentDomain.FriendlyName;
		Assembly[] assemblies = currentDomain.GetAssemblies();
		string[] array = new string[assemblies.Length];
		for (int i = 0; i < assemblies.Length; i++)
		{
			array[i] = assemblies[i].FullName;
		}
		return array;
	}

	private int ParseResult(string strResult, string failString)
	{
		try
		{
			if (strResult == TestResult.Skipped.ToString())
			{
				return 7;
			}
			if (strResult == TestResult.Failed.ToString())
			{
				return 0;
			}
			if (strResult == TestResult.Passed.ToString())
			{
				return 1;
			}
			Regex regex = new Regex(failString);
			MatchCollection matchCollection = regex.Matches(strResult);
			if (matchCollection.Count > 0)
			{
				return 0;
			}
			return 1;
		}
		catch
		{
		}
		return -1;
	}

	public void Shutdown()
	{
	}

	public bool Options(string assemblyName, ref Hashtable listOfTestParameter, bool isExclusive)
	{
		try
		{
			TestAssembly testAssembly;
			if (isExclusive)
			{
				AppDomain appDomain = getDomainChange(assemblyName);
				if (appDomain == null)
				{
					appDomain = createDomain(assemblyName);
				}
				testAssembly = initializeTestAssembly(assemblyName, appDomain);
			}
			else
			{
				testAssembly = new TestAssembly(assemblyName);
			}
			testAssembly.CreateTestSelections(listOfTestParameter);
			DotNetTestSelectionUI dotNetTestSelectionUI = new DotNetTestSelectionUI(testAssembly);
			if (dotNetTestSelectionUI.ShowDialog() == DialogResult.OK)
			{
				CreateTestParamList(testAssembly, out listOfTestParameter);
				return true;
			}
		}
		catch (ReflectionTypeLoadException ex)
		{
			string text = "";
			Exception[] loaderExceptions = ex.LoaderExceptions;
			foreach (Exception ex2 in loaderExceptions)
			{
				if (text.Length != 0)
				{
					text += "\n";
				}
				text += ex2.Message;
			}
			MessageBox.Show(ex.Message + "\n" + text, "QSPR Message Center - Assembly Load Failed");
		}
		catch (Exception ex3)
		{
			MessageBox.Show(ex3.Message, "QSPR Message Center - Assembly Load Failed");
		}
		return false;
	}

	private TestMethod GetTestMethod(string assemblyName, string testName)
	{
		string key = testName.Substring(0, testName.LastIndexOf('.'));
		TestAssembly testAssembly = (TestAssembly)registry.Assemblies[assemblyName];
		TestClass testClass = (TestClass)testAssembly.Classes[key];
		return (TestMethod)testClass.Methods[testName.Substring(testName.LastIndexOf('.') + 1)];
	}

	public string GetTestDescription(string assemblyName, string testName)
	{
		string result = "";
		try
		{
			result = GetTestMethod(assemblyName, testName).Description;
		}
		catch (Exception)
		{
		}
		return result;
	}

	public string GetTestErrorCode(string assemblyName, string testName)
	{
		string result = "";
		try
		{
			result = GetTestMethod(assemblyName, testName).ErrorCode;
		}
		catch (Exception)
		{
		}
		return result;
	}

	public TestCaseProperties GetTestCaseProperties(string assemblyName, string testName)
	{
		TestCaseProperties result = default(TestCaseProperties);
		try
		{
			result.canAddToTestPlan = GetTestMethod(assemblyName, testName).CanAddToTestPlan;
			result.isSetupTest = GetTestMethod(assemblyName, testName).IsSetupTest;
			result.isTearDownTest = GetTestMethod(assemblyName, testName).IsTearDownTest;
			result.isManual = GetTestMethod(assemblyName, testName).IsManual;
		}
		catch (Exception)
		{
		}
		return result;
	}

	private void CreateTestParamList(TestAssembly testAssembly, out Hashtable listOfTestParameter)
	{
		listOfTestParameter = new Hashtable();
		try
		{
			foreach (TestClass value in testAssembly.Classes.Values)
			{
				foreach (TestMethod value2 in value.Methods.Values)
				{
					if (!value2.IsSystemMethod && value2.Selected)
					{
						string key = value.FullName + "." + value2.Name;
						object parameterObject = ParameterHandler.GetParameterObject(value2.CheckedParametersSortedList);
						listOfTestParameter.Add(key, parameterObject);
					}
				}
			}
		}
		catch
		{
		}
	}

	public void GetAllParamInfo(string assemblyName, string testName, ref object objParameters)
	{
		string[] array = testName.Split('.');
		string text = array[array.Length - 1];
		string key = testName.Substring(0, testName.Length - text.Length - 1);
		try
		{
			TestAssembly testAssembly = (TestAssembly)registry.Assemblies[assemblyName];
			TestClass testClass = (TestClass)testAssembly.Classes[key];
			TestMethod testMethod = (TestMethod)testClass.Methods[text];
			object parameterObject = ParameterHandler.GetParameterObject(testMethod.GetArgumentsList());
			object parameterObject2 = ParameterHandler.GetParameterObject(testMethod.GetCustomProperties());
			object parameterObject3 = ParameterHandler.GetParameterObject(testMethod.GetNonCustomProperties());
			object[] array2 = new object[3] { parameterObject, parameterObject2, parameterObject3 };
			objParameters = array2;
		}
		catch
		{
		}
	}

	protected void TestDone(string testName, int iResult, object parameters)
	{
		TraceMessage.WriteAlways(ToString(), "ENTER: DotNetKernel.TestDone(" + testName + ")", TraceMessageType.Trace);
		if (this.OnTestDone != null)
		{
			this.OnTestDone(testName, iResult, parameters);
		}
		TraceMessage.WriteAlways(ToString(), "EXIT: DotNetKernel.TestDone(" + testName + ")", TraceMessageType.Trace);
	}

	protected void TextOut(string windowName, string windowText, TraceLevel traceLevel)
	{
		if (this.OnTextOut != null)
		{
			this.OnTextOut(windowName, windowText, traceLevel);
		}
	}

	protected void PrintDebug(string windowName, PrintMsgOptions IsHeader, params string[] Values)
	{
		if (this.OnPrintDebug != null)
		{
			this.OnPrintDebug(windowName, IsHeader, Values);
		}
	}

	protected void ClearDebugMsg(string windowName)
	{
		if (this.OnClearDebugMsg != null)
		{
			this.OnClearDebugMsg(windowName);
		}
	}

	protected void SaveDebugMsg(string windowName, bool appendText)
	{
		if (this.OnSaveDebugMsg != null)
		{
			this.OnSaveDebugMsg(windowName, appendText);
		}
	}

	protected void ClearAllDebugMsg()
	{
		if (this.OnClearAllDebugMsg != null)
		{
			this.OnClearAllDebugMsg();
		}
	}

	protected void SaveAllDebugMsg(bool appendText)
	{
		if (this.OnSaveAllDebugMsg != null)
		{
			this.OnSaveAllDebugMsg(appendText);
		}
	}

	protected void TPL2WindowMessage(string windowName, Tpl2Command command, Tpl2Info commandInfo)
	{
		if (this.OnTPL2WindowMsg != null)
		{
			this.OnTPL2WindowMsg(windowName, command, commandInfo);
		}
	}

	protected void HTMLWindowMessage(string windowName, HTMLCommand command, string textStr)
	{
		if (this.OnHTMLWindowMsg != null)
		{
			this.OnHTMLWindowMsg(windowName, command, textStr);
		}
	}

	protected void GenericMessage(string messageType, GenericTestData data = null, byte pluginTypes = 7, string pluginNames = "")
	{
		if (this.OnGenericMsg != null)
		{
			this.OnGenericMsg(messageType, data, pluginTypes, pluginNames);
		}
	}

	protected void GenericParamMessage(string messageType, string id = "", string name = "", string value = "", string units = "", string lowerLimit = "", string upperLimit = "", byte pluginTypes = 7, string pluginNames = "")
	{
		if (this.OnGenericParamMsg != null)
		{
			this.OnGenericParamMsg(messageType, id, name, value, units, lowerLimit, upperLimit, pluginTypes, pluginNames);
		}
	}

	protected void CustomMessage(object customData, byte pluginTypes = 7, string pluginNames = "")
	{
		if (this.OnCustomMsg != null)
		{
			this.OnCustomMsg(customData, pluginTypes, pluginNames);
		}
	}

	protected void SetDebugWindowInFocus(string debugWindowName)
	{
		if (this.OnSetDebugWindowInFocusMsg != null)
		{
			this.OnSetDebugWindowInFocusMsg(debugWindowName);
		}
	}
}
