using Components.Exceptions;
using System.Reflection;

namespace Components
{
    public partial class UserControlTreeView : UserControl
    {
        public int SelectedIndex
        {
            get
            {
                return treeView.SelectedNode?.Index ?? -1;
            }
            set
            {
                if (value > -1 && value < treeView.Nodes.Count)
                {
                    treeView.SelectedNode = treeView.Nodes[value];
                }
            }
        }
        private List<(string PropertyName, bool AlwaysCreateBranch)> Hierarchy;
        public UserControlTreeView()
        {
            InitializeComponent();
            Hierarchy = [];
        }
        public T GetObjectSelectedNode<T>()
            where T : class, new()
        {
            var node = treeView.SelectedNode;

            if (node == null)
            {
                throw new NotSelectedNodeException();
            }

            if ((Hierarchy?.Count ?? 0) == 0)
            {
                throw new TreeHierarchyNotSetException();
            }

            if (node.Nodes.Count > 0)
            {
                throw new NodeIsNotLeafException();
            }

            T obj = new T();
            int propIndex = GetNodeDepth(node);
            while (node != null)
            {
                object propValue = node.Tag;
                string propName = Hierarchy[propIndex].PropertyName;

                var prop = obj.GetType().GetProperty(propName);
                if (prop == null)
                {
                    throw new PropertyNotDeclaratedException(propName);
                }

                prop.SetValue(obj, propValue);

                node = node.Parent;
                propIndex--;
            }

            return obj;
        }
        private int GetNodeDepth(TreeNode node)
        {
            int depth = 0;
            while (node.Parent != null)
            {
                depth++;
                node = node.Parent;
            }
            return depth;
        }
        public void SetTreeObjects<T>(List<T> objects)
        {
            if (objects.Count == 0)
            {
                return;
            }

            if ((Hierarchy?.Count ?? 0) == 0)
            {
                throw new TreeHierarchyNotSetException();
            }

            PropertyInfo[]? properties = objects[0]!.GetType().GetProperties();
            ClearTreeView();
            foreach (T obj in objects)
            {
                var nodes = treeView.Nodes;
                foreach (var hierarchyProperty in Hierarchy)
                {
                    PropertyInfo? objectPropertyInfo = properties?.Single(prop => prop.Name == hierarchyProperty.PropertyName);
                    if (objectPropertyInfo == null)
                    {
                        throw new PropertyNotDeclaratedException(hierarchyProperty.PropertyName);
                    }
                    object? objectPropertyValue = objectPropertyInfo.GetValue(obj);
                    if (objectPropertyValue == null)
                    {
                        throw new PropertyNullException(hierarchyProperty.PropertyName);
                    }

                    TreeNode? node = null;

                    if (!hierarchyProperty.AlwaysCreateBranch)
                    {
                        foreach (TreeNode childNode in nodes)
                        {
                            if (childNode.Text == objectPropertyValue.ToString())
                            {
                                node = childNode;
                                break;
                            }
                        }
                    }

                    if (node == null)
                    {
                        node = nodes.Add(objectPropertyValue.ToString());
                        node.Tag = objectPropertyValue;
                    }

                    nodes = node.Nodes;
                }
            }
        }
        public void SetHierarchy(List<(string PropertyName, bool AlwaysCreateBranch)> hierarchy)
        {
            Hierarchy = hierarchy;
        }
        public void ClearTreeView()
        {
            treeView.Nodes.Clear();
            treeView.Update();
        }
    }
}