using System;
using System.Collections;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using HG;
using HG.Reflection;
using RoR2.ConVar;
using UnityEngine;
using UnityEngine.Networking;

namespace RoR2;

public class Console : MonoBehaviour
{
	public struct Log
	{
		public string message;

		public string stackTrace;

		public LogType logType;
	}

	public delegate void LogReceivedDelegate(Log log);

	private class Lexer
	{
		private enum TokenType
		{
			Identifier,
			NestedString
		}

		private string srcString;

		private int readIndex;

		private StringBuilder stringBuilder = new StringBuilder();

		public Lexer(string srcString)
		{
			this.srcString = srcString;
			readIndex = 0;
		}

		private static bool IsIgnorableCharacter(char character)
		{
			if (!IsSeparatorCharacter(character) && !IsQuoteCharacter(character) && !IsIdentifierCharacter(character))
			{
				return character != '/';
			}
			return false;
		}

		private static bool IsSeparatorCharacter(char character)
		{
			if (character != ';')
			{
				return character == '\n';
			}
			return true;
		}

		private static bool IsQuoteCharacter(char character)
		{
			if (character != '\'')
			{
				return character == '"';
			}
			return true;
		}

		private static bool IsIdentifierCharacter(char character)
		{
			if (!char.IsLetterOrDigit(character) && character != '_' && character != '.' && character != '-')
			{
				return character == ':';
			}
			return true;
		}

		private bool TrimComment()
		{
			if (readIndex >= srcString.Length)
			{
				return false;
			}
			if (srcString[readIndex] == '/')
			{
				if (readIndex + 1 < srcString.Length)
				{
					char c = srcString[readIndex + 1];
					if (c == '/')
					{
						while (readIndex < srcString.Length)
						{
							if (srcString[readIndex] == '\n')
							{
								readIndex++;
								return true;
							}
							readIndex++;
						}
						return true;
					}
					if (c == '*')
					{
						while (readIndex < srcString.Length - 1)
						{
							if (srcString[readIndex] == '*' && srcString[readIndex + 1] == '/')
							{
								readIndex += 2;
								return true;
							}
							readIndex++;
						}
						return true;
					}
				}
				readIndex++;
			}
			return false;
		}

		private void TrimWhitespace()
		{
			while (readIndex < srcString.Length && IsIgnorableCharacter(srcString[readIndex]))
			{
				readIndex++;
			}
		}

		private void TrimUnused()
		{
			do
			{
				TrimWhitespace();
			}
			while (TrimComment());
		}

		private static int UnescapeNext(string srcString, int startPos, out char result)
		{
			result = '\\';
			int num = startPos;
			num++;
			if (num < srcString.Length)
			{
				char c = srcString[num];
				switch (c)
				{
				case '"':
				case '\'':
				case '\\':
					result = c;
					return 2;
				case 'n':
					result = '\n';
					return 2;
				}
			}
			return 1;
		}

		public string NextToken()
		{
			TrimUnused();
			if (readIndex == srcString.Length)
			{
				return null;
			}
			TokenType tokenType = TokenType.Identifier;
			char c = srcString[readIndex];
			char c2 = '\0';
			if (IsQuoteCharacter(c))
			{
				tokenType = TokenType.NestedString;
				c2 = c;
				readIndex++;
			}
			else if (IsSeparatorCharacter(c))
			{
				readIndex++;
				return ";";
			}
			char result;
			for (; readIndex < srcString.Length; stringBuilder.Append(result), readIndex++)
			{
				result = srcString[readIndex];
				switch (tokenType)
				{
				case TokenType.Identifier:
					if (IsIdentifierCharacter(result))
					{
						continue;
					}
					break;
				case TokenType.NestedString:
					if (result == '\\')
					{
						readIndex += UnescapeNext(srcString, readIndex, out result) - 1;
						continue;
					}
					if (result != c2)
					{
						continue;
					}
					readIndex++;
					break;
				default:
					continue;
				}
				break;
			}
			string result2 = stringBuilder.ToString();
			stringBuilder.Length = 0;
			return result2;
		}

		public Queue<string> GetTokens()
		{
			Queue<string> queue = new Queue<string>();
			for (string text = NextToken(); text != null; text = NextToken())
			{
				queue.Enqueue(text);
			}
			queue.Enqueue(";");
			return queue;
		}
	}

	private class Substring
	{
		public string srcString;

		public int startIndex;

		public int length;

		public int endIndex => startIndex + length;

		public string str => srcString.Substring(startIndex, length);

		public Substring nextToken => new Substring
		{
			srcString = srcString,
			startIndex = startIndex + length,
			length = 0
		};
	}

	private class ConCommand
	{
		public ConVarFlags flags;

		public ConCommandDelegate action;

		public string helpText;
	}

	public delegate void ConCommandDelegate(ConCommandArgs args);

	private class ConvarClass
	{
		public string containerAssemblyName;

		public List<string> convars = new List<string>();
	}

	public readonly struct CmdSender
	{
		public readonly LocalUser localUser;

		public readonly NetworkUser networkUser;

		public CmdSender(LocalUser localUser)
		{
			this.localUser = localUser;
			networkUser = localUser?.currentNetworkUser;
		}

		public CmdSender(NetworkUser networkUser)
		{
			localUser = networkUser?.localUser;
			this.networkUser = networkUser;
		}

		public static implicit operator CmdSender(LocalUser localUser)
		{
			return new CmdSender(localUser);
		}

		public static implicit operator CmdSender(NetworkUser networkUser)
		{
			return new CmdSender(networkUser);
		}
	}

	private enum SystemConsoleType
	{
		None,
		Attach,
		Alloc
	}

	public class AutoComplete
	{
		private struct MatchInfo
		{
			public string str;

			public int similarity;
		}

		private List<string> searchableStrings = new List<string>();

		private string searchString;

		public List<string> resultsList = new List<string>();

		public List<string> GetSearchableStrings => searchableStrings;

		public AutoComplete(Console console)
		{
			HashSet<string> hashSet = new HashSet<string>();
			for (int i = 0; i < userCmdHistory.Count; i++)
			{
				hashSet.Add(userCmdHistory[i]);
			}
			foreach (KeyValuePair<string, BaseConVar> allConVar in console.allConVars)
			{
				hashSet.Add(allConVar.Key);
			}
			foreach (KeyValuePair<string, ConCommand> item in console.concommandCatalog)
			{
				hashSet.Add(item.Key);
			}
			foreach (string item2 in hashSet)
			{
				searchableStrings.Add(item2);
			}
			searchableStrings.Sort();
		}

		public bool SetSearchString(string newSearchString)
		{
			newSearchString = newSearchString.ToLower(CultureInfo.InvariantCulture);
			if (newSearchString == searchString)
			{
				return false;
			}
			searchString = newSearchString;
			List<MatchInfo> list = new List<MatchInfo>();
			for (int i = 0; i < searchableStrings.Count; i++)
			{
				string text = searchableStrings[i];
				int num = Math.Min(text.Length, searchString.Length);
				int j;
				for (j = 0; j < num && char.ToLower(text[j]) == searchString[j]; j++)
				{
				}
				if (j > 1)
				{
					list.Add(new MatchInfo
					{
						str = text,
						similarity = j
					});
				}
			}
			list.Sort(delegate(MatchInfo a, MatchInfo b)
			{
				if (a.similarity == b.similarity)
				{
					return string.CompareOrdinal(a.str, b.str);
				}
				return (a.similarity <= b.similarity) ? 1 : (-1);
			});
			resultsList = new List<string>();
			for (int k = 0; k < list.Count; k++)
			{
				resultsList.Add(list[k].str);
			}
			return true;
		}
	}

	public class CheatsConVar : BaseConVar
	{
		public static readonly CheatsConVar instance = new CheatsConVar("cheats", ConVarFlags.ExecuteOnServer, "0", "Enable cheats. Achievements, unlock progression, and stat tracking will be disabled until the application is restarted.");

		private bool _boolValue;

		public bool boolValue
		{
			get
			{
				return _boolValue;
			}
			private set
			{
				if (_boolValue)
				{
					sessionCheatsEnabled = true;
				}
			}
		}

		public CheatsConVar(string name, ConVarFlags flags, string defaultValue, string helpText)
			: base(name, flags, defaultValue, helpText)
		{
		}

		public override void SetString(string newValue)
		{
			if (TextSerialization.TryParseInvariant(newValue, out int result))
			{
				boolValue = result != 0;
			}
		}

		public override string GetString()
		{
			if (!boolValue)
			{
				return "0";
			}
			return "1";
		}
	}

	public static List<Log> logs = new List<Log>();

	private Dictionary<string, string> vstrs = new Dictionary<string, string>();

	private Dictionary<string, ConCommand> concommandCatalog = new Dictionary<string, ConCommand>();

	private Dictionary<string, BaseConVar> allConVars;

	private List<BaseConVar> archiveConVars;

	public static List<string> userCmdHistory = new List<string>();

	private const int VK_RETURN = 13;

	private const int WM_KEYDOWN = 256;

	private static byte[] inputStreamBuffer = new byte[256];

	private static Queue<string> stdInQueue = new Queue<string>();

	private static Thread stdInReaderThread = null;

	private static SystemConsoleType systemConsoleType = SystemConsoleType.None;

	public static bool loadingIsComplete = false;

	private int maxPassesBeforeYielding = 25;

	private static readonly StringBuilder sharedStringBuilder = new StringBuilder();

	private const string configFolder = "/Config/";

	private const string archiveConVarsPath = "/Config/config.cfg";

	private static IntConVar maxMessages = new IntConVar("max_messages", ConVarFlags.Archive, "25", "Maximum number of messages that can be held in the console log.");

	public static Console instance { get; private set; }

	public static bool sessionCheatsEnabled { get; private set; } = false;


	public static event LogReceivedDelegate onLogReceived;

	public static event Action onClear;

	[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
	private static void RegisterLogHandler()
	{
		Application.logMessageReceived += HandleLog;
	}

	private static void HandleLog(string message, string stackTrace, LogType logType)
	{
		switch (logType)
		{
		case LogType.Error:
			message = string.Format(CultureInfo.InvariantCulture, "<color=#FF0000>{0}</color>", message);
			break;
		case LogType.Warning:
			message = string.Format(CultureInfo.InvariantCulture, "<color=#FFFF00>{0}</color>", message);
			break;
		}
		Log log = default(Log);
		log.message = message;
		log.stackTrace = stackTrace;
		log.logType = logType;
		Log log2 = log;
		logs.Add(log2);
		if (maxMessages.value > 0)
		{
			while (logs.Count > maxMessages.value)
			{
				logs.RemoveAt(0);
			}
		}
		if (Console.onLogReceived != null)
		{
			Console.onLogReceived(log2);
		}
	}

	private string GetVstrValue(LocalUser user, string identifier)
	{
		if (user == null)
		{
			if (vstrs.TryGetValue(identifier, out var value))
			{
				return value;
			}
			return "";
		}
		return "";
	}

	public void CacheConVars()
	{
		_ = typeof(BaseConVar).Assembly;
		List<ConvarClass> list = new List<ConvarClass>();
		Type[] types = typeof(BaseConVar).Assembly.GetTypes();
		foreach (Type type in types)
		{
			bool flag = false;
			FieldInfo[] fields = type.GetFields(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
			foreach (FieldInfo fieldInfo in fields)
			{
				if (!fieldInfo.FieldType.IsSubclassOf(typeof(BaseConVar)))
				{
					continue;
				}
				if (fieldInfo.IsStatic)
				{
					BaseConVar conVar = (BaseConVar)fieldInfo.GetValue(null);
					RegisterConVarInternal(conVar);
					if (!flag)
					{
						flag = true;
						list.Add(new ConvarClass
						{
							containerAssemblyName = type.FullName
						});
					}
					list[list.Count - 1].convars.Add(fieldInfo.Name);
				}
				else if (CustomAttributeExtensions.GetCustomAttribute<CompilerGeneratedAttribute>(type) == null)
				{
					Debug.LogErrorFormat("ConVar defined as {0}.{1} could not be registered. ConVars must be static fields.", type.Name, fieldInfo.Name);
				}
			}
			MethodInfo[] methods = type.GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
			foreach (MethodInfo methodInfo in methods)
			{
				if (CustomAttributeExtensions.GetCustomAttribute<ConVarProviderAttribute>(methodInfo) == null)
				{
					continue;
				}
				Debug.LogErrorFormat("ConVarProviderAttribute on method {0}.{1}", type.Name, methodInfo.Name);
				if (methodInfo.ReturnType != typeof(IEnumerable<BaseConVar>) || methodInfo.GetParameters().Length != 0)
				{
					Debug.LogErrorFormat("ConVar provider {0}.{1} does not match the signature \"static IEnumerable<ConVar.BaseConVar>()\".", type.Name, methodInfo.Name);
					continue;
				}
				if (!methodInfo.IsStatic)
				{
					Debug.LogErrorFormat("ConVar provider {0}.{1} could not be invoked. Methods marked with the ConVarProvider attribute must be static.", type.Name, methodInfo.Name);
					continue;
				}
				foreach (BaseConVar item in (IEnumerable<BaseConVar>)methodInfo.Invoke(null, Array.Empty<object>()))
				{
					RegisterConVarInternal(item);
				}
			}
		}
		if (list.Count <= 0)
		{
			return;
		}
		StreamWriter streamWriter = new StreamWriter(Application.streamingAssetsPath + "/ConVarNames.txt");
		foreach (ConvarClass item2 in list)
		{
			streamWriter.WriteLine(item2.containerAssemblyName);
			streamWriter.WriteLine(item2.convars.Count);
			foreach (string convar in item2.convars)
			{
				streamWriter.WriteLine(convar);
			}
		}
		streamWriter.Close();
	}

	private IEnumerator InternalInitConVarsCoroutine()
	{
		CacheConVars();
		yield return null;
		string path = Application.streamingAssetsPath + "/ConVarNames.txt";
		string[] fileLines;
		int curLine;
		if (File.Exists(path))
		{
			fileLines = File.ReadAllLines(path);
			curLine = 0;
			StringBuilder sb = new StringBuilder(256);
			string assemblyName = typeof(BaseConVar).Assembly.FullName;
			int currentPass = 0;
			while (!EndOfStream())
			{
				string text = ReadLine();
				if (!string.IsNullOrEmpty(text))
				{
					int num = int.Parse(ReadLine());
					sb.Clear();
					sb.AppendFormat("{0}, {1}", text, assemblyName);
					Type type = Type.GetType(sb.ToString());
					if (type != null)
					{
						for (int i = 0; i < num; i++)
						{
							string text2 = ReadLine();
							FieldInfo field = type.GetField(text2, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
							if (field != null)
							{
								BaseConVar conVar = (BaseConVar)field.GetValue(null);
								RegisterConVarInternal(conVar);
							}
							else
							{
								Debug.LogError("Failed to find convar " + text2);
							}
						}
					}
					else
					{
						Debug.LogErrorFormat("Failed to get type {0} (Full name: {1})", text, sb.ToString());
						for (int j = 0; j < num; j++)
						{
							ReadLine();
						}
					}
				}
				currentPass++;
				if (currentPass >= maxPassesBeforeYielding)
				{
					currentPass = 0;
					yield return null;
				}
			}
		}
		else
		{
			Debug.LogError("ERROR: Failed to load StreamingAssets/ConVarNames.txt");
		}
		bool EndOfStream()
		{
			return curLine >= fileLines.Length;
		}
		string ReadLine()
		{
			return fileLines[curLine++];
		}
	}

	private void InternalInitConVars()
	{
		string path = Application.streamingAssetsPath + "/ConVarNames.txt";
		string[] fileLines;
		int curLine;
		if (File.Exists(path))
		{
			fileLines = File.ReadAllLines(path);
			curLine = 0;
			StringBuilder stringBuilder = new StringBuilder(256);
			string fullName = typeof(BaseConVar).Assembly.FullName;
			while (!EndOfStream())
			{
				string text = ReadLine();
				if (string.IsNullOrEmpty(text))
				{
					continue;
				}
				int num = int.Parse(ReadLine());
				stringBuilder.Clear();
				stringBuilder.AppendFormat("{0}, {1}", text, fullName);
				Type type = Type.GetType(stringBuilder.ToString());
				if (type != null)
				{
					for (int i = 0; i < num; i++)
					{
						string text2 = ReadLine();
						FieldInfo field = type.GetField(text2, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
						if (field != null)
						{
							BaseConVar conVar = (BaseConVar)field.GetValue(null);
							RegisterConVarInternal(conVar);
						}
						else
						{
							Debug.LogError("Failed to find convar " + text2);
						}
					}
				}
				else
				{
					Debug.LogErrorFormat("Failed to get type {0} (Full name: {1})", text, stringBuilder.ToString());
					for (int j = 0; j < num; j++)
					{
						ReadLine();
					}
				}
			}
		}
		else
		{
			Debug.LogError("ERROR: Failed to load StreamingAssets/ConVarNames.txt");
		}
		bool EndOfStream()
		{
			return curLine >= fileLines.Length;
		}
		string ReadLine()
		{
			return fileLines[curLine++];
		}
	}

	public IEnumerator InitConVarsCoroutine()
	{
		allConVars = new Dictionary<string, BaseConVar>();
		archiveConVars = new List<BaseConVar>();
		yield return InternalInitConVarsCoroutine();
		int currentPass = 0;
		foreach (KeyValuePair<string, BaseConVar> allConVar in allConVars)
		{
			try
			{
				BaseConVar value = allConVar.Value;
				if ((value.flags & ConVarFlags.Engine) != 0)
				{
					value.defaultValue = value.GetString();
				}
				else if (value.defaultValue != null)
				{
					value.AttemptSetString(value.defaultValue);
				}
			}
			catch (Exception message)
			{
				Debug.LogError(message);
			}
			currentPass++;
			if (currentPass >= maxPassesBeforeYielding)
			{
				currentPass = 0;
				yield return null;
			}
		}
	}

	public void InitConVars()
	{
		allConVars = new Dictionary<string, BaseConVar>();
		archiveConVars = new List<BaseConVar>();
		InternalInitConVars();
		foreach (KeyValuePair<string, BaseConVar> allConVar in allConVars)
		{
			try
			{
				BaseConVar value = allConVar.Value;
				if ((value.flags & ConVarFlags.Engine) != 0)
				{
					value.defaultValue = value.GetString();
				}
				else if (value.defaultValue != null)
				{
					value.AttemptSetString(value.defaultValue);
				}
			}
			catch (Exception message)
			{
				Debug.LogError(message);
			}
		}
	}

	private void RegisterConVarInternal(BaseConVar conVar)
	{
		if (conVar == null)
		{
			Debug.LogWarning("Attempted to register null ConVar");
			return;
		}
		allConVars[conVar.name] = conVar;
		if ((conVar.flags & ConVarFlags.Archive) != 0)
		{
			archiveConVars.Add(conVar);
		}
	}

	public BaseConVar FindConVar(string name)
	{
		if (allConVars.TryGetValue(name, out var value))
		{
			return value;
		}
		return null;
	}

	public void SubmitCmd(NetworkUser sender, string cmd, bool recordSubmit = false)
	{
		SubmitCmd((CmdSender)sender, cmd, recordSubmit);
	}

	public void SubmitCmd(CmdSender sender, string cmd, bool recordSubmit = false)
	{
		if (recordSubmit)
		{
			Log log = default(Log);
			log.message = string.Format(CultureInfo.InvariantCulture, "<color=#C0C0C0>] {0}</color>", cmd);
			log.stackTrace = "";
			log.logType = LogType.Log;
			Log log2 = log;
			logs.Add(log2);
			if (Console.onLogReceived != null)
			{
				Console.onLogReceived(log2);
			}
			userCmdHistory.Add(cmd);
		}
		Queue<string> tokens = new Lexer(cmd).GetTokens();
		List<string> list = new List<string>();
		bool flag = false;
		while (tokens.Count != 0)
		{
			string text = tokens.Dequeue();
			if (text == ";")
			{
				flag = false;
				if (list.Count > 0)
				{
					string concommandName = list[0].ToLower(CultureInfo.InvariantCulture);
					list.RemoveAt(0);
					RunCmd(sender, concommandName, list);
					list.Clear();
				}
			}
			else
			{
				if (flag)
				{
					text = GetVstrValue(sender.localUser, text);
					flag = false;
				}
				if (text == "vstr")
				{
					flag = true;
				}
				else
				{
					list.Add(text);
				}
			}
		}
	}

	private void ForwardCmdToServer(ConCommandArgs args)
	{
		if ((bool)args.sender)
		{
			args.sender.CallCmdSendConsoleCommand(args.commandName, args.userArgs.ToArray());
		}
	}

	public void RunClientCmd(NetworkUser sender, string concommandName, string[] args)
	{
		RunCmd(sender, concommandName, new List<string>(args));
	}

	private void RunCmd(CmdSender sender, string concommandName, List<string> userArgs)
	{
		bool flag = !(sender.networkUser?.isLocalPlayer ?? true);
		ConVarFlags conVarFlags = ConVarFlags.None;
		ConCommand value = null;
		BaseConVar baseConVar = null;
		if (concommandCatalog.TryGetValue(concommandName, out value))
		{
			conVarFlags = value.flags;
		}
		else
		{
			baseConVar = FindConVar(concommandName);
			if (baseConVar == null)
			{
				Debug.LogFormat("\"{0}\" is not a recognized ConCommand or ConVar.", concommandName);
				return;
			}
			conVarFlags = baseConVar.flags;
		}
		bool flag2 = (conVarFlags & ConVarFlags.ExecuteOnServer) != 0;
		if (!NetworkServer.active && flag2)
		{
			ForwardCmdToServer(new ConCommandArgs
			{
				sender = sender.networkUser,
				commandName = concommandName,
				userArgs = userArgs
			});
			return;
		}
		if (flag && (conVarFlags & ConVarFlags.SenderMustBeServer) != 0)
		{
			Debug.LogFormat("Blocked server-only command {0} from remote user {1}.", concommandName, sender.networkUser.userName);
			return;
		}
		if (flag && !flag2)
		{
			Debug.LogFormat("Blocked non-transmittable command {0} from remote user {1}.", concommandName, sender.networkUser.userName);
			return;
		}
		if ((conVarFlags & ConVarFlags.Cheat) != 0 && !CheatsConVar.instance.boolValue)
		{
			Debug.LogFormat("Command \"{0}\" cannot be used while cheats are disabled.", concommandName);
			return;
		}
		if (value != null)
		{
			try
			{
				value.action(new ConCommandArgs
				{
					sender = sender.networkUser,
					localUserSender = sender.localUser,
					commandName = concommandName,
					userArgs = userArgs
				});
				return;
			}
			catch (ConCommandException ex)
			{
				Debug.LogFormat("Command \"{0}\" failed: {1}", concommandName, ex.Message);
				return;
			}
		}
		if (baseConVar != null)
		{
			if (userArgs.Count > 0)
			{
				baseConVar.AttemptSetString(userArgs[0]);
				return;
			}
			Debug.LogFormat("\"{0}\" = \"{1}\"\n{2}", concommandName, baseConVar.GetString(), baseConVar.helpText);
		}
	}

	[DllImport("kernel32.dll")]
	private static extern bool AllocConsole();

	[DllImport("kernel32.dll")]
	private static extern bool FreeConsole();

	[DllImport("kernel32.dll")]
	private static extern bool AttachConsole(int processId);

	[DllImport("user32.dll")]
	private static extern bool PostMessage(IntPtr hWnd, uint msg, int wParam, int lParam);

	[DllImport("kernel32.dll")]
	private static extern IntPtr GetConsoleWindow();

	private static string ReadInputStream()
	{
		if (stdInQueue.Count > 0)
		{
			return stdInQueue.Dequeue();
		}
		return null;
	}

	private static void ThreadedInputQueue()
	{
		string item;
		while (systemConsoleType != 0 && (item = System.Console.ReadLine()) != null)
		{
			stdInQueue.Enqueue(item);
		}
	}

	private static void SetupSystemConsole()
	{
		bool flag = false;
		bool flag2 = false;
		string[] commandLineArgs = Environment.GetCommandLineArgs();
		for (int i = 0; i < commandLineArgs.Length; i++)
		{
			if (commandLineArgs[i] == "-console")
			{
				flag = flag || true;
			}
			if (commandLineArgs[i] == "-console_detach")
			{
				flag2 = flag2 || true;
			}
		}
		if (flag)
		{
			systemConsoleType = SystemConsoleType.Attach;
			if (flag2)
			{
				systemConsoleType = SystemConsoleType.Alloc;
			}
		}
		switch (systemConsoleType)
		{
		case SystemConsoleType.Attach:
			AttachConsole(-1);
			break;
		case SystemConsoleType.Alloc:
			AllocConsole();
			break;
		}
		if (systemConsoleType != 0)
		{
			System.Console.SetIn(new StreamReader(System.Console.OpenStandardInput()));
			stdInReaderThread = new Thread(ThreadedInputQueue);
			stdInReaderThread.Start();
		}
	}

	private void Awake()
	{
		instance = this;
		loadingIsComplete = false;
		StartCoroutine(AwakeCoroutine());
	}

	private IEnumerator AwakeCoroutine()
	{
		SetupSystemConsole();
		yield return InitConVarsCoroutine();
		List<HG.Reflection.SearchableAttribute> instances = HG.Reflection.SearchableAttribute.GetInstances<ConCommandAttribute>();
		int currentPass = 0;
		foreach (ConCommandAttribute item in instances)
		{
			concommandCatalog[item.commandName.ToLower(CultureInfo.InvariantCulture)] = new ConCommand
			{
				flags = item.flags,
				action = (ConCommandDelegate)Delegate.CreateDelegate(typeof(ConCommandDelegate), item.target as MethodInfo),
				helpText = item.helpText
			};
			currentPass++;
			if (currentPass >= maxPassesBeforeYielding)
			{
				currentPass = 0;
				yield return null;
			}
		}
		string[] commandLineArgs = Environment.GetCommandLineArgs();
		StringBuilder stringBuilder = HG.StringBuilderPool.RentStringBuilder();
		stringBuilder.AppendLine("Launch Parameters: ");
		for (int i = 0; i < commandLineArgs.Length; i++)
		{
			stringBuilder.Append("  arg[").AppendInt(i).Append("]=\"")
				.Append(commandLineArgs[i])
				.Append("\"")
				.AppendLine();
		}
		HG.StringBuilderPool.ReturnStringBuilder(stringBuilder);
		MPEventSystemManager.availability.CallWhenAvailable(LoadStartupConfigs);
		loadingIsComplete = true;
	}

	private void LoadStartupConfigs()
	{
		try
		{
			SubmitCmd(null, "exec config");
			SubmitCmd(null, "exec autoexec");
		}
		catch (Exception message)
		{
			Debug.LogError(message);
		}
	}

	private void Update()
	{
		string cmd;
		while ((cmd = ReadInputStream()) != null)
		{
			SubmitCmd(null, cmd, recordSubmit: true);
		}
	}

	private void OnDestroy()
	{
		if (stdInReaderThread != null)
		{
			stdInReaderThread = null;
		}
		if (systemConsoleType != 0)
		{
			systemConsoleType = SystemConsoleType.None;
			IntPtr consoleWindow = GetConsoleWindow();
			if (consoleWindow != IntPtr.Zero)
			{
				PostMessage(consoleWindow, 256u, 13, 0);
			}
			if (stdInReaderThread != null)
			{
				stdInReaderThread.Join();
				stdInReaderThread = null;
			}
			FreeConsole();
		}
	}

	private static string LoadConfig(string fileName)
	{
		string text = sharedStringBuilder.Clear().Append("/Config/").Append(fileName)
			.Append(".cfg")
			.ToString();
		try
		{
			return PlatformSystems.textDataManager.GetConfFile(fileName, text);
		}
		catch (IOException ex)
		{
			Debug.LogFormat("Could not load config {0}: {1}", text, ex.Message);
		}
		return null;
	}

	public void SaveArchiveConVars()
	{
		using MemoryStream memoryStream = new MemoryStream();
		using TextWriter textWriter = new StreamWriter(memoryStream, Encoding.UTF8);
		for (int i = 0; i < archiveConVars.Count; i++)
		{
			BaseConVar baseConVar = archiveConVars[i];
			textWriter.Write(baseConVar.name);
			textWriter.Write(" ");
			textWriter.Write(baseConVar.GetString());
			textWriter.Write(";\r\n");
		}
		textWriter.Write("echo \"Loaded archived convars.\";");
		textWriter.Flush();
		RoR2Application.fileSystem.CreateDirectory("/Config/");
		try
		{
			using Stream stream = RoR2Application.fileSystem.OpenFile("/Config/config.cfg", FileMode.Create, FileAccess.Write);
			if (stream != null)
			{
				stream.Write(memoryStream.GetBuffer(), 0, (int)memoryStream.Length);
				stream.Close();
			}
		}
		catch (IOException ex)
		{
			Debug.LogFormat("Failed to write archived convars: {0}", ex.Message);
		}
	}

	[ConCommand(commandName = "set_vstr", flags = ConVarFlags.None, helpText = "Sets the specified vstr to the specified value.")]
	private static void CCSetVstr(ConCommandArgs args)
	{
		args.CheckArgumentCount(2);
		instance.vstrs.Add(args[0], args[1]);
	}

	[ConCommand(commandName = "exec", flags = ConVarFlags.None, helpText = "Executes a named config from the \"Config/\" folder.")]
	private static void CCExec(ConCommandArgs args)
	{
		if (args.Count > 0)
		{
			string text = LoadConfig(args[0]);
			if (text != null)
			{
				instance.SubmitCmd(args.sender, text);
			}
		}
	}

	[ConCommand(commandName = "echo", flags = ConVarFlags.None, helpText = "Echoes the given text to the console.")]
	private static void CCEcho(ConCommandArgs args)
	{
		if (args.Count <= 0)
		{
			ShowHelpText(args.commandName);
		}
	}

	[ConCommand(commandName = "cvarlist", flags = ConVarFlags.None, helpText = "Print all available convars and concommands.")]
	private static void CCCvarList(ConCommandArgs args)
	{
		List<string> list = new List<string>();
		foreach (KeyValuePair<string, BaseConVar> allConVar in instance.allConVars)
		{
			list.Add(allConVar.Key);
		}
		foreach (KeyValuePair<string, ConCommand> item in instance.concommandCatalog)
		{
			list.Add(item.Key);
		}
		list.Sort();
	}

	[ConCommand(commandName = "help", flags = ConVarFlags.None, helpText = "Show help text for the named convar or concommand.")]
	private static void CCHelp(ConCommandArgs args)
	{
		if (args.Count == 0)
		{
			instance.SubmitCmd(args.sender, "find \"*\"");
		}
		else
		{
			ShowHelpText(args[0]);
		}
	}

	[ConCommand(commandName = "find", flags = ConVarFlags.None, helpText = "Find all concommands and convars with the specified substring.")]
	private static void CCFind(ConCommandArgs args)
	{
		if (args.Count == 0)
		{
			ShowHelpText("find");
		}
		else
		{
			Find(args[0].ToLower(CultureInfo.InvariantCulture));
		}
	}

	private static string Find(string searchString)
	{
		bool flag = searchString == "*";
		List<string> list = new List<string>();
		foreach (KeyValuePair<string, BaseConVar> allConVar in instance.allConVars)
		{
			if (flag || allConVar.Key.ToLower(CultureInfo.InvariantCulture).Contains(searchString) || allConVar.Value.helpText.ToLower(CultureInfo.InvariantCulture).Contains(searchString))
			{
				list.Add(allConVar.Key);
			}
		}
		foreach (KeyValuePair<string, ConCommand> item in instance.concommandCatalog)
		{
			if (flag || item.Key.ToLower(CultureInfo.InvariantCulture).Contains(searchString) || item.Value.helpText.ToLower(CultureInfo.InvariantCulture).Contains(searchString))
			{
				list.Add(item.Key);
			}
		}
		list.Sort();
		string[] array = new string[list.Count];
		for (int i = 0; i < list.Count; i++)
		{
			array[i] = GetHelpText(list[i]);
		}
		return string.Join("\n", array);
	}

	[ConCommand(commandName = "clear", flags = ConVarFlags.None, helpText = "Clears the console output.")]
	private static void CCClear(ConCommandArgs args)
	{
		logs.Clear();
		Console.onClear?.Invoke();
	}

	private static string GetHelpText(string commandName)
	{
		if (instance.concommandCatalog.TryGetValue(commandName, out var value))
		{
			return string.Format(CultureInfo.InvariantCulture, "<color=#FF7F7F>\"{0}\"</color>\n- {1}", commandName, value.helpText);
		}
		BaseConVar baseConVar = instance.FindConVar(commandName);
		if (baseConVar != null)
		{
			return string.Format(CultureInfo.InvariantCulture, "<color=#FF7F7F>\"{0}\" = \"{1}\"</color>\n - {2}", commandName, baseConVar.GetString(), baseConVar.helpText);
		}
		return Find(commandName) + "\nExact Match not found. Use `find \"" + commandName + "\" instead";
	}

	public static void ShowHelpText(string commandName)
	{
	}
}
