using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Windows.Forms;

namespace Inspectron.Settings.Windows.Configuration;

public partial class OptionsWindow : Form
{
    private Dictionary<string, List<OptionSetting>> settingsByCategory;
    private IControlFactory controlFactory;

    public OptionsWindow(IControlFactory controlFactory)
    {
        this.controlFactory = controlFactory;
        settingsByCategory = new Dictionary<string, List<OptionSetting>>();

        InitializeComponent();
        this.treeViewCategories.AfterSelect += new TreeViewEventHandler(this.treeViewCategories_AfterSelect);
        this.buttonOK.Click += new EventHandler(this.buttonOK_Click);
        this.buttonCancel.Click += new EventHandler(this.buttonCancel_Click);
    }

    public void LoadFromSettings(InspectronSettings inspectronSettings)
    {
        // UserSettings is a Tree<object> with folders (string) and UserSettingsInfo nodes.
        // Traverse the tree to find all UserSettingsInfo nodes and their PropertyDescriptors.

        void Traverse(object nodeObj, string path)
        {
            var node = nodeObj as dynamic; // Tree<object>
            object value = node.Value;

            if (value is string folderName)
            {
                // Folder node, append to path and traverse children
                string newPath = string.IsNullOrEmpty(path) ? folderName : $"{path}/{folderName}";
                foreach (var child in node.Children)
                    Traverse(child, newPath);
            }
            else if (value is Inspectron.Settings.InspectronSettings.UserSettingsInfo info)
            {
                // Leaf node: user settings group
                string groupName = info.Name;
                string groupPath = string.IsNullOrEmpty(path) ? groupName : $"{path}/{groupName}";
                foreach (var pd in info.Settings)
                {
                    // Try to get owner from BoundPropertyDescriptor, else skip
                    object owner = null;
                    string description = pd.Description;
                    if (pd is Inspectron.Settings.BoundPropertyDescriptor bpd)
                    {
                        owner = bpd.Owner;
                        if (string.IsNullOrEmpty(description))
                            description = bpd.DisplayName;
                    }
                    else
                    {
                        // If not a BoundPropertyDescriptor, cannot get owner, skip
                        continue;
                    }
                    RegisterUserSetting(owner, bpd.PropertyInfo.Name, groupPath, description);
                }
            }
        }

        Traverse(inspectronSettings.UserSettings.Root, "");
    }

    public void RegisterUserSetting(object owner, string propertyName, string treePath, string description = null)
    {
        // Get the PropertyInfo from owner and propertyName
        var property = owner.GetType().GetProperty(propertyName, BindingFlags.Public | BindingFlags.Instance);
        if (property == null)
            throw new ArgumentException($"Property '{propertyName}' not found on owner type.");

        if (string.IsNullOrEmpty(description))
            description = propertyName;

        // Create an OptionSetting instance
        var setting = new OptionSetting
        {
            Owner = owner,
            Property = property,
            Description = description,
            TreePath = treePath,
            Value = property.GetValue(owner)
        };

        // Add to settingsByCategory
        if (!settingsByCategory.TryGetValue(treePath, out var settingsList))
        {
            settingsList = new List<OptionSetting>();
            settingsByCategory[treePath] = settingsList;
        }
        settingsList.Add(setting);

        // Add nodes to treeViewCategories
        AddTreeNodes(treeViewCategories, treePath);
    }

    private void AddTreeNodes(TreeView treeView, string treePath)
    {
        var parts = treePath.Split(new[] { '/' }, StringSplitOptions.RemoveEmptyEntries);

        TreeNodeCollection nodes = treeView.Nodes;
        foreach (var part in parts)
        {
            var node = Enumerable.Cast<TreeNode>(nodes).FirstOrDefault(n => n.Text == part);
            if (node == null)
            {
                node = new TreeNode(part);
                nodes.Add(node);
            }
            nodes = node.Nodes;
        }
    }

    private void treeViewCategories_AfterSelect(object sender, TreeViewEventArgs e)
    {
        // Display the settings for the selected category
        string selectedPath = GetFullPath(e.Node);
        DisplaySettings(selectedPath);
    }

    private string GetFullPath(TreeNode node)
    {
        if (node.Parent == null)
            return node.Text;
        else
            return GetFullPath(node.Parent) + "/" + node.Text;
    }

    private void DisplaySettings(string treePath)
    {
        flowLayoutPanelSettings.Controls.Clear();

        if (settingsByCategory.TryGetValue(treePath, out var settingsList))
        {
            foreach (var setting in settingsList)
            {
                var control = controlFactory.CreateControl(
                    setting.Property.Name,
                    setting.Description,
                    setting.Property,
                    setting.Value,
                    (newValue) =>
                    {
                        setting.Value = newValue;
                    },
                    this);

                flowLayoutPanelSettings.Controls.Add(control);
            }
        }
    }

    private void buttonOK_Click(object sender, EventArgs e)
    {
        // Update the owner properties with the values
        foreach (var settingsList in settingsByCategory.Values)
        {
            foreach (var setting in settingsList)
            {
                setting.Property.SetValue(setting.Owner, setting.Value);
            }
        }

        this.DialogResult = DialogResult.OK;
        this.Close();
    }

    private void buttonCancel_Click(object sender, EventArgs e)
    {
        this.DialogResult = DialogResult.Cancel;
        this.Close();
    }
}

