#region <<< Using Directives >>>
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Windows.Forms;

using Volpe.Cafe.EspressoUI.Charts;
using Volpe.Cafe.Resources;
#endregion

namespace Volpe.Cafe.EspressoUI.Themes
{
    /// <summary>
    /// Provides theme colors for the user interface elements.
    /// </summary>
    [Serializable]
    sealed class Theme
    {
        #region <<< Constructors >>>

        static Theme()
        {
            // init themes
            AllThemes = new Theme[]
            {
                GetLightTheme(),
                GetDarkTheme(),
                GetLavenderTheme(),
                GetSoftBlueTheme(),
                GetYellowHuesTheme(),
                GetFadedOrangeTheme()
            };

            // add to dictionary to search by name
            Values = new Dictionary<string, Theme>();
            foreach (var value in AllThemes)
            {
                Values.Add(value.Name, value);
            }

            // set current theme
            CurrentTheme = PreferredTheme;
        }

        private Theme(string name, string displayName, string categoryName, bool isDefault)
        {
            this.IsDefault    = isDefault;
            this.Name         = name;
            this.DisplayName  = displayName;
            this.CategoryName = categoryName;
            this.IconTheme    = IconTheme.Default;
        }

        #endregion

        #region <<< Properties >>>

        //----- static properties (for predefined themes) -----
        /// <summary>Gets an array containing a copy of all available themes.</summary>
        public static Theme[] AllThemes { get; }

        /// <summary>Gets the currently selected theme.</summary>
        public static Theme CurrentTheme { get; private set; }

        /// <summary>Gets the preferred Theme for this revision of the application.</summary>
        public static Theme PreferredTheme => Values["Light"];

        /// <summary>Gets the default Light Theme.</summary>
        public static Theme Light => Values["Light"];
        /// <summary>Gets the default Dark Theme.</summary>
        public static Theme Dark => Values["Dark"];

        private static Dictionary<string, Theme> Values { get; }

        //----- instance properties -----
        /// <summary>Gets whether the theme is the default theme.</summary>
        public bool IsDefault { get; private set; }

        /// <summary>Gets the name of the theme.</summary>
        public string Name { get; private set; }
        /// <summary>Gets the friendly name of the theme to display in the user interface.</summary>
        public string DisplayName { get; private set; }
        /// <summary>
        /// Gets the category name of the theme, where themes belonging to the same category may be logically grouped
        /// together within the user interface. If this value is null, the theme is displayed ungrouped.
        /// </summary>
        public string CategoryName { get; private set; }

        /// <summary>Gets the <see cref="IconTheme"/> associated with this theme.</summary>
        public IconTheme IconTheme { get; private set; }

        /// <summary>Gets the base background color for the themed UI elements.</summary>
        public Color BackColor { get; private set; }
        /// <summary>Gets the base foreground color for the themed UI elements.</summary>
        public Color ForeColor { get; private set; }

        public Border3DStyle GridBorderStyle { get; private set; }
        public Color GridBorderColor { get; private set; }
        public Color GridBackColor { get; private set; }
        public Color GridLinesColor { get; private set; }
        public Color LegendBackColor { get; private set; }

        public Color MenuStripBackColor { get; private set; }
        public Color ToolStripBackColor { get; private set; }
        public Color StatusStripBackColor { get; private set; }
        public Color MenuPressedBackColor { get; private set; }
        public Color MenuSelectedBackColor { get; private set; }
        public Color MenuBorderColor { get; private set; }
        public Color MenuIconBackColor { get; private set; }
        public Color MenuItemBackColor { get; private set; }
        public Color MenuItemSelectedBackColor { get; private set; }
        public Color MenuItemSelectedBorderColor { get; private set; }
        public Color ToolbarItemPressedBackColor { get; private set; }
        public Color ToolbarItemSelectedBackColor { get; private set; }
        public Color ToolbarItemSelectedBorderColor { get; private set; }
        public Color ToolbarItemSeparatorColor { get; private set; }

        #endregion

        #region <<< Methods >>>

        //----- static methods -----
        public static void SetCurrentTheme(Theme theme)
        {
            CurrentTheme = theme ?? PreferredTheme;
        }
        public static void SetCurrentTheme(string name)
        {
            if (name == null || !Values.TryGetValue(name, out Theme value)) {
                value = PreferredTheme;
            }
            CurrentTheme = value;
        }

        //----- static methods (for initializing predefined themes) -----
        private static Theme GetLightTheme()
        {
            return GetLightBasedThemeHelper("Light", "Light", null, true);
        }
        private static Theme GetDarkTheme()
        {
            return new Theme("Dark", "Dark", null, true)
            { 
                BackColor                      = SystemColors .AppWorkspace,
                ForeColor                      = Color        .Black,
                GridBorderStyle                = Border3DStyle.RaisedInner,
                GridBorderColor                = Color        .Empty,
                GridBackColor                  = Color        .Silver,
                GridLinesColor                 = SystemColors .AppWorkspace,
                LegendBackColor                = Color        .LightGray,
                MenuStripBackColor             = SystemColors .AppWorkspace,
                ToolStripBackColor             = SystemColors .AppWorkspace,
                StatusStripBackColor           = SystemColors .AppWorkspace,
                MenuPressedBackColor           = Color        .Silver,
                MenuSelectedBackColor          = Color        .LightGray,
                MenuBorderColor                = Color        .Gray,
                MenuIconBackColor              = SystemColors .AppWorkspace,
                MenuItemBackColor              = Color        .Silver,
                MenuItemSelectedBackColor      = Color        .LightGray,
                MenuItemSelectedBorderColor    = Color        .Gray,
                ToolbarItemPressedBackColor    = Color        .Silver,
                ToolbarItemSelectedBackColor   = Color        .LightGray,
                ToolbarItemSelectedBorderColor = Color        .Gray,
                ToolbarItemSeparatorColor      = Color        .Gray,
            };
        }
        private static Theme GetLavenderTheme()
        {
            return new Theme("Lavender", "Lavender", null, false)
            {
                BackColor                      = Color.GhostWhite,
                ForeColor                      = Color.Black,
                GridBorderStyle                = Border3DStyle.RaisedInner,
                GridBorderColor                = Color.Empty,
                GridBackColor                  = Color.FromArgb(255, 239, 239, 252),
                GridLinesColor                 = Color.FromArgb(255, 212, 212, 243),
                LegendBackColor                = Color.FromArgb(255, 230, 230, 249),
                MenuStripBackColor             = Color.GhostWhite,
                ToolStripBackColor             = Color.GhostWhite,
                StatusStripBackColor           = Color.GhostWhite,
                MenuPressedBackColor           = Color.FromArgb(255, 239, 239, 252),
                MenuSelectedBackColor          = Color.FromArgb(255, 212, 212, 243),
                MenuBorderColor                = Color.FromArgb(255, 212, 212, 243),
                MenuIconBackColor              = Color.FromArgb(255, 230, 230, 249),
                MenuItemBackColor              = Color.FromArgb(255, 239, 239, 252),
                MenuItemSelectedBackColor      = Color.FromArgb(255, 212, 212, 243),
                MenuItemSelectedBorderColor    = Color.FromArgb(255, 140, 140, 219),
                ToolbarItemPressedBackColor    = Color.FromArgb(255, 239, 239, 252),
                ToolbarItemSelectedBackColor   = Color.FromArgb(255, 212, 212, 243),
                ToolbarItemSelectedBorderColor = Color.FromArgb(255, 140, 140, 219),
                ToolbarItemSeparatorColor      = Color.FromArgb(255, 212, 212, 243)
            };
        }
        private static Theme GetSoftBlueTheme()
        {
            Theme theme = GetLightBasedThemeHelper("Soft Blue", "Soft Blue", null, false);
            AverageThemeColors(theme, Color.AliceBlue);
            theme.ForeColor = ChartColors.AverageColor(Color.DarkBlue, Color.Black);
            return theme;
        }
        private static Theme GetYellowHuesTheme()
        {
            Theme theme = GetLightBasedThemeHelper("Yellow Hues", "Yellow Hues", null, false);
            AverageThemeColors(theme, ChartColors.AverageColor(Color.LightYellow, Color.White));
            theme.ForeColor = ChartColors.AverageColor(Color.DarkGreen, Color.Black);
            return theme;
        }
        private static Theme GetFadedOrangeTheme()
        {
            return new Theme("Faded Orange", "Faded Orange", null, false)
            {
                BackColor                      = Color.Bisque,
                ForeColor                      = Color.Black,
                GridBorderStyle                = Border3DStyle.RaisedInner,
                GridBorderColor                = Color.Empty,
                GridBackColor                  = Color.PapayaWhip,
                GridLinesColor                 = Color.NavajoWhite,
                LegendBackColor                = Color.Cornsilk,
                MenuStripBackColor             = Color.Moccasin,
                ToolStripBackColor             = Color.Moccasin,
                StatusStripBackColor           = Color.Moccasin,
                MenuPressedBackColor           = Color.OldLace,
                MenuSelectedBackColor          = Color.OldLace,
                MenuBorderColor                = Color.BurlyWood,
                MenuIconBackColor              = Color.PapayaWhip,
                MenuItemBackColor              = Color.OldLace,
                MenuItemSelectedBackColor      = Color.Orange,
                MenuItemSelectedBorderColor    = Color.BurlyWood,
                ToolbarItemPressedBackColor    = Color.OldLace,
                ToolbarItemSelectedBackColor   = Color.OldLace,
                ToolbarItemSelectedBorderColor = Color.BurlyWood,
                ToolbarItemSeparatorColor      = Color.BurlyWood
            };
        }

        private static Theme GetLightBasedThemeHelper(string name, string displayName, string categoryName, bool isDefault)
        {
            return new Theme(name, displayName, categoryName, isDefault)
            {
                BackColor                      = Color.FromArgb(255, 250, 250, 250),
                ForeColor                      = Color.Black,
                GridBorderStyle                = Border3DStyle.RaisedInner,
                GridBorderColor                = Color.Empty,
                GridBackColor                  = Color.WhiteSmoke,
                GridLinesColor                 = Color.Gainsboro,
                LegendBackColor                = Color.FromArgb(255, 235, 235, 235),
                MenuStripBackColor             = Color.WhiteSmoke,
                ToolStripBackColor             = Color.WhiteSmoke,
                StatusStripBackColor           = Color.WhiteSmoke,
                MenuPressedBackColor           = Color.WhiteSmoke,
                MenuSelectedBackColor          = Color.Gainsboro,
                MenuBorderColor                = Color.DimGray,
                MenuIconBackColor              = Color.Gainsboro,
                MenuItemBackColor              = Color.WhiteSmoke,
                MenuItemSelectedBackColor      = Color.White,
                MenuItemSelectedBorderColor    = Color.DimGray,
                ToolbarItemPressedBackColor    = Color.WhiteSmoke,
                ToolbarItemSelectedBackColor   = Color.Gainsboro,
                ToolbarItemSelectedBorderColor = Color.DimGray,
                ToolbarItemSeparatorColor      = Color.LightGray
            };
        }

        private static void AverageThemeColors(Theme t, Color c)
        {
            t.BackColor                      = ChartColors.AverageColor(c, t.BackColor);
            t.ForeColor                      = ChartColors.AverageColor(c, t.ForeColor);
            t.GridBackColor                  = ChartColors.AverageColor(c, t.GridBackColor);
            t.GridLinesColor                 = ChartColors.AverageColor(c, t.GridLinesColor);
            t.LegendBackColor                = ChartColors.AverageColor(c, t.LegendBackColor);
            t.MenuStripBackColor             = ChartColors.AverageColor(c, t.MenuStripBackColor);
            t.ToolStripBackColor             = ChartColors.AverageColor(c, t.ToolStripBackColor);
            t.StatusStripBackColor           = ChartColors.AverageColor(c, t.StatusStripBackColor);
            t.MenuPressedBackColor           = ChartColors.AverageColor(c, t.MenuPressedBackColor);
            t.MenuSelectedBackColor          = ChartColors.AverageColor(c, t.MenuSelectedBackColor);
            t.MenuBorderColor                = ChartColors.AverageColor(c, t.MenuBorderColor);
            t.MenuIconBackColor              = ChartColors.AverageColor(c, t.MenuIconBackColor);
            t.MenuItemBackColor              = ChartColors.AverageColor(c, t.MenuItemBackColor);
            t.MenuItemSelectedBackColor      = ChartColors.AverageColor(c, t.MenuItemSelectedBackColor);
            t.MenuItemSelectedBorderColor    = ChartColors.AverageColor(c, t.MenuItemSelectedBorderColor);
            t.ToolbarItemPressedBackColor    = ChartColors.AverageColor(c, t.ToolbarItemPressedBackColor);
            t.ToolbarItemSelectedBackColor   = ChartColors.AverageColor(c, t.ToolbarItemSelectedBackColor);
            t.ToolbarItemSelectedBorderColor = ChartColors.AverageColor(c, t.ToolbarItemSelectedBorderColor);
            t.ToolbarItemSeparatorColor      = ChartColors.AverageColor(c, t.ToolbarItemSeparatorColor);
        }

        //----- instance methods -----
        public override string ToString()
        {
            return this.Name;
        }

        public void ApplyTheme(Form form)
        {
            form.ForeColor = this.ForeColor;
            form.BackColor = this.BackColor;

            // scan each control and apply theme colors based on control type
            this.ApplyThemeToChildren(form);
        }
        private void ApplyThemeToChildren(Control parent)
        {
            foreach (Control child in parent.Controls) {
                if (child is ToolStrip) {
                    // set back/fore-colors for all ToolStrips (menu, status, tool-strip)
                    child.BackColor = (child is MenuStrip) ? this.MenuStripBackColor
                                    : (child is StatusStrip) ? this.StatusStripBackColor
                                    : this.ToolStripBackColor;
                    child.ForeColor = this.ForeColor;
                }
                else {
                    if (child is Chart chart) {
                        // set additional theme colors for all other Chart controls
                        chart.GridBorderStyle = this.GridBorderStyle;
                        chart.GridBorderColor = this.GridBorderColor;
                        chart.GridBackColor   = this.GridBackColor;
                        chart.GridLinesColor  = this.GridLinesColor;
                        chart.LegendBackColor = this.LegendBackColor;
                        chart.ColorPalette    = ColorPalette.GetPalette(this.Name);
                    }

                    // refresh on child controls as well
                    this.ApplyThemeToChildren(child);
                }
            }
        }

        #endregion
    }
}
