using System;
using System.Windows;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Threading;

namespace 自救器呼吸器综合检验仪
{
    internal static class WindowNavigationHelper
    {
        private const string SlideTransformResourceKey = "__WindowNavigationHelperSlideTransform";
        private static readonly TimeSpan TargetFadeDuration = TimeSpan.FromMilliseconds(420);
        private static readonly TimeSpan ContentSlideDuration = TimeSpan.FromMilliseconds(360);
        private static readonly CubicEase SlideEase = new CubicEase { EasingMode = EasingMode.EaseOut };

        public static void ShowWithoutWhiteFlash(Window currentWindow, Window targetWindow, string loadingTitle = null, string loadingSubtitle = null)
        {
            if (!CanInteractWith(currentWindow) || !CanInteractWith(targetWindow))
            {
                return;
            }

            ApplyWindowPlacement(currentWindow, targetWindow);
            PrepareTargetWindowForReveal(targetWindow);

            if (targetWindow.IsVisible)
            {
                RevealTargetWindow(currentWindow, targetWindow, null);
                return;
            }

            LoadingOverlayWindow loadingOverlay = ShowLoadingOverlay(currentWindow, loadingTitle, loadingSubtitle);

            if (targetWindow.IsLoaded)
            {
                targetWindow.Show();
                targetWindow.Dispatcher.BeginInvoke(
                    DispatcherPriority.ContextIdle,
                    new Action(() => RevealTargetWindow(currentWindow, targetWindow, loadingOverlay)));
                return;
            }

            EventHandler contentRenderedHandler = null;
            contentRenderedHandler = (sender, args) =>
            {
                targetWindow.ContentRendered -= contentRenderedHandler;
                RevealTargetWindow(currentWindow, targetWindow, loadingOverlay);
            };

            targetWindow.ContentRendered += contentRenderedHandler;
            targetWindow.Show();
        }

        public static void RestoreWindow(Window windowToRestore, Action beforeRestore = null)
        {
            if (!CanInteractWith(windowToRestore))
            {
                return;
            }

            beforeRestore?.Invoke();

            if (!windowToRestore.IsVisible)
            {
                windowToRestore.Show();
            }

            windowToRestore.Activate();
        }

        private static void ApplyWindowPlacement(Window currentWindow, Window targetWindow)
        {
            targetWindow.WindowStartupLocation = WindowStartupLocation.Manual;
            targetWindow.WindowState = currentWindow.WindowState;

            if (currentWindow.WindowState != WindowState.Normal)
            {
                return;
            }

            targetWindow.Left = currentWindow.Left;
            targetWindow.Top = currentWindow.Top;
            targetWindow.Width = currentWindow.ActualWidth > 0 ? currentWindow.ActualWidth : currentWindow.Width;
            targetWindow.Height = currentWindow.ActualHeight > 0 ? currentWindow.ActualHeight : currentWindow.Height;
        }

        private static void PrepareTargetWindowForReveal(Window targetWindow)
        {
            targetWindow.Opacity = 0;

            FrameworkElement contentRoot = targetWindow.Content as FrameworkElement;
            if (contentRoot == null)
            {
                return;
            }

            TranslateTransform slideTransform = GetOrCreateSlideTransform(contentRoot);
            slideTransform.X = 72;
            contentRoot.Opacity = 0.9;
        }

        private static void RevealTargetWindow(Window currentWindow, Window targetWindow, LoadingOverlayWindow loadingOverlay)
        {
            if (!CanInteractWith(currentWindow) || !CanInteractWith(targetWindow))
            {
                CloseLoadingOverlay(loadingOverlay);
                return;
            }

            currentWindow.Hide();
            AnimateTargetWindow(targetWindow);
            targetWindow.Activate();
            targetWindow.Dispatcher.BeginInvoke(
                DispatcherPriority.ContextIdle,
                new Action(() => CloseLoadingOverlay(loadingOverlay)));
        }

        private static LoadingOverlayWindow ShowLoadingOverlay(Window currentWindow, string loadingTitle, string loadingSubtitle)
        {
            if (!CanInteractWith(currentWindow))
            {
                return null;
            }

            LoadingOverlayWindow loadingOverlay = new LoadingOverlayWindow();
            loadingOverlay.AlignTo(currentWindow);
            loadingOverlay.MatchTopmost(currentWindow);
            loadingOverlay.SetMessage(
                loadingTitle ?? "正在切换检测页面",
                loadingSubtitle ?? "正在加载页面资源和设备状态，请稍候...");
            loadingOverlay.Show();
            return loadingOverlay;
        }

        private static void CloseLoadingOverlay(LoadingOverlayWindow loadingOverlay)
        {
            if (loadingOverlay == null)
            {
                return;
            }

            if (loadingOverlay.Dispatcher.HasShutdownStarted || loadingOverlay.Dispatcher.HasShutdownFinished)
            {
                return;
            }

            if (!loadingOverlay.IsLoaded)
            {
                return;
            }

            loadingOverlay.CloseWhenReady();
        }

        private static void AnimateTargetWindow(Window targetWindow)
        {
            if (targetWindow == null || targetWindow.Dispatcher.HasShutdownStarted || targetWindow.Dispatcher.HasShutdownFinished)
            {
                return;
            }

            DoubleAnimation fadeInAnimation = new DoubleAnimation
            {
                From = targetWindow.Opacity,
                To = 1,
                Duration = new Duration(TargetFadeDuration),
                FillBehavior = FillBehavior.HoldEnd
            };
            targetWindow.BeginAnimation(UIElement.OpacityProperty, fadeInAnimation);

            FrameworkElement contentRoot = targetWindow.Content as FrameworkElement;
            if (contentRoot == null)
            {
                return;
            }

            TranslateTransform slideTransform = GetOrCreateSlideTransform(contentRoot);
            DoubleAnimation slideAnimation = new DoubleAnimation
            {
                From = slideTransform.X,
                To = 0,
                Duration = new Duration(ContentSlideDuration),
                EasingFunction = SlideEase,
                FillBehavior = FillBehavior.HoldEnd
            };
            slideTransform.BeginAnimation(TranslateTransform.XProperty, slideAnimation);

            DoubleAnimation contentFadeAnimation = new DoubleAnimation
            {
                From = contentRoot.Opacity,
                To = 1,
                Duration = new Duration(ContentSlideDuration),
                EasingFunction = SlideEase,
                FillBehavior = FillBehavior.HoldEnd
            };
            contentRoot.BeginAnimation(UIElement.OpacityProperty, contentFadeAnimation);
        }

        private static TranslateTransform GetOrCreateSlideTransform(FrameworkElement contentRoot)
        {
            if (contentRoot.Resources[SlideTransformResourceKey] is TranslateTransform cachedTransform)
            {
                return cachedTransform;
            }

            TranslateTransform slideTransform = null;
            Transform renderTransform = contentRoot.RenderTransform;

            if (renderTransform is TranslateTransform existingTranslate)
            {
                slideTransform = existingTranslate;
            }
            else if (renderTransform is TransformGroup existingGroup)
            {
                foreach (Transform child in existingGroup.Children)
                {
                    if (child is TranslateTransform childTranslate)
                    {
                        slideTransform = childTranslate;
                        break;
                    }
                }

                if (slideTransform == null)
                {
                    slideTransform = new TranslateTransform();
                    existingGroup.Children.Add(slideTransform);
                }
            }
            else if (renderTransform == null || renderTransform == Transform.Identity || renderTransform.Value.IsIdentity)
            {
                slideTransform = new TranslateTransform();
                contentRoot.RenderTransform = slideTransform;
            }
            else
            {
                TransformGroup transformGroup = new TransformGroup();
                transformGroup.Children.Add(renderTransform);
                slideTransform = new TranslateTransform();
                transformGroup.Children.Add(slideTransform);
                contentRoot.RenderTransform = transformGroup;
            }

            contentRoot.RenderTransformOrigin = new Point(0.5, 0.5);
            contentRoot.Resources[SlideTransformResourceKey] = slideTransform;
            return slideTransform;
        }

        private static bool CanInteractWith(Window window)
        {
            return window != null
                && !AppShutdownCoordinator.IsShutdownInProgress
                && !window.Dispatcher.HasShutdownStarted
                && !window.Dispatcher.HasShutdownFinished;
        }
    }
}
