using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Text;
using QC.QsprTestObjects.Parameters;
using QC.QsprTestObjects.Tests.Base;
using QC.QTMInterface;

namespace QC.QsprTestObjects.TestConditions;

[Serializable]
public class ConditionMgr
{
	[Serializable]
	private enum TokenType
	{
		OpParen,
		ClsParen,
		AndOrNot,
		And,
		Or,
		Not,
		Condition
	}

	[Serializable]
	private class TokenTypePair
	{
		private string token;

		private TokenType type;

		public string Token => token;

		public TokenType Type => type;

		public TokenTypePair(string token, TokenType type)
		{
			this.token = token;
			this.type = type;
		}
	}

	private class ConditionBoolPair
	{
		private BaseCondition cond;

		private bool boolVal;

		private ParameterCollection currentParams;

		private TestResult currentResult;

		public bool BoolVal
		{
			set
			{
				boolVal = value;
				cond = null;
			}
		}

		public bool Result
		{
			get
			{
				if (cond != null)
				{
					boolVal = cond.IsConditionMet(currentParams, currentResult);
				}
				return boolVal;
			}
		}

		public ConditionBoolPair(BaseCondition cond, ParameterCollection currentParams, TestResult currentResult)
		{
			this.cond = cond;
			boolVal = false;
			this.currentParams = currentParams;
			this.currentResult = currentResult;
		}

		public ConditionBoolPair(bool boolVal)
		{
			cond = null;
			this.boolVal = boolVal;
			currentParams = null;
			currentResult = TestResult.Passed;
		}
	}

	private string expression;

	private List<TokenTypePair> fullParenExpr;

	private List<BaseCondition> conditions;

	private PostConditionActions action;

	private string branchOnTestName;

	private bool branchByTestName;

	private bool negateResult;

	private string branchTextIndex = "";

	private bool enabled = true;

	public string Expression
	{
		get
		{
			return expression;
		}
		set
		{
			expression = value;
			fullParenExpr = ParenthesizeAndTokenize(fullParenExpr, value);
		}
	}

	public List<BaseCondition> Conditions
	{
		get
		{
			return conditions;
		}
		set
		{
			conditions = value;
		}
	}

	public string FullParenExpression
	{
		get
		{
			StringBuilder stringBuilder = new StringBuilder();
			foreach (TokenTypePair item in fullParenExpr)
			{
				stringBuilder.Append(item.Token);
				stringBuilder.Append(" ");
			}
			return stringBuilder.ToString();
		}
	}

	public bool IsValid
	{
		get
		{
			bool result = true;
			foreach (BaseCondition condition in Conditions)
			{
				if (!condition.IsValid)
				{
					result = false;
					break;
				}
			}
			if (!Enabled)
			{
				result = false;
			}
			return result;
		}
	}

	[DefaultValue(false)]
	public bool NegateResult
	{
		get
		{
			return negateResult;
		}
		set
		{
			negateResult = value;
		}
	}

	public PostConditionActions Action
	{
		get
		{
			return action;
		}
		set
		{
			action = value;
		}
	}

	[DefaultValue("")]
	public string BranchTestIndex
	{
		get
		{
			return branchTextIndex;
		}
		set
		{
			branchTextIndex = value;
		}
	}

	public string BranchOnTestName
	{
		get
		{
			return branchOnTestName;
		}
		set
		{
			branchOnTestName = value;
		}
	}

	public bool BranchByTestName
	{
		get
		{
			return branchByTestName;
		}
		set
		{
			branchByTestName = value;
		}
	}

	[DefaultValue(true)]
	public bool Enabled
	{
		get
		{
			return enabled;
		}
		set
		{
			enabled = value;
		}
	}

	public ConditionMgr()
	{
		expression = "";
		fullParenExpr = new List<TokenTypePair>();
		conditions = new List<BaseCondition>();
	}

	public ConditionMgr(string expression)
	{
		this.expression = expression;
		fullParenExpr = ParenthesizeAndTokenize(new List<TokenTypePair>(), expression);
		conditions = new List<BaseCondition>();
	}

	public void AddCondition(BaseCondition cond)
	{
		conditions.Add(cond);
	}

	public BaseCondition GetCondition(string name)
	{
		foreach (BaseCondition condition in conditions)
		{
			if (condition.Name.Equals(name, StringComparison.OrdinalIgnoreCase))
			{
				return condition;
			}
		}
		return null;
	}

	public BaseCondition RemoveCondition(string name)
	{
		BaseCondition result = null;
		foreach (BaseCondition condition in conditions)
		{
			if (condition.Name.Equals(name, StringComparison.OrdinalIgnoreCase))
			{
				result = condition;
				conditions.Remove(condition);
			}
		}
		return result;
	}

	public bool IsConditionMet(ParameterCollection currentTestParams, TestResult currentTestResult)
	{
		Stack<ConditionBoolPair> stack = new Stack<ConditionBoolPair>();
		Stack<string> stack2 = new Stack<string>();
		if (!IsValid)
		{
			return false;
		}
		foreach (TokenTypePair item in fullParenExpr)
		{
			switch (item.Type)
			{
			case TokenType.Condition:
				stack.Push(new ConditionBoolPair(GetCondition(item.Token), currentTestParams, currentTestResult));
				break;
			case TokenType.OpParen:
			case TokenType.AndOrNot:
			case TokenType.And:
			case TokenType.Or:
			case TokenType.Not:
				stack2.Push(item.Token);
				break;
			case TokenType.ClsParen:
				while (stack2.Count > 0 && stack2.Peek() != "(")
				{
					if (stack2.Count == 0)
					{
						if (stack.Count > 1)
						{
							throw new Exception("Invalid expression passed to ConditionMgr.IsConditionMet()");
						}
						continue;
					}
					string text = stack2.Pop();
					switch (text.ToUpper())
					{
					case "NOT":
					{
						ConditionBoolPair cond2 = stack.Pop();
						stack.Push(EvaluatePiece(cond2, null, TokenType.Not));
						break;
					}
					case "AND":
					{
						ConditionBoolPair cond = stack.Pop();
						ConditionBoolPair cond2 = stack.Pop();
						stack.Push(EvaluatePiece(cond2, cond, TokenType.And));
						break;
					}
					case "OR":
					{
						ConditionBoolPair cond = stack.Pop();
						ConditionBoolPair cond2 = stack.Pop();
						stack.Push(EvaluatePiece(cond2, cond, TokenType.Or));
						break;
					}
					default:
						throw new Exception("Invalied operator string passed to ConditionMgr.IsConditionMet (only allowed are And, Or, and Not)");
					}
				}
				if (stack2.Count > 0)
				{
					stack2.Pop();
				}
				break;
			default:
				throw new Exception("Invalid TokenType in in ConditionMgr.IsConditionMet()");
			}
		}
		if (stack2.Count != 0 || stack.Count != 1)
		{
			throw new Exception("Invalid expression passed to ConditionMgr.IsConditionMet()");
		}
		if (!NegateResult)
		{
			return stack.Pop().Result;
		}
		return !stack.Pop().Result;
	}

	public ConditionMgr Clone()
	{
		ConditionMgr conditionMgr = new ConditionMgr();
		conditionMgr.Expression = Expression;
		foreach (BaseCondition condition in conditions)
		{
			conditionMgr.AddCondition(condition.Clone());
		}
		conditionMgr.Action = Action;
		conditionMgr.BranchTestIndex = BranchTestIndex;
		conditionMgr.BranchOnTestName = BranchOnTestName;
		conditionMgr.branchByTestName = branchByTestName;
		conditionMgr.Enabled = Enabled;
		conditionMgr.NegateResult = NegateResult;
		return conditionMgr;
	}

	private static List<TokenTypePair> ParenthesizeAndTokenize(List<TokenTypePair> tokens, string expr)
	{
		int num = 0;
		int num2 = 0;
		char[] anyOf = new char[3] { ' ', '(', ')' };
		tokens.Clear();
		while (num < expr.Length)
		{
			_ = expr[num];
			switch (expr[num])
			{
			case ' ':
				num++;
				continue;
			case '(':
				tokens.Add(new TokenTypePair("(", TokenType.OpParen));
				num++;
				continue;
			case ')':
				tokens.Add(new TokenTypePair(")", TokenType.ClsParen));
				num++;
				continue;
			}
			num2 = expr.IndexOfAny(anyOf, num);
			string text;
			if (num2 < 0)
			{
				text = expr.Substring(num);
				num = expr.Length;
			}
			else
			{
				text = expr.Substring(num, num2 - num);
				num += num2 - num;
			}
			if (text.Equals("AND", StringComparison.OrdinalIgnoreCase) || text.Equals("OR", StringComparison.OrdinalIgnoreCase) || text.Equals("NOT", StringComparison.OrdinalIgnoreCase))
			{
				tokens.Add(new TokenTypePair(text, TokenType.AndOrNot));
			}
			else
			{
				tokens.Add(new TokenTypePair(text, TokenType.Condition));
			}
		}
		for (num = 0; num < tokens.Count; num++)
		{
			if (tokens[num].Token.Equals("NOT", StringComparison.OrdinalIgnoreCase))
			{
				tokens.Insert(num, new TokenTypePair("(", TokenType.OpParen));
				num++;
				for (num2 = num + 1; tokens[num2].Token.Equals("NOT", StringComparison.OrdinalIgnoreCase); num2++)
				{
				}
				if (tokens[num2].Type != TokenType.Condition)
				{
					num2 = FindParen(tokens, num2, findReverse: false);
				}
				tokens.Insert(num2 + 1, new TokenTypePair(")", TokenType.ClsParen));
			}
		}
		ParenthesizeKeyword(tokens, "AND");
		ParenthesizeKeyword(tokens, "OR");
		return tokens;
	}

	private static void ParenthesizeKeyword(List<TokenTypePair> tokens, string keyword)
	{
		int i = 0;
		int num = 0;
		for (; i < tokens.Count; i++)
		{
			if (tokens[i].Token.Equals(keyword, StringComparison.OrdinalIgnoreCase))
			{
				num = i - 1;
				if (tokens[num].Type != TokenType.Condition)
				{
					num = FindParen(tokens, num, findReverse: true);
				}
				tokens.Insert(num, new TokenTypePair("(", TokenType.OpParen));
				i++;
				num = i + 1;
				if (tokens[num].Type != TokenType.Condition)
				{
					num = FindParen(tokens, num, findReverse: false);
				}
				tokens.Insert(num + 1, new TokenTypePair(")", TokenType.ClsParen));
			}
		}
	}

	private static int FindParen(List<TokenTypePair> tokens, int index, bool findReverse)
	{
		int num = 1;
		int num2 = ((!findReverse) ? 1 : (-1));
		while (num > 0)
		{
			index += num2;
			if (tokens[index].Type == TokenType.OpParen)
			{
				num += num2;
			}
			else if (tokens[index].Type == TokenType.ClsParen)
			{
				num -= num2;
			}
		}
		return index;
	}

	private static ConditionBoolPair EvaluatePiece(ConditionBoolPair cond1, ConditionBoolPair cond2, TokenType op)
	{
		switch (op)
		{
		case TokenType.Not:
			cond1.BoolVal = !cond1.Result;
			break;
		case TokenType.And:
			cond1.BoolVal = cond1.Result && cond2.Result;
			break;
		case TokenType.Or:
			cond1.BoolVal = cond1.Result || cond2.Result;
			break;
		default:
			throw new Exception("Invalied operator string passed to ConditionMgr.EvaluatePiece (only allowed are And, Or, and Not)");
		}
		return cond1;
	}
}
