using BPA.UIControl.Enums;
using System;
using System.Windows.Media.Animation;
using System.Windows.Media;
using System.Windows;
using Microsoft.Win32;
using BPA.UIControl.Models;
using System.Linq;
using System.Windows.Media.Effects;

namespace BPA.UIControl
{
    /// <summary>
    /// 主题管理
    /// </summary>
    public class ThemeManager
    {
        /// <summary>
        /// 所有颜色 key
        /// </summary>
        public static string[] ColorKeys = new string[]
        {
            "DefaultForeground",
            "DefaultBackground",
            "Primary",
            "Secondary",
            "SecondaryForeground",
            "Accent",
            "Light",
            "Dark",
            "Border",
            "BorderLight",
            "BorderLighter",
            "SeconarydText",
            "WatermarkText",
            "Mask",
            "MaskDark",
            "DialogBackground",
            "HeaderBackground",
            "FloatBackground",
            "AllDirectionEffect",
            "AllDirectionEffect2",
            "AllDirectionEffect3",
            "AllDirectionEffect4",
            "AllDirectionEffect5",
            "RightTopEffect",
            "LeftTopEffect",
            "RightBottomEffect",
            "LeftBottomEffect",
            "TopEffect",
            "BottomEffect",
            "RightEffect",
            "LeftEffect",
            "ControlBackground",
            "ContainerBackground",
        };

        private static bool themeApplying = false;

        /// <summary>
        /// 主题模式改变事件
        /// </summary>
        /// <param name="sender">发生对象</param>
        /// <param name="e">参数</param>
        public delegate void ThemeModeChangedEventHandler(object sender, ThemeModeChangedArgs e);

        /// <summary>
        /// 主题改变事件
        /// </summary>
        public static ThemeModeChangedEventHandler ThemeModeChanged;

        /// <summary>
        /// 获取当前应用模式是否为暗色
        /// </summary>
        /// <returns>是否为暗色</returns>
        public static bool GetIsAppDarkMode()
        {
            const string RegistryKeyPath = @"Software\Microsoft\Windows\CurrentVersion\Themes\Personalize";
            const string RegistryValueName = "AppsUseLightTheme";
            // 这里也可能是LocalMachine(HKEY_LOCAL_MACHINE)
            // see "https://www.addictivetips.com/windows-tips/how-to-enable-the-dark-theme-in-windows-10/"
            object registryValueObject = Registry.CurrentUser.OpenSubKey(RegistryKeyPath)?.GetValue(RegistryValueName);
            if (registryValueObject is null) return false;
            return (int)registryValueObject <= 0;
        }

        private static ColorAnimation GetColorAnimation(bool isDark, Color lightColor, Color darkColor)
        {
            themeApplying = true;

            var animation = new ColorAnimation
            {
                Duration = TimeSpan.FromMilliseconds(600),
                To = isDark ? darkColor : lightColor,
                EasingFunction = new CubicEase { EasingMode = EasingMode.EaseInOut }
            };

            animation.Completed += (sender, e) => themeApplying = false;
            return animation;
        }

        private static void ApplyColor(bool isDark, string colorName)
        {
            var application = Application.Current;
            Color lightColor;
            Color darkColor;
            if (colorName.Contains("Effect"))
            {
                lightColor = (Color)application.FindResource($"LightEffectColor");
                darkColor = (Color)application.FindResource($"DarkEffectColor");
            }
            else
            {
                lightColor = (Color)application.FindResource($"Light{colorName}Color");
                darkColor = (Color)application.FindResource($"Dark{colorName}Color");
            }


            if (application.Resources[colorName] is Brush brush)
            {
                ColorAnimation animation = GetColorAnimation(isDark, lightColor, darkColor);

                if (brush.IsFrozen)
                {
                    brush = brush.CloneCurrentValue();
                }

                brush.BeginAnimation(SolidColorBrush.ColorProperty, animation);

                application.Resources[colorName] = brush;
            }
            else if (application.Resources[colorName] is DropShadowEffect effect)
            {
                if (effect.IsFrozen)
                {
                    effect = effect.CloneCurrentValue();
                }

                effect.Color = isDark ? darkColor : lightColor;
                application.Resources[colorName] = effect;
            }
        }

        private static ThemeColor GetCurrentThemeColor(Application application)
        {
            return new ThemeColor
            {
                Primary = (Brush)application.Resources["Primary"],
                Light = (Brush)application.Resources["Light"],
                Dark = (Brush)application.Resources["Dark"],
                Accent = (Brush)application.Resources["Accent"],
            };
        }


        private static void ApplyTheme(bool isDark)
        {
            foreach (var colorKey in ColorKeys)
            {
                ApplyColor(isDark, colorKey);
            }

            var currentThemeColor = GetCurrentThemeColor(Application.Current);
            var args = new ThemeModeChangedArgs(currentThemeColor, isDark);
            ThemeModeChanged?.Invoke(Application.Current, args);
        }

        /// <summary>
        /// 切换主题模式
        /// </summary>
        /// <param name="mode">模式</param>
        public static void SwitchThemeMode(ThemeMode mode)
        {
            if (mode != ThemeMode.System)
            {
                SystemEvents.UserPreferenceChanged -= SystemEvents_UserPreferenceChanged;
            }

            bool isDark = false;
            switch (mode)
            {
                case ThemeMode.Light:
                default:
                    break;

                case ThemeMode.Dark:
                    isDark = true;
                    break;

                case ThemeMode.System:
                    isDark = GetIsAppDarkMode();
                    SystemEvents.UserPreferenceChanged += SystemEvents_UserPreferenceChanged;
                    break;
            }

            ApplyTheme(isDark);
        }

        private static void SystemEvents_UserPreferenceChanged(object sender, UserPreferenceChangedEventArgs e)
        {
            if (!themeApplying)
            {
                var isDark = GetIsAppDarkMode();
                ApplyTheme(isDark);
            }
        }

        /// <summary>
        /// 切换容器圆角半径
        /// </summary>
        /// <param name="cornerRadius">圆角半径</param>
        public static void SwitchContainerCornerRadius(double cornerRadius)
        {
            Application.Current.Resources["AllContainerCornerRadius"] = new CornerRadius(cornerRadius);
            Application.Current.Resources["LeftContainerCornerRadius"] = new CornerRadius(cornerRadius, 0, 0, cornerRadius);
            Application.Current.Resources["RightContainerCornerRadius"] = new CornerRadius(0, cornerRadius, cornerRadius, 0);
            Application.Current.Resources["TopContainerCornerRadius"] = new CornerRadius(cornerRadius, cornerRadius, 0, 0);
            Application.Current.Resources["BottomContainerCornerRadius"] = new CornerRadius(0, 0, cornerRadius, cornerRadius);
        }

        /// <summary>
        /// 切换控件圆角半径
        /// </summary>
        /// <param name="cornerRadius">圆角半径</param>
        public static void SwitchControlCornerRadius(double cornerRadius)
        {
            Application.Current.Resources["AllControlCornerRadius"] = new CornerRadius(cornerRadius);
            Application.Current.Resources["LeftControlCornerRadius"] = new CornerRadius(cornerRadius, 0, 0, cornerRadius);
            Application.Current.Resources["RightControlCornerRadius"] = new CornerRadius(0, cornerRadius, cornerRadius, 0);
            Application.Current.Resources["TopControlCornerRadius"] = new CornerRadius(cornerRadius, cornerRadius, 0, 0);
            Application.Current.Resources["BottomControlCornerRadius"] = new CornerRadius(0, 0, cornerRadius, cornerRadius);
        }

        private static bool CheckIsDark(Brush brush)
        {
            if (brush is SolidColorBrush color)
            {
                return (color.Color.R * 0.299 + color.Color.G * 0.578 + color.Color.B * 0.114) < 192;
            }

            return false;
        }

        /// <summary>
        /// 应用主题配色
        /// </summary>
        /// <param name="colorUrl">颜色配置文件路径</param>
        public static void ApplyThemeColor(string colorUrl)
        {
            var resourceDictionaries = Application.Current.Resources.MergedDictionaries;

            var resourceDictionary = new ResourceDictionary
            {
                Source = new Uri(colorUrl, UriKind.RelativeOrAbsolute)
            };

            if (resourceDictionaries.Any(x => x.Source.AbsoluteUri == resourceDictionary.Source.AbsoluteUri))
            {
                var oldColorResource = resourceDictionaries.First(x => x.Source.AbsoluteUri == resourceDictionary.Source.AbsoluteUri);
                resourceDictionaries.Remove(oldColorResource);
            }

            resourceDictionaries.Add(resourceDictionary);

            var currentBackground = (Brush)Application.Current.Resources["DefaultBackground"];
            var currentThemeColor = GetCurrentThemeColor(Application.Current);
            var isDarkMode = CheckIsDark(currentBackground);
            ApplyTheme(isDarkMode);

            var args = new ThemeModeChangedArgs(currentThemeColor, isDarkMode);
            ThemeModeChanged?.Invoke(Application.Current, args);
        }
    }

    /// <summary>
    /// 主题模式改变参数
    /// </summary>
    public class ThemeModeChangedArgs : EventArgs
    {
        /// <summary>
        /// 主题配色
        /// </summary>
        public ThemeColor ThemeColor { get; set; }

        /// <summary>
        /// 是否未暗色模式
        /// </summary>
        public bool IsDarkMode { get; set; }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="themeColor">主题配色</param>
        /// <param name="isDarkMode">是否未暗色模式</param>
        public ThemeModeChangedArgs(ThemeColor themeColor, bool isDarkMode)
        {
            ThemeColor = themeColor;
            IsDarkMode = isDarkMode;
        }
    }
}