using System;
using System.Globalization;

namespace AxCopilot.Handlers;

internal static class MathEvaluator
{
	private class Evaluator
	{
		private readonly string _s;

		private int _i;

		public Evaluator(string s)
		{
			_s = s;
			_i = 0;
		}

		public double Parse()
		{
			double result = ParseExpr();
			if (_i < _s.Length)
			{
				throw new InvalidOperationException($"예기치 않은 문자: '{_s[_i]}'");
			}
			return result;
		}

		private double ParseExpr()
		{
			double num = ParseTerm();
			while (_i < _s.Length && (_s[_i] == '+' || _s[_i] == '-'))
			{
				char c = _s[_i++];
				double num2 = ParseTerm();
				num = ((c == '+') ? (num + num2) : (num - num2));
			}
			return num;
		}

		private double ParseTerm()
		{
			double num = ParsePower();
			while (_i < _s.Length && (_s[_i] == '*' || _s[_i] == '/' || _s[_i] == '%'))
			{
				char c = _s[_i++];
				double num2 = ParsePower();
				num = c switch
				{
					'/' => num / num2, 
					'*' => num * num2, 
					_ => num % num2, 
				};
			}
			return num;
		}

		private double ParsePower()
		{
			double num = ParseUnary();
			if (_i < _s.Length && _s[_i] == '^')
			{
				_i++;
				double y = ParseUnary();
				return Math.Pow(num, y);
			}
			return num;
		}

		private double ParseUnary()
		{
			if (_i < _s.Length && _s[_i] == '-')
			{
				_i++;
				return 0.0 - ParsePrimary();
			}
			if (_i < _s.Length && _s[_i] == '+')
			{
				_i++;
				return ParsePrimary();
			}
			return ParsePrimary();
		}

		private double ParsePrimary()
		{
			if (_i >= _s.Length)
			{
				throw new InvalidOperationException("수식이 불완전합니다.");
			}
			if (_i + 1 < _s.Length && _s[_i] == '0' && _s[_i + 1] == 'x')
			{
				_i += 2;
				int i = _i;
				while (_i < _s.Length && "0123456789abcdef".Contains(_s[_i]))
				{
					_i++;
				}
				string s = _s;
				int num = i;
				return Convert.ToInt64(s.Substring(num, _i - num), 16);
			}
			if (char.IsDigit(_s[_i]) || _s[_i] == '.')
			{
				int i2 = _i;
				while (_i < _s.Length && (char.IsDigit(_s[_i]) || _s[_i] == '.'))
				{
					_i++;
				}
				if (_i < _s.Length && _s[_i] == 'e')
				{
					_i++;
					if (_i < _s.Length && (_s[_i] == '+' || _s[_i] == '-'))
					{
						_i++;
					}
					while (_i < _s.Length && char.IsDigit(_s[_i]))
					{
						_i++;
					}
				}
				string s2 = _s;
				int num = i2;
				return double.Parse(s2.Substring(num, _i - num), NumberStyles.Float, CultureInfo.InvariantCulture);
			}
			if (_s[_i] == '(')
			{
				_i++;
				double result = ParseExpr();
				if (_i < _s.Length && _s[_i] == ')')
				{
					_i++;
				}
				return result;
			}
			if (char.IsLetter(_s[_i]))
			{
				int i3 = _i;
				while (_i < _s.Length && (char.IsLetterOrDigit(_s[_i]) || _s[_i] == '_'))
				{
					_i++;
				}
				string s3 = _s;
				int num = i3;
				string text = s3.Substring(num, _i - num);
				switch (text)
				{
				case "pi":
					return Math.PI;
				case "e":
					return Math.E;
				case "inf":
					return double.PositiveInfinity;
				default:
					if (_i < _s.Length && _s[_i] == '(')
					{
						_i++;
						double num2 = ParseExpr();
						double? num3 = null;
						if (_i < _s.Length && _s[_i] == ',')
						{
							_i++;
							num3 = ParseExpr();
						}
						if (_i < _s.Length && _s[_i] == ')')
						{
							_i++;
						}
						if (1 == 0)
						{
						}
						double result2;
						switch (text)
						{
						case "sqrt":
							result2 = Math.Sqrt(num2);
							break;
						case "abs":
							result2 = Math.Abs(num2);
							break;
						case "ceil":
							result2 = Math.Ceiling(num2);
							break;
						case "floor":
							result2 = Math.Floor(num2);
							break;
						case "round":
							result2 = (num3.HasValue ? Math.Round(num2, (int)num3.Value) : Math.Round(num2));
							break;
						case "sin":
							result2 = Math.Sin(num2 * Math.PI / 180.0);
							break;
						case "cos":
							result2 = Math.Cos(num2 * Math.PI / 180.0);
							break;
						case "tan":
							result2 = Math.Tan(num2 * Math.PI / 180.0);
							break;
						case "asin":
							result2 = Math.Asin(num2) * 180.0 / Math.PI;
							break;
						case "acos":
							result2 = Math.Acos(num2) * 180.0 / Math.PI;
							break;
						case "atan":
							result2 = Math.Atan(num2) * 180.0 / Math.PI;
							break;
						case "log":
							result2 = (num3.HasValue ? Math.Log(num2, num3.Value) : Math.Log10(num2));
							break;
						case "log2":
							result2 = Math.Log2(num2);
							break;
						case "ln":
							result2 = Math.Log(num2);
							break;
						case "exp":
							result2 = Math.Exp(num2);
							break;
						case "pow":
							if (!num3.HasValue)
							{
								throw new InvalidOperationException("pow(x,y) 형식으로 사용하세요.");
							}
							result2 = Math.Pow(num2, num3.Value);
							break;
						case "min":
							result2 = (num3.HasValue ? Math.Min(num2, num3.Value) : num2);
							break;
						case "max":
							result2 = (num3.HasValue ? Math.Max(num2, num3.Value) : num2);
							break;
						default:
							throw new InvalidOperationException("알 수 없는 함수: " + text + "()");
						}
						if (1 == 0)
						{
						}
						return result2;
					}
					throw new InvalidOperationException("알 수 없는 식별자: " + text);
				}
			}
			throw new InvalidOperationException($"예기치 않은 문자: '{_s[_i]}'");
		}
	}

	public static double Evaluate(string expr)
	{
		Evaluator evaluator = new Evaluator(expr.Replace(" ", "").Replace("×", "*").Replace("÷", "/")
			.Replace("，", ",")
			.ToLowerInvariant());
		return evaluator.Parse();
	}
}
