// ReSharper disable ClassNeverInstantiated.Global

#if NET8_0_WINDOWS
using System.Diagnostics;
using System.Runtime.InteropServices;

namespace Dot.Native;

internal sealed class KeyboardHook : IDisposable
{
    private readonly nint _hookId;
    private          bool _disposed;

    public KeyboardHook()
    {
        _hookId = SetHook(HookCallback);
    }

    ~KeyboardHook()
    {
        Dispose(false);
    }

    public delegate bool KeyUpEventHandler(object sender, Win32.KbdllhooksStruct e);

    public KeyUpEventHandler KeyUpEvent { get; set; }

    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

    private static nint SetHook(Win32.HookProc lpfn)
    {
        using var process = Process.GetCurrentProcess();
        using var module = process.MainModule;
        return Win32.SetWindowsHookExA(Win32.WH_KEYBOARD_LL, lpfn, module!.BaseAddress, 0);
    }

    private void Dispose(bool disposing)
    {
        if (_disposed) {
            return;
        }

        if (disposing) {
            //
        }

        if (_hookId != default) {
            _ = Win32.UnhookWindowsHookExA(_hookId);
        }

        _disposed = true;
    }

    private nint HookCallback(int nCode, nint wParam, nint lParam)
    {
        // ReSharper disable once InvertIf
        if (nCode >= 0 && wParam == Win32.WM_KEYDOWN) {
            var hookStruct = (Win32.KbdllhooksStruct)Marshal.PtrToStructure(lParam, typeof(Win32.KbdllhooksStruct))!;
            if (KeyUpEvent?.Invoke(null, hookStruct) ?? false) {
                return -1;
            }
        }

        return Win32.CallNextHookEx(_hookId, nCode, wParam, lParam);
    }
}

#endif