using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Windows.Forms;
namespace MediaBrowser.UI.UserInput
{
    /// 
    /// Provides a basic low-level keyboard listener
    /// Inspired by http://blogs.msdn.com/b/toub/archive/2006/05/03/589423.aspx
    /// Use the KeyDown event to listen for keys.
    /// Make sure to detach from the event when not needed.
    /// 
    public static class KeyboardListener
    {
        #region KeyDown EventHandler
        /// 
        /// The _ key down
        /// 
        static volatile EventHandler _KeyDown;
        /// 
        /// Fires whenever CurrentItem changes
        /// 
        public static event EventHandler KeyDown
        {
            add
            {
                if (_KeyDown == null)
                {
                    StartListening();
                }
                _KeyDown += value;
            }
            remove
            {
                _KeyDown -= value;
                if (_KeyDown == null && _hookID != IntPtr.Zero)
                {
                    StopListening();
                }
            }
        }
        /// 
        /// Raises the  event.
        /// 
        /// The  instance containing the event data.
        private static void OnKeyDown(KeyEventArgs e)
        {
            e.SuppressKeyPress = false;
            if (_KeyDown != null)
            {
                // For now, don't async this
                // This will give listeners a chance to modify SuppressKeyPress if they want
                try
                {
                    _KeyDown(null, e);
                }
                catch (Exception ex)
                {
                }
            }
        }
        #endregion
        /// 
        /// The W h_ KEYBOAR d_ LL
        /// 
        private const int WH_KEYBOARD_LL = 13;
        /// 
        /// The W m_ KEYDOWN
        /// 
        private const int WM_KEYDOWN = 0x0100;
        /// 
        /// The W m_ SYSKEYDOWN
        /// 
        private const int WM_SYSKEYDOWN = 0x0104;
        /// 
        /// The _hook ID
        /// 
        private static IntPtr _hookID = IntPtr.Zero;
        /// 
        /// The _proc
        /// 
        private static LowLevelKeyboardProc _proc = HookCallback;
        /// 
        /// Starts the listening.
        /// 
        private static void StartListening()
        {
            _hookID = SetHook(_proc);
        }
        /// 
        /// Stops the listening.
        /// 
        private static void StopListening()
        {
            UnhookWindowsHookEx(_hookID);
            _hookID = IntPtr.Zero;
        }
        /// 
        /// Sets the hook.
        /// 
        /// The proc.
        /// IntPtr.
        private 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);
            }
        }
        /// 
        /// Hooks the callback.
        /// 
        /// The n code.
        /// The w param.
        /// The l param.
        /// IntPtr.
        private static IntPtr HookCallback(int nCode, IntPtr wParam, IntPtr lParam)
        {
            var suppressKeyPress = false;
            if (nCode >= 0)
            {
                if (wParam == (IntPtr)WM_KEYDOWN || wParam == (IntPtr)WM_SYSKEYDOWN)
                {
                    var vkCode = Marshal.ReadInt32(lParam);
                    var keyData = (Keys)vkCode;
                    var e = new KeyEventArgs(keyData);
                    OnKeyDown(e);
                    suppressKeyPress = e.SuppressKeyPress;
                }
            }
            if (suppressKeyPress)
            {
                return IntPtr.Zero;
            }
            return CallNextHookEx(_hookID, nCode, wParam, lParam);
        }
        /// 
        /// Delegate LowLevelKeyboardProc
        /// 
        /// The n code.
        /// The w param.
        /// The l param.
        /// IntPtr.
        private delegate IntPtr LowLevelKeyboardProc(int nCode, IntPtr wParam, IntPtr lParam);
        #region Imports
        /// 
        /// Sets the windows hook ex.
        /// 
        /// The id hook.
        /// The LPFN.
        /// The h mod.
        /// The dw thread id.
        /// IntPtr.
        [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        private static extern IntPtr SetWindowsHookEx(int idHook,
            LowLevelKeyboardProc lpfn, IntPtr hMod, uint dwThreadId);
        /// 
        /// Unhooks the windows hook ex.
        /// 
        /// The HHK.
        /// true if XXXX, false otherwise
        [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        private static extern bool UnhookWindowsHookEx(IntPtr hhk);
        /// 
        /// Calls the next hook ex.
        /// 
        /// The HHK.
        /// The n code.
        /// The w param.
        /// The l param.
        /// IntPtr.
        [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        private static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode,
            IntPtr wParam, IntPtr lParam);
        /// 
        /// Gets the module handle.
        /// 
        /// Name of the lp module.
        /// IntPtr.
        [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        private static extern IntPtr GetModuleHandle(string lpModuleName);
        #endregion
    }
}