using Features; using System; using System.Diagnostics; using System.Drawing; using System.Threading.Tasks; using System.Windows.Forms; namespace CrapFixer { /// /// Provides operations to load, analyze, fix, restore, and show help for FeatureNodes. /// public static class FeatureNodeManager { private static int totalChecked; private static int issuesFound; // Public properties to access the analysis results public static int TotalChecked => totalChecked; public static int IssuesFound => issuesFound; public static void ResetAnalysis() { totalChecked = 0; issuesFound = 0; Logger.Clear(); } /// /// Loads all features into the TreeView. /// public static void LoadFeatures(TreeView tree) { // Hide the TreeView to avoid flickering and visible scroll jump tree.Visible = false; var features = FeatureLoader.Load(); tree.Nodes.Clear(); foreach (var feature in features) AddNode(tree.Nodes, feature); // root nodes (categories) foreach (TreeNode root in tree.Nodes) { root.NodeFont = new Font(tree.Font, FontStyle.Bold); root.ForeColor = Color.RoyalBlue; // category color } tree.ExpandAll(); // expand all nodes // Set scroll to top and make TreeView visible tree.BeginInvoke(new Action(() => { // Ensure the first node is shown at the top (prevents auto-scroll to bottom) if (tree.Nodes.Count > 0) tree.TopNode = tree.Nodes[0]; tree.Visible = true; })); } /// /// Recursively adds a FeatureNode and its children into the TreeView. /// private static void AddNode(TreeNodeCollection treeNodes, FeatureNode featureNode) { string text = featureNode.IsCategory ? " " + featureNode.Name + " " // add extra space to avoid clipping : featureNode.Name; TreeNode node = new TreeNode(text) { Tag = featureNode, Checked = featureNode.DefaultChecked, }; treeNodes.Add(node); foreach (var child in featureNode.Children) AddNode(node.Nodes, child); } /// /// Analyzes all checked features recursively and logs only issues. /// public static async Task AnalyzeAll(TreeNodeCollection nodes) { ResetAnalysis(); // Iterate through all nodes and analyze each one recursively foreach (TreeNode node in nodes) { // Recursively analyze each node and ensure async tasks are awaited await AnalyzeCheckedRecursive(node); } Logger.Log("🔎 ANALYSIS COMPLETE", LogLevel.Info); Logger.Log(new string('=', 50), LogLevel.Info); int ok = totalChecked - issuesFound; Logger.Log($"Summary: {ok} of {totalChecked} checked settings are OK; {issuesFound} require attention.", issuesFound > 0 ? LogLevel.Warning : LogLevel.Info); } /// /// Recursively checks all features and logs misconfigurations. /// private static async Task AnalyzeCheckedRecursive(TreeNode node) { if (node.Tag is FeatureNode fn) { // If the node is not a category, is checked, and has a feature to check if (!fn.IsCategory && node.Checked && fn.Feature != null) { totalChecked++; bool isOk = await fn.Feature.CheckFeature(); // Await the async operation if (!isOk) { issuesFound++; node.ForeColor = Color.Red; // Mark as misconfigured string category = node.Parent?.Text ?? "General"; Logger.Log($"❌ [{category}] {fn.Name} - Not configured as recommended."); Logger.Log($" ➤ {fn.Feature.GetFeatureDetails()}"); // Log a separator when an issue was found Logger.Log(new string('-', 50), LogLevel.Info); } else { node.ForeColor = Color.Gray; // Mark as properly configured } } // Recursively process child nodes and ensure awaiting the tasks foreach (TreeNode child in node.Nodes) { await AnalyzeCheckedRecursive(child); // Recursively call and await the result } } } /// /// Fixes all checked features recursively. /// public static async Task FixChecked(TreeNode node) { if (node.Tag is FeatureNode fn) { if (!fn.IsCategory && node.Checked && fn.Feature != null) { bool result = await fn.Feature.DoFeature(); Logger.Log(result ? $"🔧 {fn.Name} - Fixed" : $"❌ {fn.Name} - ⚠️ Fix failed (This feature may require admin privileges)", result ? LogLevel.Info : LogLevel.Error); } foreach (TreeNode child in node.Nodes) await FixChecked(child); } } /// /// Restores all checked features recursively. /// public static void RestoreChecked(TreeNode node) { if (node.Tag is FeatureNode fn) { if (!fn.IsCategory && node.Checked && fn.Feature != null) { bool ok = fn.Feature.UndoFeature(); string category = node.Parent?.Text ?? "General"; Logger.Log(ok ? $"↩️ [{category}] {fn.Name} - Restored" : $"❌ [{category}] {fn.Name} - Restore failed", ok ? LogLevel.Info : LogLevel.Error); } foreach (TreeNode child in node.Nodes) RestoreChecked(child); } } /// /// Analyzes a selected feature or, if it's a category, analyzes only checked child features. /// public static async void AnalyzeFeature(TreeNode node) { // Analyze this node if it's a leaf node (not a category) if (node.Tag is FeatureNode fn && !fn.IsCategory && fn.Feature != null) { bool isOk = await fn.Feature.CheckFeature(); node.ForeColor = isOk ? Color.Gray : Color.Red; if (isOk) { Logger.Log($"✅ Feature: {fn.Name} is properly configured.", LogLevel.Info); } else { string category = node.Parent?.Text ?? "General"; Logger.Log($"❌ Feature: {fn.Name} requires attention.", LogLevel.Warning); Logger.Log($" ➤ {fn.Feature.GetFeatureDetails()}"); Logger.Log(new string('-', 50), LogLevel.Info); } } else { // If it's a category node, analyze only checked child nodes foreach (TreeNode child in node.Nodes) { if (child.Checked) AnalyzeFeature(child); } } } /// /// Attempts to fix the selected feature or, if it is a category, fixes only checked child features. /// public static async Task FixFeature(TreeNode node) { // Try to fix this node if it is NOT a category (i.e., a leaf node) if (node.Tag is FeatureNode fn && !fn.IsCategory && fn.Feature != null) { // Always fix the selected leaf node, regardless of Checked bool result = await fn.Feature.DoFeature(); Logger.Log(result ? $"🔧 {fn.Name} - Fixed" : $"❌ {fn.Name} - ⚠️ Fix failed (This feature may require admin privileges)", result ? LogLevel.Info : LogLevel.Error); } else { // If it's a category node, fix only checked child nodes (recursively) foreach (TreeNode child in node.Nodes) { if (child.Checked) await FixFeature(child); } } } /// /// Restores a selected feature (always) or, if it's a category, only restores checked child features. /// Logs success or failure. /// public static void RestoreFeature(TreeNode node) { // Restore feature node regardless of Checked state if (node.Tag is FeatureNode fn && !fn.IsCategory && fn.Feature != null) { bool ok = fn.Feature.UndoFeature(); Logger.Log(ok ? $"↩️ {fn.Name} - Restored" : $"❌ {fn.Name} - Restore failed", ok ? LogLevel.Info : LogLevel.Error); } else { // For category nodes, only restore checked children foreach (TreeNode child in node.Nodes) { if (child.Checked) RestoreFeature(child); } } } /// /// Displays help information for the selected feature or plugin. /// If a feature is selected, also offers to search online. /// public static void ShowHelp(TreeNode node) { // Show help for features if (node?.Tag is FeatureNode fn && fn.Feature != null) { string info = fn.Feature.Info(); MessageBox.Show( !string.IsNullOrEmpty(info) ? info : "No additional information available.", $"Help: {fn.Name}", MessageBoxButtons.OK, MessageBoxIcon.Information); // Optional online help var result = MessageBox.Show( "Would you like to search online for more information about this feature?", "Online Help", MessageBoxButtons.YesNo, MessageBoxIcon.Question); if (result == DialogResult.Yes) { string searchQuery = Uri.EscapeDataString(fn.Feature.GetFeatureDetails()); string webUrl = $"https://www.google.com/search?q={searchQuery}"; System.Diagnostics.Process.Start(new ProcessStartInfo { FileName = webUrl, UseShellExecute = true }); } return; } // Show help for plugins if (!PluginManager.ShowHelp(node)) { MessageBox.Show("⚠️ No feature or plugin selected, or help info unavailable.", "Help", MessageBoxButtons.OK, MessageBoxIcon.Warning); } } } }