using System;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
using Microsoft.Win32;
namespace DM_Weight.util.TabTip
{
    public static class TabTip
    {
        private const string TabTipWindowClassName = "IPTip_Main_Window";
        private const string TabTipExecPath = @"C:\Program Files\Common Files\microsoft shared\ink\TabTip.exe";
        private const string TabTipRegistryKeyName = @"HKEY_CURRENT_USER\Software\Microsoft\TabletTip\1.7";
        [DllImport("user32.dll")]
        private static extern int SendMessage(int hWnd, uint msg, int wParam, int lParam);
        [DllImport("user32.dll")]
        private static extern IntPtr FindWindow(String sClassName, String sAppName);
        [DllImport("user32.dll")]
        [return: MarshalAs(UnmanagedType.Bool)]
        private static extern bool GetWindowRect(HandleRef hWnd, out RECT lpRect);
        [StructLayout(LayoutKind.Sequential)]
        private struct RECT
        {
            public readonly int Left;        // x position of upper-left corner
            public readonly int Top;         // y position of upper-left corner
            public readonly int Right;       // x position of lower-right corner
            public readonly int Bottom;      // y position of lower-right corner
        }
        [DllImport("user32.dll", SetLastError = true)]
        private static extern UInt32 GetWindowLong(IntPtr hWnd, int nIndex);
        /// 
        /// Signals that TabTip was closed after it was opened 
        /// with a call to StartPoolingForTabTipClosedEvent method
        /// 
        internal static event Action Closed;
        private static IntPtr GetTabTipWindowHandle() => FindWindow(TabTipWindowClassName, null);
        
        internal static void OpenUndockedAndStartPoolingForClosedEvent()
        {
            OpenUndocked();
            StartPoolingForTabTipClosedEvent();
        }
        /// 
        /// Open TabTip
        /// 
        public static void Open()
        {
            if (EnvironmentEx.GetOSVersion() == OSVersion.Win10)
                EnableTabTipOpenInDesctopModeOnWin10();
            Process.Start(new ProcessStartInfo(TabTipExecPath) { UseShellExecute = true});
        }
        private static void EnableTabTipOpenInDesctopModeOnWin10()
        {
            const string TabTipAutoInvokeKey = "EnableDesktopModeAutoInvoke";
            int EnableDesktopModeAutoInvoke = (int) (Registry.GetValue(TabTipRegistryKeyName, TabTipAutoInvokeKey, -1) ?? -1);
            if (EnableDesktopModeAutoInvoke != 1)
                Registry.SetValue(TabTipRegistryKeyName, TabTipAutoInvokeKey, 1);
        }
        /// 
        /// Open TabTip in undocked state
        /// 
        public static void OpenUndocked()
        {
            const string TabTipDockedKey = "EdgeTargetDockedState";
            const string TabTipProcessName = "TabTip";
            int docked = (int) (Registry.GetValue(TabTipRegistryKeyName, TabTipDockedKey, 1) ?? 1);
            if (docked == 1)
            {
                Registry.SetValue(TabTipRegistryKeyName, TabTipDockedKey, 0);
                foreach (Process tabTipProcess in Process.GetProcessesByName(TabTipProcessName))
                    tabTipProcess.Kill();
            }
            Open();
        }
        /// 
        /// Close TabTip
        /// 
        public static void Close()
        {
            const int WM_SYSCOMMAND = 274;
            const int SC_CLOSE = 61536;
            SendMessage(GetTabTipWindowHandle().ToInt32(), WM_SYSCOMMAND, SC_CLOSE, 0);
        }
        private static void StartPoolingForTabTipClosedEvent()
        {
            PoolingTimer.PoolUntilTrue(
                PoolingFunc: TabTipClosed,
                Callback: () => Closed?.Invoke(),
                dueTime: TimeSpan.FromMilliseconds(700),
                period: TimeSpan.FromMilliseconds(50));
        }
        private static bool TabTipClosed()
        {
            const int GWL_STYLE = -16; // Specifies we wish to retrieve window styles.
            const uint KeyboardClosedStyle = 2617245696;
            IntPtr KeyboardWnd = GetTabTipWindowHandle();
            return (KeyboardWnd.ToInt32() == 0 || GetWindowLong(KeyboardWnd, GWL_STYLE) == KeyboardClosedStyle);
        }
        // ReSharper disable once UnusedMember.Local
        private static bool IsTabTipProcessRunning => GetTabTipWindowHandle() != IntPtr.Zero;
        /// 
        /// Gets TabTip Window Rectangle
        /// 
        /// 
        [SuppressMessage("ReSharper", "ConvertIfStatementToReturnStatement")]
        public static Rectangle GetTabTipRectangle()
        {
            if (TabTipClosed())
                return new Rectangle();
            return GetWouldBeTabTipRectangle();
        }
        private static Rectangle previousTabTipRectangle;
        /// 
        /// Gets Window Rectangle which would be occupied by TabTip if TabTip was opened.
        /// 
        /// 
        [SuppressMessage("ReSharper", "ConvertIfStatementToReturnStatement")]
        internal static Rectangle GetWouldBeTabTipRectangle()
        {
            RECT rect;
            if (!GetWindowRect(new HandleRef(null, GetTabTipWindowHandle()), out rect))
            {
                if (previousTabTipRectangle.Equals(new Rectangle())) //in case TabTip was closed and previousTabTipRectangle was not set
                    Task.Delay(TimeSpan.FromSeconds(1)).ContinueWith(TryGetTabTipRectangleToСache);
                return previousTabTipRectangle;
            }
            Rectangle wouldBeTabTipRectangle = new Rectangle(x: rect.Left, y: rect.Top, width: rect.Right - rect.Left + 1, height: rect.Bottom - rect.Top + 1);
            previousTabTipRectangle = wouldBeTabTipRectangle;
            return wouldBeTabTipRectangle;
        }
        private static void TryGetTabTipRectangleToСache(Task task)
        {
            RECT rect;
            if (GetWindowRect(new HandleRef(null, GetTabTipWindowHandle()), out rect))
                previousTabTipRectangle = new Rectangle(x: rect.Left, y: rect.Top, width: rect.Right - rect.Left + 1, height: rect.Bottom - rect.Top + 1);
        }
    }
}