using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Media;
namespace TeamAAS.Theme
{
///
/// 主题门面:主程序唯一的主题调用入口。
///
/// 职责边界:
/// - 主程序(App/ViewModel)只调用本类,不感知 VmResourceGuard/ThemeOverlay/官方字典细节;
/// - 配置读取(SystemConfig.Theme / AccentColor)由主程序完成后以参数传入,
/// 本模块不反向依赖主程序配置类型(依赖方向:主程序 → Global)。
///
/// 内部结构(同程序集 internal):
/// - VmResourceGuard:资源守护(切主题入口消毒 / Purge 第三方注入字典 / 污染清理 / 坏画刷整替)
/// - ThemeOverlay:纯值覆盖字典(主色派生配色 + 官方基准遮蔽,免疫第三方污染)
/// - ColorUtility:HSL 颜色派生
///
public static class ThemeManager
{
/// 明暗主题变化通知(主界面图标/预览刷新订阅)。
public static event Action ThemeChanged;
private static bool _dark;
private static Color _accent = Color.FromRgb(0x2D, 0x6C, 0xDF);
/// 按配置应用完整主题(启动 / 设置页保存时调用)。themeSetting:"Dark"/"Light"。
public static void ApplyTheme(string themeSetting, string accentHex)
{
ApplyTheme(string.Equals(themeSetting, "Dark", StringComparison.OrdinalIgnoreCase), accentHex);
}
/// 按配置应用完整主题(bool 版本)。
public static void ApplyTheme(bool dark, string accentHex)
{
ApplyTheme(dark, ParseAccent(accentHex));
}
/// 按配置应用完整主题(Color 版本)。
public static void ApplyTheme(bool dark, Color accent)
{
_dark = dark;
_accent = accent;
UpdateThemeInternal(dark, accent);
}
/// 切换明暗主题(保持当前主色)。
public static void UpdateTheme(bool dark)
{
_dark = dark;
UpdateThemeInternal(dark, _accent);
}
/// 切换主色(保持当前明暗)。hex 形式如 "#2D6CDF"。
public static void UpdateAccent(string accentHex)
{
UpdateAccent(ParseAccent(accentHex));
}
/// 切换主色(保持当前明暗)。
public static void UpdateAccent(Color color)
{
_accent = color;
// 覆盖字典重建(毫秒级,主色系派生 + 文字对比适配);
// 若该明暗的官方基准尚未捕获(异常时序),回退整替路径补齐
if (!ThemeOverlay.Apply(_dark, color))
{
VmResourceGuard.ForceReplaceTheme(_dark);
}
}
/// 安全解析颜色(非法/空值回退默认主色蓝)。
public static Color ParseAccent(string hex)
{
try
{
if (!string.IsNullOrWhiteSpace(hex))
return (Color)ColorConverter.ConvertFromString(hex);
}
catch { /* 非法颜色值回退默认 */ }
return Color.FromRgb(0x2D, 0x6C, 0xDF);
}
/// 核心切换流程(原 App.UpdateTheme 逻辑)。
private static void UpdateThemeInternal(bool dark, Color accent)
{
var merged = Application.Current != null
? Application.Current.Resources.MergedDictionaries
: null;
if (merged == null) return;
// 切主题前先消毒:移除第三方注入字典/字符串污染条目
VmResourceGuard.Sanitize(nameof(UpdateTheme));
// 结构损坏 / 存在坏画刷 / 该明暗的官方基准尚未捕获 → 整替官方字典
// (内部完成:摘覆盖→整替→捕获基准→重建覆盖)
// 否则 → 纯覆盖重建(丝滑,毫秒级)
if (!IsThemeStructureValid(merged) || VmResourceGuard.HasBrokenBrush() || !ThemeOverlay.HasBase(dark))
{
VmResourceGuard.ForceReplaceTheme(dark);
}
else if (!IsTargetSkinAlready(merged, dark))
{
if (!ThemeOverlay.Apply(dark, accent))
{
VmResourceGuard.ForceReplaceTheme(dark);
}
}
ThemeChanged?.Invoke();
}
///
/// 判定合并字典是否包含有效的官方主题结构
/// (Skin 调色板字典 + Theme.xaml 画刷/样式字典)。
/// 覆盖层加入后索引会变化,因此按存在性遍历判定。
///
private static bool IsThemeStructureValid(IList merged)
{
bool hasSkin = false;
bool hasTheme = false;
foreach (var d in merged)
{
try
{
var s = d.Source != null ? d.Source.OriginalString : null;
if (s == null) continue;
if (s.Contains("SkinDark.xaml") || s.Contains("SkinDefault.xaml")) hasSkin = true;
else if (s.Contains("Theme.xaml")) hasTheme = true;
}
catch { /* Source 读取异常时跳过 */ }
}
return hasSkin && hasTheme;
}
/// md 中是否已是目标明暗的调色板字典。
private static bool IsTargetSkinAlready(IList merged, bool dark)
{
foreach (var d in merged)
{
try
{
var s = d.Source != null ? d.Source.OriginalString : null;
if (s == null) continue;
if (dark && s.Contains("SkinDark.xaml")) return true;
if (!dark && s.Contains("SkinDefault.xaml")) return true;
}
catch { }
}
return false;
}
}
}