using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.InteropServices;
using static CPF.Windows.UnmanagedMethods;
using CPF;
using System.Threading;
using System.IO;
using CPF.Threading;
using System.Collections.Concurrent;
using CPF.Drawing;
using CPF.Input;
using System.Linq;
using CPF.Controls;
using CPF.Platform;
using CPF.Shapes;
using CPF.Animation;
using System.Diagnostics;
using System.Text;
using CPF.Design;
using CPF.Windows.OpenGL;
using CPF.OpenGL;
//using System.Runtime.InteropServices.ComTypes;

namespace CPF.Windows
{
    public class WindowImpl : IWindowImpl
    {
        internal static int threadCallbackMessage;
        public ConcurrentQueue<SendOrPostData> invokeQueue = new ConcurrentQueue<SendOrPostData>();
        public ConcurrentQueue<SendOrPostData> asyncQueue = new ConcurrentQueue<SendOrPostData>();
        float scaling = 1;
        internal static WindowImpl Window;
        const double wheelDelta = 120.0;
        private bool _trackingMouse;
        DispatcherTimer timer;
        View root;
        public View Root
        {
            get { return root; }
        }
        WindowState windowState = WindowState.Normal;
        WindowState oldwindowState = WindowState.Normal;
        //bool isFirst = true;
        private string _className;

        static WindowImpl()
        {
            threadCallbackMessage = RegisterWindowMessage("CPF_ThreadCallbackMessage");
            if (WindowsPlatform.registerForMarshalling)
            {
#if NET5_0_OR_GREATER
                ComWrappers.RegisterForMarshalling(ComWrapper.CpfComWrappers.Instance);
#endif
            }
            HRESULT res = UnmanagedMethods.OleInitialize(IntPtr.Zero);
            if (res != HRESULT.S_OK &&
                res != HRESULT.S_FALSE /*already initialized*/)
                throw new Win32Exception((int)res, "Failed to initialize OLE：必须设置线程STA，Main方法上加[STAThread]");

        }
        static IntPtr moduleHandle;
        public static IntPtr ModuleHandle
        {
            get
            {
                if (moduleHandle == IntPtr.Zero)
                {
                    moduleHandle = GetModuleHandle(null);
                }
                return moduleHandle;
            }
        }

        WNDCLASSEX wc = new WNDCLASSEX();
        bool touchMsg;
        public WindowImpl()
        {
            if (Window == null)
            {
                Window = this;
            }
            posttime = Application.Elapsed;
            var v = System.Environment.OSVersion.Version;
            if (v.Major >= 6 && DwmIsCompositionEnabled() && !Application.DesignMode)
            {
            }
            else
            {
                isLayered = true;
            }
            if ((v.Major == 6 && v.Minor < 2))
            {
               touchMsg =  RegisterTouchWindow(handle, RegisterTouchFlags.TWF_NONE);
            }
            _className = "CPFWindow-" + Guid.NewGuid();
            // 初始化窗口类结构  
            wc.cbSize = Marshal.SizeOf(typeof(WNDCLASSEX));
            //wc.style = (int)ClassStyles.CS_DBLCLKS;
            wc.lpfnWndProc = WndProc;
            wc.hInstance = ModuleHandle;
            wc.hbrBackground = IntPtr.Zero;
            wc.lpszClassName = _className;
            wc.cbClsExtra = 0;
            wc.cbWndExtra = 0;
            wc.hIcon = IntPtr.Zero;
            wc.hCursor = DefaultCursor;
            wc.lpszMenuName = "";
            // 注册窗口类  
            RegisterClassEx(ref wc);
            // 创建并显示窗口  
            handle = CreateWindowEx((int)ExStyle, _className, "窗体标题", (uint)Style,
              CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, IntPtr.Zero, IntPtr.Zero, ModuleHandle, null);

            //timer = new DispatcherTimer { Interval = TimeSpan.FromMilliseconds(1), IsEnabled = true };
            //timer.Tick += Timer_Tick;

            if (UnmanagedMethods.ShCoreAvailable)
            {
                uint dpix, dpiy;

                var monitor = UnmanagedMethods.MonitorFromWindow(
                    handle,
                    UnmanagedMethods.MONITOR.MONITOR_DEFAULTTONEAREST);

                if (UnmanagedMethods.GetDpiForMonitor != null)
                {
                    if (UnmanagedMethods.GetDpiForMonitor(
                            monitor,
                            UnmanagedMethods.MONITOR_DPI_TYPE.MDT_EFFECTIVE_DPI,
                            out dpix,
                            out dpiy) == 0)
                    {
                        scaling = (float)(dpix / 96.0);
                    }
                }
            }

            scaling = Application.BaseScale * scaling;
            //scaling = 1;

            imeCentext = ImmGetContext(handle);
            if (imeCentext == IntPtr.Zero)
            {
                imeCentext = ImmCreateContext();
                //ImmAssociateContext(handle, imeCentext);
                createimeCentext = true;
            }
            dropTarget = new OleDropTarget(this);

            var r = UnmanagedMethods.RegisterDragDrop(handle, dropTarget);

            //UnmanagedMethods.RegisterDragDrop(handle, Marshal.GetComInterfaceForObject(dropTarget, typeof(IDropTarget)));
            //UpdateWindow(handle);
            useGPU = Application.GetDrawingFactory().UseGPU;
            if (!isLayered && useGPU)
            {
                wglContext = new WglContext(handle);
                if (wglContext.RC == IntPtr.Zero)
                {
                    //useGPU = false;
                    wglContext.Dispose();
                    wglContext = null;
                    isLayered = true;
                    SetWindowLong(handle, (int)WindowLongParam.GWL_EXSTYLE, (uint)ExStyle);
                }
            }
            if (!isLayered)
            {
                DWM_BLURBEHIND dwm = new DWM_BLURBEHIND();
                dwm.dwFlags = DWM_BB_ENABLE | DWM_BB_BLURREGION;
                dwm.fEnable = true;
                dwm.hRegionBlur = CreateRectRgn(0, 0, -1, -1);
                DwmEnableBlurBehindWindow(handle, ref dwm);
                DeleteObject(dwm.hRegionBlur);
                //MARGINS sRT = new MARGINS();
                //sRT.Right = sRT.Left = sRT.Top = sRT.Bottom = -1;
                //DwmExtendFrameIntoClientArea(handle, ref sRT);
            }
        }


        bool useGPU;

        CPF.Windows.OpenGL.WglContext wglContext;
        //[DllImport("QCall")]
        //private static extern void SetGlobalInstanceRegisteredForMarshalling(long id);
        OleDropTarget dropTarget;

        public static readonly IntPtr DefaultCursor = LoadCursor(IntPtr.Zero, new IntPtr((int)UnmanagedMethods.Cursor.IDC_ARROW));

        protected virtual WindowStyles ExStyle
        {
            get
            {
                var style = WindowStyles.WS_EX_LEFT;
                if (isLayered)
                {
                    style |= WindowStyles.WS_EX_LAYERED;
                }
                return style;
            }
        }

        protected bool isLayered;

        protected virtual WindowStyles Style
        {
            get
            {
                return WindowStyles.WS_CLIPCHILDREN | WindowStyles.WS_CLIPSIBLINGS | WindowStyles.WS_POPUP |
                  //WindowStyles.WS_BORDER |
                  //WindowStyles.WS_THICKFRAME |
                  WindowStyles.WS_MINIMIZEBOX
                  ;
            }
        }

        public void DragMove()
        {
            SendMessage(handle, (int)WindowsMessage.WM_SYSCOMMAND, (IntPtr)SystemCommand.SC_MOUSEMOVE, IntPtr.Zero);
            BeginInvoke(a =>
            {
                SendMessage(handle, (int)WindowsMessage.WM_LBUTTONUP, IntPtr.Zero, IntPtr.Zero);
            }, null);
        }

        public void Invoke(SendOrPostCallback callback, object data)
        {
            if (Dispatcher.MainThread.CheckAccess())
            {
                callback(data);
            }
            else
            {
                var invokeMre = new ManualResetEvent(false);
                invokeQueue.Enqueue(new SendOrPostData { Data = data, SendOrPostCallback = callback, ManualResetEvent = invokeMre });
                PostMessage(handle, (uint)threadCallbackMessage, IntPtr.Zero, IntPtr.Zero);
                invokeMre.WaitOne();
            }
        }

        TimeSpan posttime;
        public void BeginInvoke(SendOrPostCallback callback, object data)
        {
            var time = Application.Elapsed;
            asyncQueue.Enqueue(new SendOrPostData { Data = data, SendOrPostCallback = callback });
            if (asyncQueue.Count <= 1 || (time.TotalMilliseconds - posttime.TotalMilliseconds) > 10)
            {
                PostMessage(handle, (uint)threadCallbackMessage, (IntPtr)1, IntPtr.Zero);
            }
            posttime = time;
        }
        bool canResize;
        public bool CanResize
        {
            get { return canResize; }
            set
            {
                canResize = value;
                var ex = GetWindowLong(handle, (int)WindowLongParam.GWL_STYLE);
                if (value)
                {
                    SetWindowLong(handle, (int)WindowLongParam.GWL_STYLE, ex | (uint)WindowStyles.WS_MAXIMIZEBOX);
                }
                else
                {
                    SetWindowLong(handle, (int)WindowLongParam.GWL_STYLE, ex ^ (uint)WindowStyles.WS_MAXIMIZEBOX);
                }
            }
        }
        public Action<PixelPoint> PositionChanged { get; set; }
        public event Action Move;
        public Action<Size> Resized { get; set; }
        public Action WindowStateChanged { get; set; }
        public Action ScalingChanged { get; set; }
        public Action Activated { get; set; }

        public Func<bool> Closing { get; set; }

        public Action Closed { get; set; }

        public Action Deactivated { get; set; }

        public IntPtr Handle { get { return handle; } }
        IntPtr handle;
        IntPtr hBitmap;
        Size oldSize;
        IntPtr ppvBits;
        /// <summary>
        /// 为了防止多屏幕拖动的时候抖动
        /// </summary>
        PixelPoint? ScalingChangedPosition;
        //bool cc;
        // 窗口过程  
        protected unsafe virtual IntPtr WndProc(IntPtr hwnd, uint msg, IntPtr wParam, IntPtr lParam)
        {
            if (msg == threadCallbackMessage)
            {
                if (wParam == IntPtr.Zero)
                {
                    if (invokeQueue.TryDequeue(out SendOrPostData result))
                    {
                        try
                        {
                            result.SendOrPostCallback(result.Data);
                        }
                        //catch (Exception e)
                        //{
                        //    throw new InvalidOperationException("Invoke操作异常", e);
                        //}
                        finally
                        {
                            result.ManualResetEvent.Set();
                        }
                    }
                }
                else
                {
                    while (asyncQueue.TryDequeue(out SendOrPostData data))
                    {
                        data.SendOrPostCallback(data.Data);
                    }
                }
            }
            if (WndPro != null)
            {
                var r = WndPro(hwnd, (WindowsMessage)msg, wParam, lParam);
                if (r != null)
                {
                    return r.Value;
                }
            }
            InputModifiers modifiers;
            switch ((WindowsMessage)msg)
            {
                case WindowsMessage.WM_ACTIVATEAPP:
                    //Debug.WriteLine(root.ToString() + "Deactivated");
                    //if (!cc)
                    //{
                    //    var s = Bounds;
                    //    InitOpenGLWin((int)s.Width, (int)s.Height, handle);
                    //}
                    //cc = true;
                    break;
                case WindowsMessage.WM_CREATE:
                    break;
                case UnmanagedMethods.WindowsMessage.WM_ACTIVATE:
                    //{
                    //}
                    //{
                    //    var s = Bounds;
                    //    ChangeOpenGLWinSize((int)s.Width, (int)s.Height);
                    //}
                    var wa = (UnmanagedMethods.WindowActivate)(ToInt32(wParam) & 0xffff);
                    //Debug.WriteLine(root.ToString() + " " + wa);
                    switch (wa)
                    {
                        case UnmanagedMethods.WindowActivate.WA_ACTIVE:
                        case UnmanagedMethods.WindowActivate.WA_CLICKACTIVE:
                            Activated?.Invoke();
                            break;
                        case UnmanagedMethods.WindowActivate.WA_INACTIVE:
                            Deactivated?.Invoke();
                            //Debug.WriteLine(root.ToString() + "Deactivated");
                            //root.InputManager.KeyboardDevice.ProcessEvent(new KeyEventArgs(root, Keys.None, root.InputManager.KeyboardDevice.Modifiers, root.InputManager.KeyboardDevice), KeyEventType.LostFocus);
                            break;
                    }
                    return IntPtr.Zero;
                //case WindowsMessage.WM_NCACTIVATE:
                //    if (!canActivate)
                //    {
                //        Debug.WriteLine(root.ToString() + "WM_NCACTIVATE");
                //        if (((int)wParam & 0xFFFF) != (int)WindowActivate.WA_INACTIVE)
                //        {
                //            if (lParam != IntPtr.Zero)
                //            {
                //                SetActiveWindow(lParam);
                //            }
                //            else
                //            {
                //                SetActiveWindow(IntPtr.Zero);
                //            }
                //        }
                //    }
                //    break;
                case WindowsMessage.WM_MOUSEACTIVATE:
                    if (!canActivate)
                    {
                        return new IntPtr(3);
                    }
                    break;
                case WindowsMessage.WM_DPICHANGED:
                    var dpi = ToInt32(wParam) & 0xffff;
#if Net4
                    var newDisplayRect = (UnmanagedMethods.RECT)Marshal.PtrToStructure(lParam, typeof(UnmanagedMethods.RECT));
#else
                    var newDisplayRect = Marshal.PtrToStructure<UnmanagedMethods.RECT>(lParam);
#endif
                    scaling = dpi / 96f;
                    //SetWindowPos(hwnd,
                    //    IntPtr.Zero,
                    //    newDisplayRect.left,
                    //    newDisplayRect.top,
                    //    newDisplayRect.right - newDisplayRect.left,
                    //    newDisplayRect.bottom - newDisplayRect.top,
                    //    SetWindowPosFlags.SWP_NOZORDER | SetWindowPosFlags.SWP_NOACTIVATE);
                    ScalingChangedPosition = new PixelPoint(newDisplayRect.left, newDisplayRect.top);
                    ScalingChanged?.Invoke();
                    root.LayoutManager.ExecuteLayoutPass();
                    ScalingChangedPosition = null;
                    return IntPtr.Zero;
                case WindowsMessage.WM_NCHITTEST:
                    break;
                case WindowsMessage.WM_NCCALCSIZE:
                    if (wParam != (IntPtr)0)
                    {
                        //var style = GetWindowLong(handle, (int)WindowLongParam.GWL_STYLE);
                        //if ((style & (uint)WindowStyles.WS_THICKFRAME) == 0)
                        //{
                        //    SetWindowLong(handle, (int)WindowLongParam.GWL_STYLE, style | (uint)WindowStyles.WS_THICKFRAME);
                        //}
                    }
                    //break;
                    return (IntPtr)1;
                case WindowsMessage.WM_GETMINMAXINFO:
                    WmGetMinMaxInfo(hwnd, lParam);
                    //break;
                    return IntPtr.Zero;
                case WindowsMessage.WM_NCLBUTTONDOWN:
                    int test = (int)wParam;
                    if (test == (int)HitTestValues.HTCLOSE || test == (int)HitTestValues.HTHELP || test == (int)HitTestValues.HTMINBUTTON || test == (int)HitTestValues.HTMAXBUTTON)
                    {
                        return IntPtr.Zero;
                    }
                    break;
                case WindowsMessage.WM_SYSCOMMAND:
                    //if (wParam.ToInt32() == 61728)
                    //{
                    //    return 0;
                    //}
                    break;
                case UnmanagedMethods.WindowsMessage.WM_KEYDOWN:
                case UnmanagedMethods.WindowsMessage.WM_SYSKEYDOWN:
                    GetKeyboardState(_keyStates);
                    InputModifiers result = 0;

                    if (IsDown(Keys.LeftAlt) || IsDown(Keys.RightAlt))
                    {
                        result |= InputModifiers.Alt;
                    }

                    if (IsDown(Keys.LeftCtrl) || IsDown(Keys.RightCtrl))
                    {
                        result |= InputModifiers.Control;
                    }

                    if (IsDown(Keys.LeftShift) || IsDown(Keys.RightShift))
                    {
                        result |= InputModifiers.Shift;
                    }

                    if (IsDown(Keys.LWin) || IsDown(Keys.RWin))
                    {
                        result |= InputModifiers.Windows;
                    }
                    root.InputManager.KeyboardDevice.Modifiers = result;
                    if (isEnable)
                    {
                        root.InputManager.KeyboardDevice.ProcessEvent(new KeyEventArgs(root, KeyInterop.KeyFromVirtualKey(ToInt32(wParam)), ToInt32(wParam), root.InputManager.KeyboardDevice.Modifiers, root.InputManager.KeyboardDevice), KeyEventType.KeyDown);
                    }
                    //System.Diagnostics.Debug.WriteLine(root.ToString() + wParam + "|" + lParam);
                    break;
                case UnmanagedMethods.WindowsMessage.WM_KEYUP:
                case UnmanagedMethods.WindowsMessage.WM_SYSKEYUP:

                    InputModifiers result1 = 0;

                    if (IsDown(Keys.LeftAlt) || IsDown(Keys.RightAlt))
                    {
                        result1 |= InputModifiers.Alt;
                    }

                    if (IsDown(Keys.LeftCtrl) || IsDown(Keys.RightCtrl))
                    {
                        result1 |= InputModifiers.Control;
                    }

                    if (IsDown(Keys.LeftShift) || IsDown(Keys.RightShift))
                    {
                        result1 |= InputModifiers.Shift;
                    }

                    if (IsDown(Keys.LWin) || IsDown(Keys.RWin))
                    {
                        result1 |= InputModifiers.Windows;
                    }
                    root.InputManager.KeyboardDevice.Modifiers = result1;
                    //root.InputManager.KeyboardDevice.Modifiers = GetMouseModifiers(wParam);
                    if (isEnable)
                    {
                        root.InputManager.KeyboardDevice.ProcessEvent(new KeyEventArgs(root, KeyInterop.KeyFromVirtualKey(ToInt32(wParam)), ToInt32(wParam), root.InputManager.KeyboardDevice.Modifiers, root.InputManager.KeyboardDevice), KeyEventType.KeyUp);
                    }
                    break;
                case UnmanagedMethods.WindowsMessage.WM_CHAR:
                    // Ignore control chars
                    if (ToInt32(wParam) >= 32 && isEnable)
                    {
                        var str = new string((char)ToInt32(wParam), 1);
                        root.InputManager.KeyboardDevice.ProcessEvent(new TextInputEventArgs(root, root.InputManager.KeyboardDevice, str), KeyEventType.TextInput);
                    }

                    break;
                case WindowsMessage.WM_IME_SETCONTEXT:
                    if (ToInt32(wParam) == 1)
                    {
                        var old = ImmAssociateContext(handle, imeCentext);
                    }
                    break;
                case WindowsMessage.WM_IME_STARTCOMPOSITION:

                    break;
                case WindowsMessage.WM_IME_ENDCOMPOSITION:

                    break;
                case WindowsMessage.WM_IME_NOTIFY:

                    break;
                case WindowsMessage.WM_MOUSEMOVE:
                    if (!_trackingMouse)
                    {
                        var tm = new TRACKMOUSEEVENT
                        {
#if Net4
                            cbSize = Marshal.SizeOf(typeof(TRACKMOUSEEVENT)),
#else
                            cbSize = Marshal.SizeOf<TRACKMOUSEEVENT>(),
#endif
                            dwFlags = 2,
                            hwndTrack = hwnd,
                            dwHoverTime = 0,
                        };

                        TrackMouseEvent(ref tm);
                    }
                    root.InputManager.KeyboardDevice.Modifiers = GetMouseModifiers(wParam);
                    modifiers = root.InputManager.KeyboardDevice.Modifiers;
                    if (isEnable)
                    {
                        root.InputManager.MouseDevice.ProcessEvent(new MouseEventArgs(root, modifiers.HasFlag(InputModifiers.LeftMouseButton), modifiers.HasFlag(InputModifiers.RightMouseButton), modifiers.HasFlag(InputModifiers.MiddleMouseButton), DipFromLParam(lParam), root.InputManager.MouseDevice, IsTouch()), root.LayoutManager.VisibleUIElements, EventType.MouseMove);
                    }
                    break;
                case WindowsMessage.WM_MOUSELEAVE:
                    _trackingMouse = false;
                    root.InputManager.KeyboardDevice.Modifiers = GetMouseModifiers(wParam);
                    modifiers = root.InputManager.KeyboardDevice.Modifiers;
                    if (isEnable)
                    {
                        root.InputManager.MouseDevice.ProcessEvent(new MouseEventArgs(root, modifiers.HasFlag(InputModifiers.LeftMouseButton), modifiers.HasFlag(InputModifiers.RightMouseButton), modifiers.HasFlag(InputModifiers.MiddleMouseButton), DipFromLParam(lParam), root.InputManager.MouseDevice, IsTouch()), root.LayoutManager.VisibleUIElements, EventType.MouseLeave);
                    }
                    break;
                case WindowsMessage.WM_MOUSEWHEEL:
                    root.InputManager.KeyboardDevice.Modifiers = GetMouseModifiers(wParam);
                    modifiers = root.InputManager.KeyboardDevice.Modifiers;
                    if (isEnable)
                    {
                        root.InputManager.MouseDevice.ProcessEvent(new MouseWheelEventArgs(root, modifiers.HasFlag(InputModifiers.LeftMouseButton), modifiers.HasFlag(InputModifiers.RightMouseButton), modifiers.HasFlag(InputModifiers.MiddleMouseButton), PointToClient(PointFromLParam(lParam)), root.InputManager.MouseDevice, new Vector(0, (ToInt32(wParam) >> 16))), root.LayoutManager.VisibleUIElements, EventType.MouseWheel);
                    }
                    break;
                case WindowsMessage.WM_MBUTTONUP:
                case WindowsMessage.WM_RBUTTONUP:
                case WindowsMessage.WM_LBUTTONUP:
                    MouseButton mouseButton = MouseButton.Left;
                    if ((WindowsMessage)msg == WindowsMessage.WM_RBUTTONUP)
                    {
                        mouseButton = MouseButton.Right;
                    }
                    if ((WindowsMessage)msg == WindowsMessage.WM_MBUTTONUP)
                    {
                        mouseButton = MouseButton.Middle;
                    }
                    root.InputManager.KeyboardDevice.Modifiers = GetMouseModifiers(wParam);
                    if (isEnable)
                    {
                        root.InputManager.MouseDevice.ProcessEvent(new MouseButtonEventArgs(root, root.InputManager.KeyboardDevice.Modifiers.HasFlag(InputModifiers.LeftMouseButton), root.InputManager.KeyboardDevice.Modifiers.HasFlag(InputModifiers.RightMouseButton), root.InputManager.KeyboardDevice.Modifiers.HasFlag(InputModifiers.MiddleMouseButton), DipFromLParam(lParam), root.InputManager.MouseDevice, mouseButton, IsTouch()), root.LayoutManager.VisibleUIElements, EventType.MouseUp);
                    }
                    break;
                case WindowsMessage.WM_MBUTTONDOWN:
                case WindowsMessage.WM_LBUTTONDOWN:
                case WindowsMessage.WM_RBUTTONDOWN:
                    //Debug.WriteLine("触摸点击：" + IsTouch());
                    mouseButton = MouseButton.Left;
                    if ((WindowsMessage)msg == WindowsMessage.WM_RBUTTONDOWN)
                    {
                        mouseButton = MouseButton.Right;
                    }
                    if ((WindowsMessage)msg == WindowsMessage.WM_MBUTTONDOWN)
                    {
                        mouseButton = MouseButton.Middle;
                    }
                    root.InputManager.KeyboardDevice.Modifiers = GetMouseModifiers(wParam);
                    if (isEnable)
                    {
                        root.InputManager.MouseDevice.ProcessEvent(new MouseButtonEventArgs(root, root.InputManager.KeyboardDevice.Modifiers.HasFlag(InputModifiers.LeftMouseButton), root.InputManager.KeyboardDevice.Modifiers.HasFlag(InputModifiers.RightMouseButton), root.InputManager.KeyboardDevice.Modifiers.HasFlag(InputModifiers.MiddleMouseButton), DipFromLParam(lParam), root.InputManager.MouseDevice, mouseButton, IsTouch()), root.LayoutManager.VisibleUIElements, EventType.MouseDown);
                    }
                    break;
                case WindowsMessage.WM_LBUTTONDBLCLK:
                case WindowsMessage.WM_MBUTTONDBLCLK:
                case WindowsMessage.WM_RBUTTONDBLCLK:
                    //Debug.WriteLine("DBLCLK");
                    break;
                case WindowsMessage.WM_MOVE:
                    //var pos = new Point((short)(ToInt32(lParam) & 0xffff) / scaling, (short)(ToInt32(lParam) >> 16) / scaling);
                    //var pos = Bounds.Location;
                    if (Move != null)
                    {
                        Move();
                    }
                    var pos = new PixelPoint((short)(ToInt32(lParam) & 0xffff), (short)(ToInt32(lParam) >> 16));
                    //bounds = Bounds;
                    PositionChanged?.Invoke(pos);
                    break;
                case WindowsMessage.WM_SIZE:
                    var size = (SizeCommand)wParam;

                    if (Resized != null &&
                        (size == SizeCommand.Restored ||
                         size == SizeCommand.Maximized))
                    {
                        var clientSize = new Size(ToInt32(lParam) & 0xffff, ToInt32(lParam) >> 16);
                        Resized(clientSize / scaling);
                    }

                    var windowState = size == SizeCommand.Maximized ? WindowState.Maximized
                        : (size == SizeCommand.Minimized ? WindowState.Minimized : WindowState.Normal);

                    if (windowState != this.oldwindowState || this.windowState == WindowState.FullScreen)
                    {
                        if (this.windowState != WindowState.FullScreen)
                        {
                            this.windowState = windowState;
                            oldwindowState = windowState;
                            WindowStateChanged?.Invoke();
                        }
                        else
                        {
                            if (oldwindowState != WindowState.FullScreen && windowState != WindowState.Normal)
                            {
                                oldwindowState = WindowState.FullScreen;
                                WindowStateChanged?.Invoke();
                            }
                        }
                    }
                    if (windowState != WindowState.Minimized)
                    {
                        RECT rr = new RECT(0, 0, LOWORD((int)lParam), HIWORD((int)lParam));
                        //Debug.WriteLine("WM_SIZE:" + rr.Width + "," + rr.Height);
                        //InvalidateRect(handle, ref rr, false);
                        if (root != null)
                        {
                            Invalidate(new Rect(new Point(), root.ActualSize));
                        }
                        //if (root != null)
                        //{
                        //System.Diagnostics.Debug.WriteLine(rr);
                        //root.Width = rr.Width / scaling;
                        //root.Height = rr.Height / scaling;
                        //root.ActualSize = rr;
                        //root.LayoutManager.ExecuteLayoutPass();
                        //}
                        if (!isLayered)
                        {
                            RedrawWindow(handle, ref rr, IntPtr.Zero, RDW_INVALIDATE | RDW_ERASE | RDW_ALLCHILDREN);
                            //UpdateWindow(handle);
                            //InvalidateRect(handle, ref rr, false);
                        }
                    }
                    break;
                case WindowsMessage.WM_NCUAHDRAWCAPTION:
                case WindowsMessage.WM_NCUAHDRAWFRAME:
                    return IntPtr.Zero;
                case WindowsMessage.WM_ERASEBKGND:
                    return (IntPtr)1;
                //case WindowsMessage.WM_NCACTIVATE:
                case WindowsMessage.WM_NCPAINT:
                    //DrawBoder(hwnd);
                    var _hdc = GetDC(hwnd);
                    OnPaint(_hdc, new Rect(new Point(), Bounds.Size));
                    ReleaseDC(hwnd, _hdc);
                    return (IntPtr)1;
                case WindowsMessage.WM_PAINT:
                    PAINTSTRUCT ps = new PAINTSTRUCT();
                    IntPtr hdc = BeginPaint(hwnd, out ps);
                    if (ps.rcPaint.Width > 0 && ps.rcPaint.Height > 0)
                    {
                        OnPaint(hdc, new Rect(ps.rcPaint.left, ps.rcPaint.top, ps.rcPaint.Width, ps.rcPaint.Height));
                    }
                    EndPaint(hwnd, ref ps);
                    break;
                case WindowsMessage.WM_SHOWWINDOW:
                    //System.Diagnostics.Debug.WriteLine(wParam);
                    //if (wParam == (IntPtr)1)
                    //{
                    //    root.Visibility = Visibility.Visible;
                    //}
                    //else
                    //{
                    //    root.Visibility = Visibility.Collapsed;
                    //}
                    break;
                case WindowsMessage.WM_POINTERDOWN:
                case WindowsMessage.WM_POINTERUP:
                case WindowsMessage.WM_POINTERUPDATE:
                    if (!touchMsg)
                    {
                        var id = GetPointerId(wParam);
                        var position = GetPointerLocation(lParam);
                        position = PointToClient(position);
                        //Debug.WriteLine("id:" + id + "  " + position);
                        POINTER_INFO pi = new POINTER_INFO();
                        if (GetPointerInfo(id, ref pi))
                        {
                            switch ((WindowsMessage)msg)
                            {
                                case WindowsMessage.WM_POINTERDOWN:
                                    //Debug.WriteLine("down");
                                    root.InputManager.TouchDevice.ProcessEvent(new TouchEventArgs(new TouchPoint { Id = id, Position = position }, root.InputManager.TouchDevice, Root), root.LayoutManager.VisibleUIElements, EventType.TouchDown);
                                    break;
                                case WindowsMessage.WM_POINTERUP:
                                    //Debug.WriteLine("up");
                                    root.InputManager.TouchDevice.ProcessEvent(new TouchEventArgs(new TouchPoint { Id = id, Position = position }, root.InputManager.TouchDevice, Root), root.LayoutManager.VisibleUIElements, EventType.TouchUp);
                                    //root.InputManager.TouchDevice.ProcessEvent(new TouchEventArgs(new TouchPoint { Id = id, Position = position }, root.InputManager.TouchDevice, Root), root.LayoutManager.VisibleUIElements, EventType.TouchLeave);
                                    break;
                                case WindowsMessage.WM_POINTERUPDATE:
                                    //Debug.WriteLine("update");
                                    root.InputManager.TouchDevice.ProcessEvent(new TouchEventArgs(new TouchPoint { Id = id, Position = position }, root.InputManager.TouchDevice, Root), root.LayoutManager.VisibleUIElements, EventType.TouchMove);
                                    break;
                            }
                        }
                    }
                    break;
                case WindowsMessage.WM_POINTERWHEEL:
                    Debug.WriteLine("WM_POINTERWHEEL");
                    break;
                case WindowsMessage.WM_GESTURE:
                    Debug.WriteLine("WM_GESTURE");
                    break;
                case WindowsMessage.WM_TOUCH:
                    if (touchMsg)
                    {
                        var touchInputCount = wParam.ToInt32();
                        var pTouchInputs = stackalloc TOUCHINPUT[touchInputCount];
                        if (GetTouchInputInfo(lParam, (uint)touchInputCount, pTouchInputs, Marshal.SizeOf(typeof(TOUCHINPUT))))
                        {
                            for (int i = 0; i < touchInputCount; i++)
                            {
                                var input = pTouchInputs[i];

                                if ((input.Flags & TouchInputFlags.TOUCHEVENTF_DOWN) > 0)
                                {
                                    Root.InputManager.TouchDevice.ProcessEvent(new TouchEventArgs(new TouchPoint
                                    {
                                        Id = (int)input.Id,
                                        Position = new Point((float)(input.X * 0.01), (float)(input.Y * 0.01))
                                    }, Root.InputManager.TouchDevice, Root), Root.LayoutManager.VisibleUIElements, EventType.TouchDown);

                                }
                                else if ((input.Flags & TouchInputFlags.TOUCHEVENTF_UP) > 0)
                                {
                                    Root.InputManager.TouchDevice.ProcessEvent(new TouchEventArgs(new TouchPoint
                                    {
                                        Id = (int)input.Id,
                                        Position = new Point((float)(input.X * 0.01), (float)(input.Y * 0.01))
                                    }, Root.InputManager.TouchDevice, Root), Root.LayoutManager.VisibleUIElements, EventType.TouchUp);

                                }
                                else if ((input.Flags & TouchInputFlags.TOUCHEVENTF_MOVE) > 0)
                                {
                                    Root.InputManager.TouchDevice.ProcessEvent(new TouchEventArgs(new TouchPoint
                                    {
                                        Id = (int)input.Id,
                                        Position = new Point((float)(input.X * 0.01), (float)(input.Y * 0.01))
                                    }, Root.InputManager.TouchDevice, Root), Root.LayoutManager.VisibleUIElements, EventType.TouchMove);
                                }
                            }
                            CloseTouchInputHandle(lParam);
                            return IntPtr.Zero;
                        }
                    }
                    break;
                case UnmanagedMethods.WindowsMessage.WM_CLOSE:
                    bool? preventClosing = Closing?.Invoke();
                    if (preventClosing == true)
                    {
                        return IntPtr.Zero;
                    }
                    if (_parent != null)
                    {
                        //EnableWindow(_parent.handle, true);
                        //_parent.SetEnable(true);
                        _parent.Activate();
                        SetFocus(_parent.handle);
                        SetWindowLongPtr(handle, (int)WindowLongParam.GWL_HWNDPARENT, IntPtr.Zero);
                        //_parent.SetVisible(true);
                        _parent = null;
                    }
                    break;
                case WindowsMessage.WM_DESTROY:
                    if (handle != IntPtr.Zero)
                    {
                        try
                        {
                            RevokeDragDrop(handle);
                        }
                        catch (Exception e)
                        {
                            Debug.WriteLine(e.Message);
                        }
                    }
                    if (imeCentext != IntPtr.Zero)
                    {
                        ImmReleaseContext(handle, imeCentext);
                        if (createimeCentext)
                        {
                            ImmDestroyContext(imeCentext);
                        }
                        imeCentext = IntPtr.Zero;
                    }
                    if (ismain)
                    {
                        PostQuitMessage(0);
                    }
                    timer?.Dispose();
                    timer = null;
                    wglContext?.Dispose();
                    wglContext = null;
                    if (hBitmap != IntPtr.Zero)
                    {
                        DeleteObject(hBitmap);
                        hBitmap = IntPtr.Zero;
                    }
                    //Dispose();
                    if (IconHandle != IntPtr.Zero)
                    {
                        DestroyIcon(IconHandle);
                        IconHandle = IntPtr.Zero;
                    }
                    if (_className != null)
                    {
                        UnmanagedMethods.UnregisterClass(_className, ModuleHandle);
                        _className = null;
                    }
                    if (RenderBitmap != null)
                    {
                        RenderBitmap.Dispose();
                        RenderBitmap = null;
                    }
                    handle = IntPtr.Zero;
                    Closed?.Invoke();
                    foreach (var item in invokeQueue)
                    {
                        item.SendOrPostCallback(item.Data);
                        item.ManualResetEvent.Set();
                    }
                    if (ismain)
                    {
                        Window = null;
                    }
                    //GC.Collect();
                    break;
            }
            return DefWindowProc(hwnd, msg, wParam, lParam);
        }


        [DllImport("Native.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)]
        public static extern void InitOpenGLWin(int width, int height, IntPtr pwin);

        [DllImport("Native.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)]
        public static extern void ChangeOpenGLWinSize(int width, int height);
        /// <summary>
        /// 处理WindowProc
        /// </summary>
        public Func<IntPtr, WindowsMessage, IntPtr, IntPtr, IntPtr?> WndPro { get; set; }

        bool rendToBitmap;
        public bool RenderToBitmap
        {
            get { return rendToBitmap; }
            set
            {
                rendToBitmap = value;
                //if (value)
                //{
                //    if (!isLayered)
                //    {
                //        isLayered = true;
                //        SetWindowLong(handle, (int)WindowLongParam.GWL_EXSTYLE, (uint)ExStyle);
                //        ShowInTaskbar(false);
                //    }
                //}
            }
        }
        /// <summary>
        /// RenderToBitmap=true，图像渲染到这个位图里，界面将不显示
        /// </summary>
        public Bitmap RenderBitmap { get; private set; }
        public Action OnRenderedBitmap { get; set; }
        //int cc = 0;

        //void testPaint()
        //{
        //    OpenGl gl = new OpenGl();
        //    gl.Enable(OpenGl.GL_ALPHA_TEST);
        //    gl.Enable(OpenGl.GL_DEPTH_TEST);
        //    gl.Enable(OpenGl.GL_COLOR_MATERIAL);

        //    gl.Enable(OpenGl.GL_LIGHTING);
        //    gl.Enable(OpenGl.GL_LIGHT0);

        //    gl.Enable(OpenGl.GL_BLEND);
        //    gl.BlendFunc(OpenGl.GL_SRC_ALPHA, OpenGl.GL_ONE_MINUS_SRC_ALPHA);
        //    gl.ClearColor(0, 0, 0, 0);
        //    gl.Clear(OpenGl.GL_COLOR_BUFFER_BIT | OpenGl.GL_DEPTH_BUFFER_BIT);

        //    gl.PushMatrix();

        //    gl.Color(0, 1, 1);
        //    gl.Begin(OpenGl.GL_TRIANGLES);                              // Drawing Using Triangles
        //    gl.Color(1.0f, 0.0f, 0.0f);                      // Set The Color To Red
        //    gl.Vertex(0.0f, 1.0f, 0.0f);                  // Top
        //    gl.Color(0.0f, 1.0f, 0.0f);                      // Set The Color To Green
        //    gl.Vertex(-1.0f, -1.0f, 0.0f);                  // Bottom Left
        //    gl.Color(0.0f, 0.0f, 1.0f);                      // Set The Color To Blue
        //    gl.Vertex(1.0f, -1.0f, 0.0f);                  // Bottom Right
        //    gl.End();

        //    gl.PopMatrix();
        //    gl.Flush();
        //}
        void OnPaint(IntPtr hdc, Rect rect)
        {
            if (handle == IntPtr.Zero)
            {
                return;
            }
            var r = this.Bounds;

            root.LayoutManager.ExecuteLayoutPass();
            if (RenderToBitmap)
            {
                if (RenderBitmap == null || oldSize != r.Size)
                {
                    oldSize = r.Size;
                    if (RenderBitmap != null)
                    {
                        RenderBitmap.Dispose();
                    }
                    RenderBitmap = new Bitmap((int)r.Width, (int)r.Height);
                }
                if (root.LayoutManager.VisibleUIElements != null)
                {
                    using (DrawingContext dc = DrawingContext.FromBitmap(RenderBitmap))
                    {
                        root.RenderView(dc, rect);
                        //DrawRenderRectangle(dc);
                    }
                }
                if (OnRenderedBitmap != null)
                {
                    //Debug.WriteLine(r.Size);
                    OnRenderedBitmap();
                }
            }
            else
            {
                IntPtr screenDC = IntPtr.Zero;
                IntPtr memDc = IntPtr.Zero;
                var oldBits = IntPtr.Zero;

                if (isLayered || !useGPU)
                {
                    screenDC = GetDC(IntPtr.Zero);
                    memDc = CreateCompatibleDC(screenDC);
                    if (oldSize != r.Size || hBitmap == IntPtr.Zero)
                    {
                        oldSize = r.Size;
                        if (hBitmap != IntPtr.Zero)
                        {
                            DeleteObject(hBitmap);
                        }

                        BITMAPINFOHEADER info = new BITMAPINFOHEADER();
                        info.biSize = (uint)Marshal.SizeOf(typeof(BITMAPINFOHEADER));
                        info.biBitCount = 32;
                        info.biHeight = -(int)r.Height;
                        info.biWidth = (int)r.Width;
                        info.biPlanes = 1;
                        hBitmap = CreateDIBSection(memDc, ref info, 0, out ppvBits, IntPtr.Zero, 0);
                    }
                    oldBits = SelectObject(memDc, hBitmap);
                }

                //Stopwatch stopwatch = new Stopwatch();
                //stopwatch.Start();
                wglContext?.MakeCurrent();
                if (root.LayoutManager.VisibleUIElements != null)
                {
                    IRenderTarget rt = new HDCRenderTarget((isLayered || !useGPU) ? memDc : hdc, (int)Bounds.Width, (int)Bounds.Height, !isLayered);
                    if (wglContext != null)
                    {
                        wglContext.GetFramebufferInfo(out var fb, out var sam, out var sten);
                        rt = new OpenGlRenderTarget<IntPtr>(hdc, wglContext, (int)Bounds.Width, (int)Bounds.Height, fb, sam, sten);
                    }
                    //Console.WriteLine(rt + " " + isLayered + "  " + useGPU + "  " + memDc);
                    using (DrawingContext dc = DrawingContext.FromRenderTarget(rt))
                    {
                        root.RenderView(dc, rect);
                        //DrawRenderRectangle(dc);
                    }
                    //testPaint();
                }
                wglContext?.SwapBuffers();
                //stopwatch.Stop();
                //Debug.WriteLine(stopwatch.ElapsedMilliseconds);

                if (hdc != IntPtr.Zero)
                {
                    //AlphaBlend(hdc, (int)rect.X, (int)rect.Y, (int)rect.Width, (int)rect.Height, memDc, (int)rect.X, (int)rect.Y, (int)rect.Width, (int)rect.Height, new BLENDFUNCTION { SourceConstantAlpha = 255, AlphaFormat = 1 });
                    if (!useGPU)
                    {
                        BitBlt(hdc, (int)rect.X, (int)rect.Y, (int)rect.Width, (int)rect.Height, memDc, (int)rect.X, (int)rect.Y, TernaryRasterOperations.SRCCOPY);
                    }
                }
                else
                {
                    if (isLayered)
                    {
                        if (windowState != WindowState.Minimized)
                        {
                            //Stopwatch stopwatch = new Stopwatch();
                            //stopwatch.Start();
                            BLENDFUNCTION blendFunc = new BLENDFUNCTION();
                            blendFunc.BlendOp = 0;
                            blendFunc.SourceConstantAlpha = 255;
                            blendFunc.AlphaFormat = 1;
                            blendFunc.BlendFlags = 0;
                            POINT topLoc = new POINT((int)r.Left, (int)r.Top);
                            SIZE bitMapSize = new SIZE((int)r.Width, (int)r.Height);
                            POINT srcLoc = new POINT(0, 0);
                            var rr = UpdateLayeredWindow(handle, screenDC, ref topLoc, ref bitMapSize, memDc, ref srcLoc, 0, ref blendFunc, 2);
                            //stopwatch.Stop();
                            //Debug.WriteLine(stopwatch.ElapsedMilliseconds);
                        }
                    }
                    else
                    {
                        //UpdateWindow(handle);
                    }
                }
                if (isLayered || !useGPU)
                {
                    SelectObject(memDc, oldBits);
                    ReleaseDC(IntPtr.Zero, screenDC);
                    DeleteDC(memDc);
                }
            }
        }

        //private void DrawRenderRectangle(DrawingContext dc)
        //{
        //    if (renderRectElement != null)
        //    {
        //        var rect1 = renderRectElement.GetContentBounds();
        //        var points = new List<Point>();
        //        points.Add(new Point());
        //        points.Add(new Point((float)Math.Round(rect1.Width * scaling) / scaling, 0));
        //        points.Add(new Point((float)Math.Round(rect1.Width * scaling) / scaling, (float)Math.Round(rect1.Height * scaling) / scaling));
        //        points.Add(new Point(0, (float)Math.Round(rect1.Height * scaling) / scaling));
        //        for (int i = 0; i < points.Count; i++)
        //        {
        //            points[i] = renderRectElement.TransformPoint(points[i]);
        //        }
        //        var p = renderRectElement.Parent;
        //        while (p != null && !p.IsRoot)
        //        {
        //            for (int i = 0; i < points.Count; i++)
        //            {
        //                points[i] = p.TransformPoint(points[i]);
        //            }
        //            p = p.Parent;
        //        }

        //        using (var brush = new SolidColorBrush(Color.FromRgb(255, 0, 0)))
        //        {
        //            var stroke = new Stroke(1);
        //            for (int i = 0; i < points.Count; i++)
        //            {
        //                dc.DrawLine(stroke, brush, points[i] * scaling, points[i == 3 ? 0 : (i + 1)] * scaling);
        //            }
        //        }
        //    }
        //}

        private Point PointFromLParam(IntPtr lParam)
        {
            return new Point((short)(ToInt32(lParam) & 0xffff), (short)(ToInt32(lParam) >> 16));
        }
        public Point PointToClient(Point point)
        {
            var p = new POINT { X = (int)point.X, Y = (int)point.Y };
            UnmanagedMethods.ScreenToClient(handle, ref p);
            return new Point(p.X, p.Y) / scaling;
        }
        //private Point DipFromLParam(IntPtr lParam)
        //{
        //    //var rect = WindowPixelRectangle;
        //    Debug.WriteLine((ToInt32(lParam) & 0xffff));
        //    return new Point((ToInt32(lParam) & 0xffff), (ToInt32(lParam) >> 16)) / scaling;
        //}
        private Point DipFromLParam(IntPtr lParam)
        {
            return new Point((short)(ToInt32(lParam) & 0xffff), (short)(ToInt32(lParam) >> 16)) / RenderScaling;
        }
        public static int ToInt32(IntPtr ptr)
        {
            if (IntPtr.Size == 4) return ptr.ToInt32();

            return (int)(ptr.ToInt64() & 0xffffffff);
        }
        public static int SignedHIWORD(int n)
        {
            int i = (int)(short)((n >> 16) & 0xffff);

            return i;
        }

        public static int SignedLOWORD(int n)
        {
            int i = (int)(short)(n & 0xFFFF);

            return i;
        }
        public static int SignedHIWORD(IntPtr n)
        {
            return SignedHIWORD(unchecked((int)(long)n));
        }
        public static int SignedLOWORD(IntPtr n)
        {
            return SignedLOWORD(unchecked((int)(long)n));
        }
        public bool IsTouch()
        {
            uint extra = GetMessageExtraInfo();
            bool isTouchOrPen = ((extra & 0xFFFFFF00) == 0xFF515700);
            if (!isTouchOrPen)
                return false;
            bool isTouch = ((extra & 0x00000080) == 0x00000080);
            return isTouch;
        }
        public static int GetPointerId(IntPtr WParam)
        {
            return (short)WParam;
        }

        public static Point GetPointerLocation(IntPtr LParam)
        {
            var lowword = LParam.ToInt32();
            return new Point()
            {
                X = (short)(lowword),
                Y = (short)(lowword >> 16),
            };
        }

        private bool IsDown(Keys key)
        {
            return (GetKeyStates(key) & KeyStates.Down) != 0;
        }

        byte[] _keyStates = new byte[256];
        private KeyStates GetKeyStates(Keys key)
        {
            int vk = KeyInterop.VirtualKeyFromKey(key);
            byte state = _keyStates[vk];
            KeyStates result = 0;

            if ((state & 0x80) != 0)
            {
                result |= KeyStates.Down;
            }

            if ((state & 0x01) != 0)
            {
                result |= KeyStates.Toggled;
            }

            return result;
        }
        [Flags]
        public enum KeyStates : byte
        {
            None = 0,
            Down = 1,
            Toggled = 2,
        }
        InputModifiers GetMouseModifiers(IntPtr wParam)
        {
            GetKeyboardState(_keyStates);
            var keys = (ModifierKeys)ToInt32(wParam);
            //var modifiers = InputManager.KeyboardDevice.Modifiers;
            var modifiers = InputModifiers.None;
            if (keys.HasFlag(ModifierKeys.MK_LBUTTON))
                modifiers |= InputModifiers.LeftMouseButton;
            if (keys.HasFlag(ModifierKeys.MK_RBUTTON))
                modifiers |= InputModifiers.RightMouseButton;
            if (keys.HasFlag(ModifierKeys.MK_MBUTTON))
                modifiers |= InputModifiers.MiddleMouseButton;
            if (keys.HasFlag(ModifierKeys.MK_CONTROL))
                modifiers |= InputModifiers.Control;
            if (keys.HasFlag(ModifierKeys.MK_SHIFT))
                modifiers |= InputModifiers.Shift;
            if (keys.HasFlag(ModifierKeys.MK_ALT) || IsDown(Keys.LeftAlt) || IsDown(Keys.RightAlt))
                modifiers |= InputModifiers.Alt;//因为wParam获取不到alt键
            return modifiers;
        }
        /// <summary>
        /// 窗体位置和尺寸，屏幕像素
        /// </summary>
        public Rect Bounds
        {
            get
            {
                RECT r = new RECT();
                GetWindowRect(handle, out r);
                return new Rect(r.left, r.top, r.Width, r.Height);
            }
            set
            {
                //bounds = value;
                if (ScalingChangedPosition.HasValue)
                {
                    SetWindowPos(handle, IntPtr.Zero, ScalingChangedPosition.Value.X, ScalingChangedPosition.Value.Y, (int)value.Width, (int)value.Height, SetWindowPosFlags.SWP_NOACTIVATE);
                }
                else
                {
                    SetWindowPos(handle, IntPtr.Zero, (int)value.Left, (int)value.Top, (int)value.Width, (int)value.Height, SetWindowPosFlags.SWP_NOACTIVATE);
                }

            }
        }
        //Rect? bounds;

        public PixelPoint Position
        {
            get
            {
                RECT r = new RECT();
                GetWindowRect(handle, out r);
                return new PixelPoint(r.left, r.top);
            }
            set
            {
                RECT r = new RECT();
                GetWindowRect(handle, out r);
                //bounds = new Rect(value.X, value.Y, r.Width, r.Height);
                SetWindowPos(handle, IntPtr.Zero, value.X, value.Y, r.Width, r.Height, SetWindowPosFlags.SWP_NOACTIVATE);
            }
        }


        public WindowState WindowState
        {
            get
            {
                var placement = default(UnmanagedMethods.WINDOWPLACEMENT);
                UnmanagedMethods.GetWindowPlacement(handle, ref placement);

                switch (placement.ShowCmd)
                {
                    case ShowWindowCommand.ShowMaximized:
                        if (windowState == WindowState.FullScreen)
                        {
                            return windowState;
                        }
                        return WindowState.Maximized;
                    case ShowWindowCommand.ShowMinimized:
                        return WindowState.Minimized;
                    default:
                        return WindowState.Normal;
                }
            }

            set
            {
                windowState = value;
                if (UnmanagedMethods.IsWindowVisible(handle))
                {
                    SetVisible(true);
                }
            }
        }

        private static bool multiMonitorSupport = (GetSystemMetrics(SystemMetric.SM_CMONITORS) != 0);
        private const int PRIMARY_MONITOR = unchecked((int)0xBAADF00D);
        public Screen Screen
        {
            get
            {
                if (multiMonitorSupport)
                {
                    var sc = MonitorFromWindow(handle, MONITOR.MONITOR_DEFAULTTONEAREST);
                    return ScreenImpl.FromMonitor(sc, IntPtr.Zero);
                }
                else
                {
                    return ScreenImpl.FromMonitor((IntPtr)PRIMARY_MONITOR, IntPtr.Zero);
                }
            }
        }
        bool canActivate = true;
        public bool CanActivate
        {
            get { return canActivate; }

            set
            {
                if (canActivate != value)
                {
                    canActivate = value;
                    if (!value)
                    {
                        SetWindowLongPtr(handle, (int)WindowLongParam.GWL_EXSTYLE, (IntPtr)(ExStyle | WindowStyles.WS_EX_NOACTIVATE));
                        SetWindowLongPtr(handle, (int)WindowLongParam.GWL_STYLE, (IntPtr)(Style | WindowStyles.WS_CHILD));
                    }
                    else
                    {
                        SetWindowLongPtr(handle, (int)WindowLongParam.GWL_EXSTYLE, (IntPtr)(ExStyle));
                        SetWindowLongPtr(handle, (int)WindowLongParam.GWL_STYLE, (IntPtr)(Style));
                    }
                }
            }
        }

        private void WmGetMinMaxInfo(IntPtr handle, IntPtr lParam)
        {
            // MINMAXINFO structure  
            MINMAXINFO mmi = (MINMAXINFO)Marshal.PtrToStructure(lParam, typeof(MINMAXINFO));

            // Get handle for nearest monitor to this window  
            //WindowInteropHelper wih = new WindowInteropHelper(this);
            IntPtr hMonitor = MonitorFromWindow(handle, MONITOR.MONITOR_DEFAULTTONEAREST);

            // Get monitor info  
            MONITORINFO monitorInfo = new MONITORINFO();
            monitorInfo.cbSize = Marshal.SizeOf(monitorInfo);
            GetMonitorInfo(hMonitor, ref monitorInfo);

            //// Get HwndSource  
            //HwndSource source = HwndSource.FromHwnd(wih.Handle);
            //if (source == null)
            //    // Should never be null  
            //    throw new Exception("Cannot get HwndSource instance.");
            //if (source.CompositionTarget == null)
            //    // Should never be null  
            //    throw new Exception("Cannot get HwndTarget instance.");

            //// Get transformation matrix  
            //Matrix matrix = source.CompositionTarget.TransformFromDevice;

            // Convert working area  
            RECT workingArea = monitorInfo.rcWork;
            //Point dpiIndependentSize =
            //    matrix.Transform(new Point(
            //            workingArea.Right - workingArea.Left,
            //            workingArea.Bottom - workingArea.Top
            //            ));

            //// Convert minimum size  
            //Point dpiIndenpendentTrackingSize = matrix.Transform(new Point(
            //    this.MinWidth,
            //    this.MinHeight
            //    ));

            // Set the maximized size of the window  
            //mmi.ptMaxSize.x = (int)dpiIndependentSize.X;
            //mmi.ptMaxSize.y = (int)dpiIndependentSize.Y;
            if (windowState == WindowState.FullScreen)
            {
                mmi.ptMaxSize.X = monitorInfo.rcMonitor.right - monitorInfo.rcMonitor.left;
                mmi.ptMaxSize.Y = monitorInfo.rcMonitor.bottom - monitorInfo.rcMonitor.top;
            }
            else
            {
                mmi.ptMaxSize.X = workingArea.right - workingArea.left;
                mmi.ptMaxSize.Y = workingArea.bottom - workingArea.top;
            }
            ////mmi.ptMaxTrackSize.x = 1370;
            ////mmi.ptMaxTrackSize.y = 772;
            //// Set the position of the maximized window  
            mmi.ptMaxPosition.X = windowState == WindowState.FullScreen ? 0 : workingArea.left - monitorInfo.rcMonitor.left;
            mmi.ptMaxPosition.Y = windowState == WindowState.FullScreen ? 0 : workingArea.top - monitorInfo.rcMonitor.top;

            // Set the minimum tracking size  
            //mmi.ptMinTrackSize.X = 1;//(int)dpiIndenpendentTrackingSize.X;
            //mmi.ptMinTrackSize.Y = 1;//(int)dpiIndenpendentTrackingSize.Y;

            mmi.ptMinTrackSize.X = 0;
            mmi.ptMinTrackSize.Y = 0;

            Marshal.StructureToPtr(mmi, lParam, true);
        }

        public void SetCursor(Cursor cursor)
        {
            var hCursor = (IntPtr)cursor.PlatformCursor == IntPtr.Zero ? DefaultCursor : (IntPtr)cursor.PlatformCursor;
            UnmanagedMethods.SetClassLong(handle, UnmanagedMethods.ClassLongIndex.GCLP_HCURSOR, hCursor);

            //if (_owner.IsPointerOver)
            //    UnmanagedMethods.SetCursor(hCursor);
        }

        public void SetRoot(View view)
        {
            root = view;
            root.LayoutUpdated += Root_LayoutUpdated;
        }

        private void Root_LayoutUpdated(object sender, RoutedEventArgs e)
        {
            var b = Bounds;
            var s = root.ActualSize;
            var l = root.ActualOffset;
            var src = root.Screen;
            if ((int)b.Width != (int)Math.Ceiling(s.Width * scaling) || (int)b.Height != (int)Math.Ceiling(s.Height * scaling) || (int)b.X != (int)(l.X * scaling + src.WorkingArea.X) || (int)b.Y != (int)(l.Y * scaling + src.WorkingArea.Y))
            {
                Bounds = new Rect(l.X * scaling + src.WorkingArea.X, l.Y * scaling + src.WorkingArea.Y, (float)Math.Ceiling(s.Width * scaling), (float)Math.Ceiling(s.Height * scaling));
            }
            //Debug.WriteLine("Root_LayoutUpdated" + s);
        }

        public float RenderScaling
        {
            get { return scaling; }
        }

        bool createimeCentext = false;
        IntPtr imeCentext;
        //public bool EnabledIME
        //{
        //    get { return ImmGetOpenStatus(imeCentext); }
        //    set
        //    {
        //        ImmSetOpenStatus(imeCentext, value);
        //    }
        //}
        public void SetIMEEnable(bool value)
        {
            ImmSetOpenStatus(imeCentext, value);
            if (IsTouch())
            {
                string windir = Environment.GetEnvironmentVariable("WINDIR");
                string osk = null;

                if (osk == null)
                {
                    osk = System.IO.Path.Combine(System.IO.Path.Combine(windir, "sysnative"), "osk.exe");
                    if (!System.IO.File.Exists(osk))
                        osk = null;
                }

                if (osk == null)
                {
                    osk = System.IO.Path.Combine(System.IO.Path.Combine(windir, "system32"), "osk.exe");
                    if (!System.IO.File.Exists(osk))
                    {
                        osk = null;
                    }
                }

                if (osk == null)
                    osk = "osk.exe";
                Process.Start(new ProcessStartInfo { UseShellExecute = true, FileName = osk });
            }
        }

        bool ismain;
        public bool IsMain
        {
            get
            {
                return ismain;
            }

            set
            {
                ismain = value;
                if (value)
                {
                    Window = this;
                }
            }
        }

        public void SetIMEPosition(Point point)
        {
            COMPOSITIONFORM cf = new COMPOSITIONFORM();
            cf.dwStyle = 2;
            cf.ptCurrentPos = new POINT((int)(point.X * scaling), (int)(point.Y * scaling));
            ImmSetCompositionWindow(imeCentext, ref cf);
        }

        public Point PointToScreen(Point point)
        {
            point *= scaling;
            var p = new POINT { X = (int)point.X, Y = (int)point.Y };
            ClientToScreen(handle, ref p);
            return new Point(p.X, p.Y);
        }

        bool paint = false;
        Rect invalidateRect;
        public void Invalidate(in Rect rect)
        {
            Rect all = new Rect(new Point(), root.ActualSize);//控件区域
            if ((all.Contains(rect) || rect.IntersectsWith(all) || all == rect || rect.Contains(all)) && !rect.IsEmpty && !all.IsEmpty)
            {
                //Debug.WriteLine(invalidateRect);
                if (invalidateRect.IsEmpty || rect.Contains(invalidateRect) || rect == invalidateRect)//如果更新区域大于原有失效区域，则当前失效区域设为更新区域
                {
                    invalidateRect = rect;
                }
                else if (invalidateRect.Contains(rect))//如果更新区域小于原来的失效区域，则失效区域不变
                {

                }
                else if (invalidateRect.IsEmpty)//如果原来的失效区域为空
                {
                    invalidateRect = rect;
                }
                else
                {//如果两个区域没有关联或者相交
                    var minX = invalidateRect.X < rect.X ? invalidateRect.X : rect.X;//确定包含这两个矩形的最小矩形
                    var minY = invalidateRect.Y < rect.Y ? invalidateRect.Y : rect.Y;
                    var maxW = (invalidateRect.Width + invalidateRect.X - minX) > (rect.Width + rect.X - minX) ? (invalidateRect.Width + invalidateRect.X - minX) : (rect.Width + rect.X - minX);
                    var maxH = (invalidateRect.Height + invalidateRect.Y - minY) > (rect.Height + rect.Y - minY) ? (invalidateRect.Height + invalidateRect.Y - minY) : (rect.Height + rect.Y - minY);
                    Rect min = new Rect(minX, minY, maxW, maxH);

                    invalidateRect = min;
                }
                invalidateRect.Intersect(all);//最后失效区域为在控件区域里面的相交区域
            }
            if (!paint && (timer == null))//&& isLayered
            {
                paint = true;
                BeginInvoke(a =>
                {
                    root.LayoutManager.ExecuteLayoutPass();
                    Rect rect1 = new Rect(((invalidateRect.X - 1) * scaling), ((invalidateRect.Y - 1) * scaling), ((invalidateRect.Width + 2) * scaling), ((invalidateRect.Height + 2) * scaling));
                    if (isLayered)
                    {
                        OnPaint(IntPtr.Zero, rect1);
                    }
                    else
                    {
                        RECT r = new RECT(rect1);
                        //InvalidateRect(handle, ref r, false);
                        RedrawWindow(handle, ref r, IntPtr.Zero, RDW_INVALIDATE | RDW_ALLCHILDREN | RDW_UPDATENOW);
                    }
                    //System.Diagnostics.Debug.WriteLine(invalidateRect);
                    invalidateRect = new Rect();
                    paint = false;
                }, null);
            }
            //if (timer == null && !isLayered)
            //{
            //    RECT r = new RECT((int)((rect.X - 1) * scaling), (int)((rect.Y - 1) * scaling), (int)((rect.Right + 2) * scaling), (int)((rect.Bottom + 2) * scaling));
            //    InvalidateRect(handle, ref r, false);
            //}
        }

        private void Timer_Tick(object sender, EventArgs e)
        {
            if (handle != IntPtr.Zero && IsWindowVisible(handle) && windowState != WindowState.Minimized && invalidateRect.Width > 0 && invalidateRect.Height > 0)
            {
                root.LayoutManager.ExecuteLayoutPass();
                Rect rect1 = new Rect(((invalidateRect.X - 1) * scaling), ((invalidateRect.Y - 1) * scaling), ((invalidateRect.Width + 2) * scaling), ((invalidateRect.Height + 2) * scaling));
                if (isLayered)
                {
                    OnPaint(IntPtr.Zero, rect1);
                }
                else
                {
                    RECT r = new RECT(rect1);
                    //InvalidateRect(handle, ref r, false);
                    RedrawWindow(handle, ref r, IntPtr.Zero, RDW_INVALIDATE | RDW_ALLCHILDREN | RDW_UPDATENOW);
                }
                //System.Diagnostics.Debug.WriteLine(invalidateRect);
                invalidateRect = new Rect();
            }
        }

        public void Activate()
        {
            UnmanagedMethods.SetActiveWindow(handle);
        }

        public void Capture()
        {
            SetCapture(handle);
        }

        WindowImpl _parent;
        public void ShowDialog(Window parent)
        {
            _parent = (WindowImpl)parent.ViewImpl;
            Show();
            SetWindowLongPtr(handle, (int)WindowLongParam.GWL_HWNDPARENT, _parent.handle);
        }

        public void ReleaseCapture()
        {
            UnmanagedMethods.ReleaseCapture();
        }

        public void Show()
        {
            //_parent = null;
            root.Visibility = Visibility.Visible;
            //if (isFirst)
            //{
            //    isFirst = false;
            //root.LayoutManager.ExecuteLayoutPass();

        }

        public void Hide()
        {
            root.Visibility = Visibility.Collapsed;
        }

        public void Close()
        {
            SendMessage(handle, (int)WindowsMessage.WM_CLOSE, IntPtr.Zero, IntPtr.Zero);
        }

        public void SetTitle(string title)
        {
            UnmanagedMethods.SetWindowText(handle, title);
        }

        bool _taskbarIcon = true;
        public void ShowInTaskbar(bool value)
        {
            if (_taskbarIcon == value)
            {
                return;
            }

            _taskbarIcon = value;

            var style = (UnmanagedMethods.WindowStyles)UnmanagedMethods.GetWindowLong(handle, (int)UnmanagedMethods.WindowLongParam.GWL_EXSTYLE);

            style &= ~(UnmanagedMethods.WindowStyles.WS_VISIBLE);

            style |= UnmanagedMethods.WindowStyles.WS_EX_TOOLWINDOW;

            if (value)
                style |= UnmanagedMethods.WindowStyles.WS_EX_APPWINDOW;
            else
                style &= ~(UnmanagedMethods.WindowStyles.WS_EX_APPWINDOW);

            WINDOWPLACEMENT windowPlacement = UnmanagedMethods.WINDOWPLACEMENT.Default;
            if (UnmanagedMethods.GetWindowPlacement(handle, ref windowPlacement))
            {
                //Toggle to make the styles stick
                UnmanagedMethods.ShowWindow(handle, ShowWindowCommand.Hide);
                UnmanagedMethods.SetWindowLong(handle, (int)UnmanagedMethods.WindowLongParam.GWL_EXSTYLE, (uint)style);
                UnmanagedMethods.ShowWindow(handle, windowPlacement.ShowCmd);
            }
        }

        public bool IsTopMost
        {
            get { return _topmost; }
        }

        bool _topmost = false;
        public void TopMost(bool value)
        {
            if (value == _topmost)
            {
                return;
            }

            IntPtr hWndInsertAfter = value ? WindowPosZOrder.HWND_TOPMOST : WindowPosZOrder.HWND_NOTOPMOST;
            UnmanagedMethods.SetWindowPos(handle,
                   hWndInsertAfter,
                   0, 0, 0, 0,
                   SetWindowPosFlags.SWP_NOMOVE | SetWindowPosFlags.SWP_NOSIZE | SetWindowPosFlags.SWP_NOACTIVATE);

            _topmost = value;
        }

        public void Dispose()
        {
            if (handle != IntPtr.Zero)
            {
                var h = handle;
                handle = IntPtr.Zero;
                UnmanagedMethods.DestroyWindow(h);
            }
            //if (Window == this)
            //{
            //    Window = null;
            //}
        }

        public void SetVisible(bool visible)
        {
            //var v = IsWindowVisible(handle);
            if (visible)
            {
                if (windowState == WindowState.Normal)
                {
                    root.LayoutManager.ExecuteLayoutPass();
                    invalidateRect = new Rect(new Point(), root.ActualSize);
                    if (isLayered)
                    {
                        OnPaint(IntPtr.Zero, new Rect((invalidateRect.X - 1) * scaling, (invalidateRect.Y - 1) * scaling, (invalidateRect.Width + 2) * scaling, (invalidateRect.Height + 2) * scaling));
                    }
                    else
                    {
                        //UpdateWindow(handle);
                    }
                    invalidateRect = new Rect();
                    if (canActivate)
                    {
                        ShowWindow(handle, ShowWindowCommand.Normal);
                    }
                    else
                    {
                        ShowWindow(handle, ShowWindowCommand.ShowNoActivate);
                    }
                    //if (_parent != null)
                    //{
                    //    //EnableWindow(_parent.handle, false);
                    //    _parent.SetEnable(false);
                    //}
                    if (this is PopupImpl)
                    {
                        SetWindowPos(handle, WindowPosZOrder.HWND_TOP, 0, 0, 0, 0, SetWindowPosFlags.SWP_NOMOVE | SetWindowPosFlags.SWP_NOSIZE | SetWindowPosFlags.SWP_NOACTIVATE);
                    }
                }
                else if (windowState == WindowState.Maximized || windowState == WindowState.FullScreen)
                {
                    root.LayoutManager.ExecuteLayoutPass();
                    invalidateRect = new Rect(new Point(), root.ActualSize);
                    if (isLayered)
                    {
                        OnPaint(IntPtr.Zero, new Rect((invalidateRect.X - 1) * scaling, (invalidateRect.Y - 1) * scaling, (invalidateRect.Width + 2) * scaling, (invalidateRect.Height + 2) * scaling));
                    }
                    else
                    {

                    }
                    invalidateRect = new Rect();
                    if (windowState == WindowState.FullScreen)
                    {
                        var placement = default(UnmanagedMethods.WINDOWPLACEMENT);
                        UnmanagedMethods.GetWindowPlacement(handle, ref placement);
                        if (placement.ShowCmd == ShowWindowCommand.ShowMaximized)
                        {
                            ShowWindow(handle, ShowWindowCommand.Normal);
                        }
                    }
                    ShowWindow(handle, ShowWindowCommand.Maximize);
                    //if (_parent != null)
                    //{
                    //    //EnableWindow(_parent.handle, false);
                    //    _parent.SetEnable(false);
                    //}
                }
                else
                {
                    ShowWindow(handle, ShowWindowCommand.Minimize);
                    //if (_parent != null)
                    //{
                    //    //EnableWindow(_parent.handle, false);
                    //    _parent.SetEnable(false);
                    //}
                }
                //if (bounds.HasValue)
                //{
                //    Bounds = bounds.Value;
                //}
                //Show();
            }
            else if (!visible)
            {
                //Hide();
                ShowWindow(handle, ShowWindowCommand.Hide);
            }
        }

        //private static int bitDepth;
        IntPtr IconHandle = IntPtr.Zero;
        public unsafe void SetIcon(Image image)
        {
            var stream = image.SaveToStream(ImageFormat.Png);
            var states = GdipCreateBitmapFromStream(new GPStream(stream), out IntPtr bitmap);
            stream.Dispose();
            GdipCreateHICONFromBitmap(bitmap, out IntPtr hIcon);
            GdipDisposeImage(bitmap);
            if (IconHandle != IntPtr.Zero)
            {
                DestroyIcon(IconHandle);
            }
            IconHandle = hIcon;
            PostMessage(handle, (int)WindowsMessage.WM_SETICON, new IntPtr((int)Icons.ICON_BIG), hIcon);
        }
        private unsafe short GetShort(byte* pb)
        {
            int retval = 0;
            if (0 != (unchecked((byte)pb) & 1))
            {
                retval = *pb;
                pb++;
                retval = unchecked(retval | (*pb << 8));
            }
            else
            {
                retval = unchecked((int)(*(short*)pb));
            }
            return unchecked((short)retval);
        }

        private unsafe int GetInt(byte* pb)
        {
            int retval = 0;
            if (0 != (unchecked((byte)pb) & 3))
            {
                retval = *pb; pb++;
                retval = retval | (*pb << 8); pb++;
                retval = retval | (*pb << 16); pb++;
                retval = unchecked(retval | (*pb << 24));
            }
            else
            {
                retval = *(int*)pb;
            }
            return retval;
        }


        //bool fullscreen;
        //public void SetFullscreen(bool fullscreen)
        //{
        //    this.fullscreen = fullscreen;
        //    if (fullscreen)
        //    {
        //        WindowState = WindowState.Maximized;
        //    }
        //    else
        //    {
        //        if (WindowState == WindowState.Maximized)
        //        {
        //            WindowState = WindowState.Normal;
        //        }
        //    }
        //}

        bool isEnable = true;
        public void SetEnable(bool enable)
        {
            isEnable = enable;
            //EnableWindow(handle, enable);
        }


        public float LayoutScaling { get { return scaling; } }
    }
    class PopupImpl : WindowImpl, IPopupImpl
    {
        protected override WindowStyles Style
        {
            get { return WindowStyles.WS_CLIPSIBLINGS | WindowStyles.WS_POPUP; }// | WindowStyles.WS_MAXIMIZEBOX
        }
        protected override WindowStyles ExStyle
        {
            get
            {
                var style = WindowStyles.WS_EX_TOOLWINDOW | WindowStyles.WS_EX_TOPMOST;
                if (isLayered)
                {
                    style |= WindowStyles.WS_EX_LAYERED;
                }
                return style;
            }
        }

        protected override IntPtr WndProc(IntPtr hwnd, uint msg, IntPtr wParam, IntPtr lParam)
        {
            switch ((WindowsMessage)msg)
            {
                case WindowsMessage.WM_ACTIVATE:
                    var wa = (WindowActivate)(ToInt32(wParam) & 0xffff);

                    switch (wa)
                    {
                        case WindowActivate.WA_INACTIVE:

                            break;
                    }
                    break;
                default:
                    break;
            }
            return base.WndProc(hwnd, msg, wParam, lParam);
        }
    }

    public class SendOrPostData
    {
        public SendOrPostCallback SendOrPostCallback;

        public object Data;

        public ManualResetEvent ManualResetEvent;
    }

}
