#if UNITY_STANDALONE_WIN

using System;
using UnityEngine;



namespace SatorImaging.AppWindowUtility
{
    public class Windows : IPlatformDependent
    {
        static IntPtr hWnd;

        static uint defaultWindowStyle;
        static uint defaultExWindowStyle;

        // unity's shader error color.
        static uint defaultKeyingColor = 0xFF00FF;
        static int borderWidth;
        static int titleBarHeight;


        private static bool isAlwaysOnTop = false;
        private static bool isTransparent = false;
        private static bool isFrameVisible = true;
        private static bool isClickThrough = false;
        private static bool isWallpaper = false;

        private static bool isInitialized = false;
        private static WinApi.RECT lastClientRect = new WinApi.RECT { left = 32, top = 64, right = 640, bottom = 480 };



        // features supported
        public bool AlwaysOnTopSupported { get; } = true;
        public bool TransparentSupported { get; } = true;
        public bool FrameVisibilitySupported { get; } = true;
        public bool ClickThroughSupported { get; } = true;
        public bool AsWallpaperSupported { get; } = true;
        public bool KeyingColorSupported { get; } = true;
        public bool WindowOpacitySupported { get; } = true;
        public bool WindowPlacementSupported { get; } = true;
        public bool WindowResizeSupported { get; } = true;



        public Windows()
        {
            if (isInitialized) return;

            hWnd = WinApi.GetUnityWindowHandle();
            defaultWindowStyle = WinApi.GetWindowLong(WinApi.GetUnityWindowHandle(), WinApi.GWL_STYLE);
            defaultExWindowStyle = WinApi.GetWindowLong(WinApi.GetUnityWindowHandle(), WinApi.GWL_EXSTYLE);

            // initialize client rect.
            WinApi.GetClientRect(hWnd, out lastClientRect);

            // calculate title bar and frame size
            WinApi.RECT windowRect;
            WinApi.GetWindowRect(hWnd, out windowRect);
            WinApi.RECT clientRect;
            WinApi.GetClientRect(hWnd, out clientRect);
            borderWidth = (windowRect.right - windowRect.left) - clientRect.right;
            borderWidth = (int)(borderWidth * 0.5f);
            titleBarHeight = (windowRect.bottom - windowRect.top) - clientRect.bottom - borderWidth;


            //Debug.Log($"{typeof(AppWindowUtility).FullName}.Initialize: Title Bar {titleBarHeight}px / Border {borderWidth}px");

            isInitialized = true;
        }//




        public void ResetStyle()
        {
            WinApi.SetWindowLong(hWnd, WinApi.GWL_STYLE, defaultWindowStyle);
            WinApi.SetWindowLong(hWnd, WinApi.GWL_EXSTYLE, defaultExWindowStyle);
        }//





        public bool GetAlwaysOnTop() => isAlwaysOnTop;
        public void SetAlwaysOnTop(bool enable)
        {
            WinApi.SetWindowPos(
                hWnd,
                enable ? WinApi.HWND_TOPMOST : WinApi.HWND_NOTOPMOST,
                0, 0, 0, 0,
                WinApi.SetWindowPosFlags.IgnoreMove | WinApi.SetWindowPosFlags.IgnoreResize
            );

            isAlwaysOnTop = enable;
        }//


        public bool GetTransparent() => isTransparent;
        public void SetTransparent(bool enable)
        {
            if (enable)
            {
                SetFrameVisibility(false);

                var currExStyle = WinApi.GetWindowLong(hWnd, WinApi.GWL_EXSTYLE);
                WinApi.SetWindowLong(hWnd, WinApi.GWL_EXSTYLE, currExStyle & ~WinApi.WS_EX_LAYERED);
                WinApi.SetDwmTransparent(true);

            }
            else
            {
                SetFrameVisibility(true);
                WinApi.SetDwmTransparent(false);
            }

            isTransparent = enable;
        }//




        public void SetKeyingColor(byte red, byte green, byte blue)
        {
            var currExStyle = WinApi.GetWindowLong(hWnd, WinApi.GWL_EXSTYLE);
            WinApi.SetWindowLong(hWnd, WinApi.GWL_EXSTYLE, currExStyle | WinApi.WS_EX_LAYERED);// | WindowsApi.WS_EX_TRANSPARENT);

            var color = (uint)(blue + (green << 8) + (red << 16));
            WinApi.SetLayeredWindowAttributes(hWnd, color, 0xFF, WinApi.LWA_COLORKEY);
        }//




        public bool GetFrameVisibility() => isFrameVisible;
        public void SetFrameVisibility(bool visible)
        {
            if (AppWindowUtility.FullScreen) return;
            if (visible == isFrameVisible) return;

            if (visible)
            {
                // must be done BEFORE SetWindowLong
                MoveWindowRelative(-borderWidth, -titleBarHeight);
                //ResizeWindowRelative(borderWidth * 2, titleBarHeight + borderWidth);

                WinApi.SetWindowLong(hWnd, WinApi.GWL_STYLE, defaultWindowStyle);

                // to make uGUI correct.
                WinApi.SetWindowPos(hWnd, IntPtr.Zero,
                    lastClientRect.left - borderWidth,
                    lastClientRect.top - titleBarHeight,
                    lastClientRect.right - lastClientRect.left + borderWidth * 2,
                    lastClientRect.bottom - lastClientRect.top + titleBarHeight + borderWidth,
                    WinApi.SetWindowPosFlags.FrameChanged | WinApi.SetWindowPosFlags.IgnoreMove
                );

            }
            else
            {
                // store last client size for showing frame again
                if (!AppWindowUtility.FullScreen)
                {
                    WinApi.GetClientRect(hWnd, out lastClientRect);
                    //Debug.Log($"SetFrameVisibility: Client Rect stored.");
                }

                var currStyle = WinApi.GetWindowLong(hWnd, WinApi.GWL_STYLE);
                WinApi.SetWindowLong(hWnd, WinApi.GWL_STYLE, currStyle & ~WinApi.WS_BORDER & ~WinApi.WS_THICKFRAME & ~WinApi.WS_CAPTION);

                //// must be done AFTER SetWindowLong
                MoveWindowRelative(borderWidth, titleBarHeight);
                // must be change window size 2 times to work correctly, idk why.
                WinApi.SetWindowPos(hWnd, IntPtr.Zero,
                    lastClientRect.left - borderWidth,
                    lastClientRect.top - titleBarHeight,
                    lastClientRect.right,
                    lastClientRect.bottom + 1,
                    WinApi.SetWindowPosFlags.FrameChanged | WinApi.SetWindowPosFlags.IgnoreMove
                );
                WinApi.SetWindowPos(hWnd, IntPtr.Zero,
                    lastClientRect.left - borderWidth,
                    lastClientRect.top - titleBarHeight,
                    lastClientRect.right,
                    lastClientRect.bottom - 1,
                    WinApi.SetWindowPosFlags.FrameChanged | WinApi.SetWindowPosFlags.IgnoreMove
                );

            }

            isFrameVisible = visible;

        }//





        public bool GetClickThrough() => isClickThrough;
        public void SetClickThrough(bool enable)
        {
            if (enable)
            {
                var currExStyle = WinApi.GetWindowLong(hWnd, WinApi.GWL_EXSTYLE);
                WinApi.SetWindowLong(hWnd, WinApi.GWL_EXSTYLE, currExStyle | WinApi.WS_EX_LAYERED | WinApi.WS_EX_TRANSPARENT);
            }
            else
            {
                WinApi.SetWindowLong(hWnd, WinApi.GWL_EXSTYLE, defaultExWindowStyle);
            }

            isClickThrough = enable;
        }//



        public void SetWindowOpacity(byte opacity)
        {
            ////////if(0xFF == opacity)
            {
                var currExStyle = WinApi.GetWindowLong(hWnd, WinApi.GWL_EXSTYLE);
                WinApi.SetWindowLong(hWnd, WinApi.GWL_EXSTYLE, currExStyle | WinApi.WS_EX_LAYERED);
                WinApi.SetLayeredWindowAttributes(hWnd, defaultKeyingColor, opacity, WinApi.LWA_ALPHA);
            }

            /* do not reset for combination with Transparent
            else
            {
                ResetStyle();
            }
            */
        }//



        public bool GetAsWallpaper() => isWallpaper;
        public void SetAsWallpaper(bool enable)
        {
        }//



        public void MoveWindowRelative(int relativeX, int relativeY)
        {
            if (AppWindowUtility.FullScreen) return;

            WinApi.RECT rect;
            WinApi.GetWindowRect(hWnd, out rect);
            WinApi.SetWindowPos(hWnd, IntPtr.Zero,
                rect.left + relativeX,
                rect.top + relativeY,
                rect.right - rect.left,
                rect.bottom - rect.top,
                WinApi.SetWindowPosFlags.NoFlag //ApiWindows.SetWindowPosFlags.IgnoreResize
            );
        }//

        public void MoveWindow(int x, int y)
        {
            if (AppWindowUtility.FullScreen) return;

            WinApi.RECT rect;
            WinApi.GetWindowRect(hWnd, out rect);
            WinApi.SetWindowPos(hWnd, IntPtr.Zero,
                x,
                y,
                rect.right - rect.left,
                rect.bottom - rect.top,
                WinApi.SetWindowPosFlags.NoFlag //ApiWindows.SetWindowPosFlags.IgnoreResize
            );
        }//



        public void ResizeWindow(int width, int height)
        {
            if (AppWindowUtility.FullScreen) return;

            WinApi.RECT rect;
            WinApi.GetWindowRect(hWnd, out rect);
            WinApi.SetWindowPos(hWnd, IntPtr.Zero,
                rect.left,
                rect.top,
                width,
                height,
                WinApi.SetWindowPosFlags.NoFlag //ApiWindows.SetWindowPosFlags.IgnoreMove
            );

        }

        public void ResizeWindowRelative(float deltaX, float deltaY, Vector2 resizeDirection, float aspectRatio) {
            if (AppWindowUtility.FullScreen) return;

            WinApi.RECT rect;
            WinApi.GetWindowRect(hWnd, out rect);

            int x = rect.left;
            int y = rect.top;
            int width = 0;
            int height = 0;

            if (resizeDirection.x > 0) {
                if (resizeDirection.y > 0) {
                    width = (int)(rect.right - rect.left + deltaX);
                }else if (resizeDirection.y < 0) {
                    width = (int)(rect.right - rect.left + deltaX);
                }else {
                    width = (int)(rect.right - rect.left + deltaX);
                }
                width = Mathf.Max(width, 100);
                height = (int)(width / aspectRatio);
            }else if (resizeDirection.x < 0) {
                if (resizeDirection.y > 0) {
                    x += (int)deltaX;
                    width = rect.right - x;
                }else if (resizeDirection.y < 0) {
                    x += (int)deltaX;
                    width = rect.right - x;
                }else {
                    x += (int)deltaX;
                    width = rect.right - x;
                }
                if (width < 100) x -= 100 - width;
                width = Mathf.Max(width, 100);
                height = (int)(width / aspectRatio);
            } else {
                if (resizeDirection.y > 0) {
                    y += (int)deltaY;
                    height = rect.bottom - y;
                }else if (resizeDirection.y < 0) {
                    height = (int)(rect.bottom - rect.top + deltaY);
                }
                if (height < 150 && resizeDirection.y > 0) y -= 150 - height;
                height = Mathf.Max(height, 150);
                width = (int)(height * aspectRatio);
            }

            WinApi.SetWindowPos(hWnd, IntPtr.Zero, x, y, width, height, WinApi.SetWindowPosFlags.NoFlag);
        }





    }//class
}//namespace
#endif
