using System.Diagnostics;
using System.Runtime.InteropServices;
using Microsoft.Win32;

static class Program
{
    const int WH_KEYBOARD_LL = 13;
    const int VK_CAPITAL = 0x14;

    delegate IntPtr LowLevelKeyboardProc(int nCode, IntPtr wParam, IntPtr lParam);

    [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    static extern IntPtr SetWindowsHookEx(int idHook, LowLevelKeyboardProc lpfn, IntPtr hMod, uint dwThreadId);

    [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    [return: MarshalAs(UnmanagedType.Bool)]
    static extern bool UnhookWindowsHookEx(IntPtr hhk);

    [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode, IntPtr wParam, IntPtr lParam);

    [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    static extern IntPtr GetModuleHandle(string lpModuleName);

    [StructLayout(LayoutKind.Sequential)]
    struct KBDLLHOOKSTRUCT
    {
        public uint vkCode;
        public uint scanCode;
        public uint flags;
        public uint time;
        public IntPtr dwExtraInfo;
    }

    static LowLevelKeyboardProc _proc = HookCallback;
    static IntPtr _hookId = IntPtr.Zero;
    static NotifyIcon? _trayIcon;

    [STAThread]
    static void Main()
    {
        RegisterStartup();

        using var mutex = new Mutex(true, @"Global\FuckCapsLock", out var createdNew);
        if (!createdNew)
            return;

        _hookId = SetHook(_proc);
        if (_hookId == IntPtr.Zero)
            return;

        ApplicationConfiguration.Initialize();

        using var form = new Form();
        form.WindowState = FormWindowState.Minimized;
        form.ShowInTaskbar = false;
        form.Load += (_, _) => form.Hide();

        _trayIcon = new NotifyIcon
        {
            Icon = SystemIcons.Application,
            Text = "FuckCapsLock",
            ContextMenuStrip = new ContextMenuStrip()
        };
        _trayIcon.ContextMenuStrip.Items.Add("Exit", null, (_, _) =>
        {
            _trayIcon.Visible = false;
            Application.Exit();
        });
        _trayIcon.Visible = true;

        Application.Run(form);

        UnhookWindowsHookEx(_hookId);
    }

    static void RegisterStartup()
    {
        try
        {
            using var key = Registry.CurrentUser.OpenSubKey(
                @"Software\Microsoft\Windows\CurrentVersion\Run", true);
            if (key is null) return;

            var path = Environment.ProcessPath;
            if (path is null) return;

            var value = path.Contains(' ') ? $"\"{path}\"" : path;

            var existing = key.GetValue("FuckCapsLock") as string;
            if (!string.Equals(existing, value, StringComparison.OrdinalIgnoreCase))
                key.SetValue("FuckCapsLock", value);
        }
        catch { }
    }

    static IntPtr SetHook(LowLevelKeyboardProc proc)
    {
        using var curProcess = Process.GetCurrentProcess();
        using var curModule = curProcess.MainModule;
        return SetWindowsHookEx(WH_KEYBOARD_LL, proc,
            GetModuleHandle(curModule!.ModuleName), 0);
    }

    static IntPtr HookCallback(int nCode, IntPtr wParam, IntPtr lParam)
    {
        if (nCode >= 0)
        {
            var hookStruct = Marshal.PtrToStructure<KBDLLHOOKSTRUCT>(lParam);
            if (hookStruct.vkCode == VK_CAPITAL)
                return (IntPtr)1;
        }
        return CallNextHookEx(_hookId, nCode, wParam, lParam);
    }
}
