using System;
using System.Diagnostics;
using System.Runtime.InteropServices;

namespace ETDiscordPresence
{
	internal class GameReader : IDisposable
	{
		private const int ADDRESS = 0x014D3650;
		private const int ONLINE = 0x013E0B80;
		private const int CSTRING = 0x013EA2B1;
		
		[DllImport("kernel32.dll")]
		private static extern IntPtr OpenProcess(int dwDesiredAccess, bool bInheritHandle, int dwProcessId);
		
		[DllImport("kernel32.dll")]
		private static extern bool CloseHandle(int hObject);

		[DllImport("kernel32.dll")]
		private static extern bool ReadProcessMemory(int hProcess, int lpBaseAddress, byte[] lpBuffer, int dwSize, ref int lpNumberOfBytesRead);

		private readonly Process process;

		private readonly IntPtr handle;

		public GameReader(Process process)
		{
			this.process = process;
			handle = OpenProcess(0x0010, false, process.Id);
		}

		public GameWatcher.ServerInfo? Read()
		{
			var address = new byte[128];
			var addressRead = -1;
			
			var online = new byte[1];
			var onlineRead = -1;
			
			var cstring = new byte[1024];
			var cstringRead = -1;

			if (!ReadProcessMemory((int) handle, ONLINE, online, online.Length, ref onlineRead) ||
			    onlineRead != online.Length ||
			    online[0] != 1)
			{
				return null;
			}

			if (!ReadProcessMemory((int) handle, ADDRESS, address, address.Length, ref addressRead) ||
			    addressRead != address.Length ||
			    !ReadProcessMemory((int) handle, CSTRING, cstring, cstring.Length, ref cstringRead) ||
			    cstringRead != cstring.Length)
			{
				return null;
			}

			try
			{
				var addressValue = System.Text.Encoding.Default.GetString(address).TrimEnd((char) 0);
				var cstringValues = System.Text.Encoding.Default.GetString(cstring).TrimEnd((char) 0).Split('\\');
				string hostname = null;

				for (var i = 1; i + 1 < cstringValues.Length; i += 2)
				{
					if (cstringValues[i].ToLower() != "sv_hostname")
					{
						continue;
					}

					hostname = cstringValues[i + 1];
					break;
				}

				if (hostname == null || addressValue.Length == 0)
				{
					return null;
				}

				var hostnameClean = "";

				for (var i = 0; i < hostname.Length; i++)
				{
					if (hostname[i] == '^' && i + 1 < hostname.Length)
					{
						i++;
						continue;
					}

					hostnameClean += hostname[i];
				}

				if (hostnameClean.Length == 0)
				{
					return null;
				}
				
				return new GameWatcher.ServerInfo
				{
					Address = addressValue,
					Hostname = hostnameClean,
				};
			}
			catch
			{
				return null;
			}
		}

		public void Dispose()
		{
			CloseHandle((int) handle);
			process?.Dispose();
		}
	}
}