using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using Aga.Controls.Tree;
using Microsoft.Win32;

namespace LibreHardwareMonitor.Windows.Forms.UI.Themes
{
    public abstract class Theme
    {
        private static Theme _current = new LightTheme();
        public static Theme Current
        {
            get { return _current; }
            set
            {
                _current = value;
                foreach (Form form in Application.OpenForms)
                {
                    _current.Apply(form);
                }

                Init();
            }
        }

        private static void Init()
        {
            TreeViewAdv.CustomPlusMinusRenderFunc = (g, rect, isExpanded) =>
            {
                int x = rect.Left;
                int y = rect.Top + 5;
                int size = 8;
                using (Brush brush = new SolidBrush(Current.BackgroundColor))
                {
                    g.FillRectangle(brush, x - 1, y - 1, size + 4, size + 4);
                }
                using (Pen pen = new Pen(Current.TreeOutlineColor))
                {

                    g.DrawRectangle(pen, x, y, size, size);
                    g.DrawLine(pen, x + 2, y + (size / 2), x + size - 2, y + (size / 2));
                    if (!isExpanded)
                    {
                        g.DrawLine(pen, x + (size / 2), y + 2, x + (size / 2), y + size - 2);
                    }
                }
            };

            TreeViewAdv.CustomCheckRenderFunc = (g, rect, isChecked) =>
            {
                int x = rect.Left;
                int y = rect.Top + 1;
                int size = 12;
                using (Brush brush = new SolidBrush(Current.BackgroundColor))
                {
                    g.FillRectangle(brush, x - 1, y - 1, 12, 12);
                }
                using (Pen pen = new Pen(Current.TreeOutlineColor))
                {
                    g.DrawRectangle(pen, x, y, size, size);
                    if (isChecked)
                    {
                        x += 3;
                        y += 3;
                        g.DrawLine(pen, x, y + 3, x + 2, y + 5);
                        g.DrawLine(pen, x + 2, y + 5, x + 6, y + 1);
                        g.DrawLine(pen, x, y + 4, x + 2, y + 6);
                        g.DrawLine(pen, x + 2, y + 6, x + 6, y + 2);
                    }
                }
            };

            TreeViewAdv.CustomColumnBackgroundRenderFunc = (g, rect, isPressed, isHot) =>
            {
                using (Brush brush = new SolidBrush(Current.TreeBackgroundColor))
                {
                    g.FillRectangle(brush, rect);
                }
                using (Pen pen = new Pen(Current.TreeRowSepearatorColor))
                {
                    g.DrawLine(pen, rect.Left, rect.Top, rect.Right, rect.Top);
                    g.DrawLine(pen, rect.Left, rect.Top + 1, rect.Right, rect.Top + 1);
                }
            };

            TreeViewAdv.CustomColumnTextRenderFunc = (g, rect, font, text) =>
            {
                TextRenderer.DrawText(g, text, font, rect, Current.TreeTextColor, TextFormatFlags.Left);
            };

            TreeViewAdv.CustomHorizontalLinePen = new Pen(Current.TreeRowSepearatorColor);
            TreeViewAdv.CustomSelectedRowBrush = new SolidBrush(Current.TreeSelectedBackgroundColor);
            TreeViewAdv.CustomSelectedTextColor = Current.TreeSelectedTextColor;
        }

        private static List<Theme> _all;
        public static List<Theme> All
        {
            get
            {
                if (_all == null)
                {
                    _all = new List<Theme>();
                    foreach (Type type in typeof(Theme).Assembly.GetTypes())
                    {
                        if (type != typeof(Theme) && typeof(Theme).IsAssignableFrom(type))
                        {
                            _all.Add((Theme)type.GetConstructor(new Type[] { }).Invoke(new object[] { }));
                        }
                    }
                }
                return _all.OrderBy(x => x.DisplayName).ToList();
            }
        }

        public static bool SupportsAutoThemeSwitching()
        {
            if (Software.OperatingSystem.IsUnix)
            {
                return false;
            }

            if (Registry.GetValue(@"HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Themes\Personalize", "AppsUseLightTheme", -1) is int useLightTheme)
            {
                return useLightTheme != -1;
            }
            return false;
        }

        public static void SetAutoTheme()
        {
            if (Software.OperatingSystem.IsUnix)
            {
                return;
            }

            if (Registry.GetValue(@"HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Themes\Personalize", "AppsUseLightTheme", 1) is int useLightTheme)
            {
                if (useLightTheme > 0)
                {
                    Current = new LightTheme();
                }
                else
                {
                    Current = new DarkTheme();
                }
            }
            else
            {
                // Fallback incase registry fails
                Current = new LightTheme();
            }
        }

        public Theme(string id, string displayName)
        {
            Id = id;
            DisplayName = displayName;
        }

        public string Id { get; }
        public string DisplayName { get; }
        public abstract Color BackgroundColor { get; }
        public abstract Color ForegroundColor { get; }
        public abstract Color HyperlinkColor { get; }
        public abstract Color LineColor { get; }
        public abstract Color StrongLineColor { get; }
        public abstract Color SelectedBackgroundColor { get; }
        public abstract Color SelectedForegroundColor { get; }

        // button
        public virtual Color ButtonBackgroundColor => BackgroundColor;
        public virtual Color ButtonBorderColor => ForegroundColor;
        public virtual Color ButtonHoverBackgroundColor => SelectedBackgroundColor;
        public virtual Color ButtonPressedBackgroundColor => LineColor;
        public virtual Color ButtonTextColor => ForegroundColor;

        // menu
        public virtual Color MenuBackgroundColor => BackgroundColor;
        public virtual Color MenuBorderColor => StrongLineColor;
        public virtual Color MenuForegroundColor => ForegroundColor;
        public virtual Color MenuSelectedBackgroundColor => SelectedBackgroundColor;
        public virtual Color MenuSelectedForegroundColor => SelectedForegroundColor;

        // plot
        public virtual Color PlotBackgroundColor => BackgroundColor;
        public virtual Color PlotBorderColor => ForegroundColor;
        public abstract Color[] PlotColorPalette { get; }
        public abstract Color PlotGridMajorColor { get; }
        public abstract Color PlotGridMinorColor { get; }
        public virtual Color PlotTextColor => ForegroundColor;

        // scrollbar
        public virtual Color ScrollbarBackground => BackgroundColor;
        public virtual Color ScrollbarTrack => StrongLineColor;

        // splitter
        public virtual Color SplitterColor => BackgroundColor;
        public virtual Color SplitterHoverColor => SelectedBackgroundColor;

        // tree
        public virtual Color TreeBackgroundColor => BackgroundColor;
        public virtual Color TreeOutlineColor => ForegroundColor;
        public virtual Color TreeSelectedBackgroundColor => SelectedBackgroundColor;
        public virtual Color TreeTextColor => ForegroundColor;
        public virtual Color TreeSelectedTextColor => SelectedForegroundColor;
        public virtual Color TreeRowSepearatorColor => LineColor;

        // window
        public virtual Color WindowTitlebarBackgroundColor => BackgroundColor;
        public abstract bool WindowTitlebarFallbackToImmersiveDarkMode { get; }
        public virtual Color WindowTitlebarForegroundColor => ForegroundColor;

        public void Apply(Form form)
        {
            if (IsWindows10OrGreater(22000))
            {
                // Windows 11, Set the titlebar color based on theme
                int color = ColorTranslator.ToWin32(WindowTitlebarBackgroundColor);
                DwmSetWindowAttribute(form.Handle, DWMWA_CAPTION_COLOR, ref color, sizeof(int));
                color = ColorTranslator.ToWin32(WindowTitlebarForegroundColor);
                DwmSetWindowAttribute(form.Handle, DWMWA_TEXT_COLOR, ref color, sizeof(int));
            }
            else if (IsWindows10OrGreater(17763))
            {
                // Windows 10, fallback to using "Immersive Dark Mode" instead
                var attribute = DWMWA_USE_IMMERSIVE_DARK_MODE_BEFORE_20H1;
                if (IsWindows10OrGreater(18985))
                {
                    // Windows 10 20H1 or later
                    attribute = DWMWA_USE_IMMERSIVE_DARK_MODE;
                }
                int useImmersiveDarkMode = WindowTitlebarFallbackToImmersiveDarkMode ? 1 : 0;
                DwmSetWindowAttribute(form.Handle, (int)attribute, ref useImmersiveDarkMode, sizeof(int));
            }
            form.BackColor = BackgroundColor;
            foreach (Control control in form.Controls)
            {
                Apply(control);
            }
        }

        public void Apply(Control control)
        {
            if (control is Button button)
            {
                button.ForeColor = ButtonTextColor;
                button.FlatStyle = FlatStyle.Flat;
                button.FlatAppearance.BorderColor = ButtonBorderColor;
                button.FlatAppearance.MouseOverBackColor = ButtonHoverBackgroundColor;
                button.FlatAppearance.MouseDownBackColor = ButtonPressedBackgroundColor;
            }
            else if (control is LinkLabel linkLabel)
            {
                linkLabel.LinkColor = HyperlinkColor;
            }
            else if (control is PlotPanel plotPanel)
            {
                plotPanel.ApplyTheme();
            }
            else if (control is TreeViewAdv treeView)
            {
                treeView.BackColor = TreeBackgroundColor;
                treeView.ForeColor = TreeTextColor;
                treeView.LineColor = TreeOutlineColor;
            }
            else
            {
                control.BackColor = BackgroundColor;
                control.ForeColor = ForegroundColor;
            }

            foreach (Control child in control.Controls)
            {
                Apply(child);
            }
        }

        [DllImport("dwmapi.dll")]
        private static extern int DwmSetWindowAttribute(IntPtr hwnd, int attr, ref int attrValue, int attrSize);

        private const int DWMWA_USE_IMMERSIVE_DARK_MODE_BEFORE_20H1 = 19;
        private const int DWMWA_USE_IMMERSIVE_DARK_MODE = 20;
        private const int DWMWA_BORDER_COLOR = 34;
        private const int DWMWA_CAPTION_COLOR = 35;
        private const int DWMWA_TEXT_COLOR = 36;

        private static bool IsWindows10OrGreater(int build = -1)
        {
            return !Software.OperatingSystem.IsUnix && Environment.OSVersion.Version.Major >= 10 && Environment.OSVersion.Version.Build >= build;
        }
    }
}
