using System;
using System.Runtime.InteropServices;
using System.Windows;

namespace Timelapser
{
    class GlobalInput : IDisposable
    {
        delegate int HookProc(int nCode, IntPtr wParam, IntPtr lParam);
        static int mHook = 0;
        static int kHook = 0;

        public enum Hooks
        {
            WH_KEYBOARD_LL = 13,
            WH_MOUSE_LL = 14
        }

        HookProc MouseHook;
        HookProc KeyboardHook;

        [StructLayout(LayoutKind.Sequential)]
        class POINT
        {
            public int x;
            public int y;
        }

        [StructLayout(LayoutKind.Sequential)]
        class MouseHookStruct
        {
            public POINT pt;
            public int hwnd;
            public int wHitTestCode;
            public int dwExtraInfo;
        }

        [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
        static extern int SetWindowsHookEx(int idHook, HookProc lpfn, IntPtr hInstance, int threadId);
        [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
        static extern bool UnhookWindowsHookEx(int idHook);
        [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
        static extern int CallNextHookEx(int idHook, int nCode, IntPtr wParam, IntPtr lParam);

        public GlobalInput()
        {
            if (mHook == 0)
            {
                MouseHook = new HookProc(_mouseHook);
                mHook = SetWindowsHookEx((int)Hooks.WH_MOUSE_LL, MouseHook, (IntPtr)0, 0);
                if (mHook == 0)
                {
                    MessageBox.Show("That failed.");
                    return;
                }
            }
            else
                throw new ApplicationException("Already hooking.");
            if (kHook == 0)
            {
                KeyboardHook = new HookProc(_keyboardHook);
                kHook = SetWindowsHookEx((int)Hooks.WH_KEYBOARD_LL, KeyboardHook, (IntPtr)0, 0);
                if (kHook == 0)
                {
                    MessageBox.Show("That failed.");
                    return;
                }
            }
            else
                throw new ApplicationException("Already hooking.");
        }

        public void Dispose()
        {
            if (mHook != 0)
                if (UnhookWindowsHookEx(mHook))
                    mHook = 0;

            if (kHook != 0)
                if (UnhookWindowsHookEx(kHook))
                    kHook = 0;
        }

        int _mouseHook(int nCode, IntPtr wParam, IntPtr lParam)
        {
            if (nCode >= 0)
            {
                MouseHookStruct data = (MouseHookStruct)Marshal.PtrToStructure(lParam, typeof(MouseHookStruct));
                
                MousePoint = new Point(data.pt.x, data.pt.y);
                MouseTime = DateTime.Now;
            }

            return CallNextHookEx(mHook, nCode, wParam, lParam);
        }
        int _keyboardHook(int nCode, IntPtr wParam, IntPtr lParam)
        {
            if (nCode >= 0)
            {
                KeyboardTime = DateTime.Now;
            }

            return CallNextHookEx(mHook, nCode, wParam, lParam);
        }

        public DateTime MouseTime { get; private set; }
        public DateTime KeyboardTime { get; private set; }
        public Point MousePoint { get; private set; }
    }
}
