using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Linq.Expressions;

namespace InABox.Core
{
    public class CoreTreeNode : INotifyPropertyChanged
    {

        private CoreTreeNodes _owner;
        
        public ObservableCollection<CoreTreeNode> Children => _owner.GetChilden(_id);
        
        private Guid _id;
        public Guid ID 
        {
            get { return _id; }
            set
            {
                _id = value;
                RaisedOnPropertyChanged("ID");
            }
        }
        
        private Guid _parent;
        public Guid Parent 
        {
            get { return _parent; }
            set
            {
                _parent = value;
                RaisedOnPropertyChanged("Parent");
            }
        }

        private string _description;
        public string Description
        {
            get { return _description; }
            set
            {
                _description = value;
                RaisedOnPropertyChanged("Description");
            }
        }
   
        private object _image;
        public object Image
        {
            get => _image;
            set
            {
                _image = value;
                RaisedOnPropertyChanged("Image");
            }
        }

        public event PropertyChangedEventHandler? PropertyChanged;

        public void RaisedOnPropertyChanged(string propertyName)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }

        public CoreTreeNode(CoreTreeNodes owner)
        {
            _owner = owner;
            _description = "";
        }

        public CoreTreeNode(CoreTreeNodes owner, Guid id, Guid parent) : this(owner)
        {
            _id = id;
            _parent = parent;
        }

        public CoreTreeNode? GetParent() => _owner[_parent];

        public int Index()
        {
            var parent = GetParent();
            return parent != null
                ? parent.Children.IndexOf(this) + 1
                : _owner.Nodes.IndexOf(this) + 1;
        }

        public String Number
        {
            get
            {
                String result = Index().ToString();
                var parent = GetParent();
                while (parent != null)
                {
                    int index = parent.Index();
                    result = $"{index}.{result}";
                    parent = parent.GetParent();
                }
                return result;
            }
        }
        
    }

    public class CoreTreeNodes 
    {

        private List<CoreTreeNode> _nodes;
        public ObservableCollection<CoreTreeNode> Nodes => new ObservableCollection<CoreTreeNode>(_nodes.Where(x => x.Parent == Guid.Empty));
        
        public CoreTreeNode? this[Guid id] => _nodes.FirstOrDefault(x => x.ID == id);
        
        public CoreTreeNodes()
        {
            _nodes = new List<CoreTreeNode>();
        }

        public CoreTreeNode Add(Guid id, Guid parent)
        {
            var node = new CoreTreeNode(this, id, parent);
            _nodes.Add(node);
            return node;
        }
        
        public CoreTreeNode Find(Guid id) => _nodes.FirstOrDefault(x => x.ID == id);

        public void GetChildren(List<Guid> nodes, Guid id)
        {
            nodes.Add(id);
            var children = GetChilden(id);
            foreach (var child in children)
                GetChildren(nodes, child.ID);
        }
        
        public ObservableCollection<CoreTreeNode> GetChilden(Guid id)
        {
            
            return new ObservableCollection<CoreTreeNode>(_nodes.Where(x => x.Parent.Equals(id) && (x.ID != id)));
        }

        public void Load<T>(CoreTable table, Expression<Func<T, Guid>> id, Expression<Func<T, Guid>> parentid, Expression<Func<T, String>> description)
        {
            _nodes.Clear();
            foreach (var row in table.Rows)
            {
                Guid _id = row.Get<T, Guid>(id);
                Guid _parent = row.Get<T, Guid>(parentid);
                String _description = row.Get<T, String>(description);
                Add(_id, _parent).Description = _description;
            }
        }
        
    }
}