dorisoy_HardwareMonitor/LibreHardwareMonitor/UI/Node.cs
// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
// Copyright (C) LibreHardwareMonitor and Contributors.
// Partial Copyright (C) Michael Möller <mmoeller@openhardwaremonitor.org> and Contributors.
// All Rights Reserved.

using System;
using System.Collections.ObjectModel;
using System.Drawing;

namespace LibreHardwareMonitor.UI
{
    public class Node
    {
        private Node _parent;
        private readonly NodeCollection _nodes;
        private string _text;
        private bool _visible;

        public delegate void NodeEventHandler(Node node);
        public event NodeEventHandler IsVisibleChanged;
        public event NodeEventHandler NodeAdded;
        public event NodeEventHandler NodeRemoved;

        private TreeModel RootTreeModel()
        {
            Node node = this;
            while (node != null)
            {
                if (node.Model != null)
                    return node.Model;
                node = node._parent;
            }
            return null;
        }

        public Node() : this(string.Empty) { }

        public Node(string text)
        {
            _text = text;
            _nodes = new NodeCollection(this);
            _visible = true;
        }

        public TreeModel Model { get; set; }

        public Node Parent
        {
            get { return _parent; }
            set
            {
                if (value != _parent)
                {
                    _parent?._nodes.Remove(this);
                    value?._nodes.Add(this);
                }
            }
        }

        public Collection<Node> Nodes
        {
            get { return _nodes; }
        }

        public virtual string Text
        {
            get { return _text; }
            set
            {
                _text = value;
            }
        }

        public virtual string ToolTip { get; }

        public Image Image { get; set; }

        public virtual bool IsVisible
        {
            get { return _visible; }
            set
            {
                if (value != _visible)
                {
                    _visible = value;
                    TreeModel model = RootTreeModel();
                    if (model != null && _parent != null)
                    {
                        int index = 0;
                        for (int i = 0; i < _parent._nodes.Count; i++)
                        {
                            Node node = _parent._nodes[i];
                            if (node == this)
                                break;
                            if (node.IsVisible || model.ForceVisible)
                                index++;
                        }
                        if (model.ForceVisible)
                        {
                            model.OnNodeChanged(_parent, index, this);
                        }
                        else
                        {
                            if (value)
                                model.OnNodeInserted(_parent, index, this);
                            else
                                model.OnNodeRemoved(_parent, index, this);
                        }
                    }
                    IsVisibleChanged?.Invoke(this);
                }
            }
        }

        private class NodeCollection : Collection<Node>
        {
            private readonly Node _owner;

            public NodeCollection(Node owner)
            {
                _owner = owner;
            }

            protected override void ClearItems()
            {
                while (Count != 0)
                    RemoveAt(Count - 1);
            }

            protected override void InsertItem(int index, Node item)
            {
                if (item == null)
                    throw new ArgumentNullException(nameof(item));

                if (item._parent != _owner)
                {
                    item._parent?._nodes.Remove(item);
                    item._parent = _owner;
                    base.InsertItem(index, item);

                    TreeModel model = _owner.RootTreeModel();
                    model?.OnStructureChanged(_owner);
                    _owner.NodeAdded?.Invoke(item);
                }
            }

            protected override void RemoveItem(int index)
            {
                Node item = this[index];
                item._parent = null;
                base.RemoveItem(index);

                TreeModel model = _owner.RootTreeModel();
                model?.OnStructureChanged(_owner);
                _owner.NodeRemoved?.Invoke(item);
            }

            protected override void SetItem(int index, Node item)
            {
                if (item == null)
                    throw new ArgumentNullException(nameof(item));

                RemoveAt(index);
                InsertItem(index, item);
            }
        }
    }
}
