using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Linq.Expressions;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using InABox.Core;
using InABox.WPF;
using NPOI.OpenXmlFormats.Dml.Chart;
using Syncfusion.UI.Xaml.TreeGrid;
using Syncfusion.UI.Xaml.TreeView;
using Syncfusion.Windows.Tools.Controls;

namespace InABox.DynamicGrid
{
    public class DynamicTreeNode : INotifyPropertyChanged
    {

        private DynamicTreeNodes _owner;
        
        public ObservableCollection<DynamicTreeNode> 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 ImageSource? _image;
        public ImageSource? Image
        {
            get { return _image; }
            set
            {
                _image = value;
                RaisedOnPropertyChanged("Image");
            }
        }

        public event PropertyChangedEventHandler? PropertyChanged;

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

        public DynamicTreeNode(DynamicTreeNodes owner)
        {
            _owner = owner;
            _description = "";
        }

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

    public class DynamicTreeNodes 
    {

        private List<DynamicTreeNode> _nodes;
        public ObservableCollection<DynamicTreeNode> Nodes => new ObservableCollection<DynamicTreeNode>(_nodes.Where(x => x.Parent == Guid.Empty));
        
        public DynamicTreeNodes()
        {
            _nodes = new List<DynamicTreeNode>();
        }

        public DynamicTreeNode Add(Guid id, Guid parent)
        {
            var node = new DynamicTreeNode(this, id, parent);
            _nodes.Add(node);
            return node;
        }

        public ObservableCollection<DynamicTreeNode> GetChilden(Guid id)
        {
            return new ObservableCollection<DynamicTreeNode>(_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;
            }
        }
        
    }
    
    public enum DynamicTreeOption
    {
        Add,
        Edit,
        Delete
    }

    public delegate void OnSelectItem(DynamicTreeNode node);

    public delegate void OnContextMenuOpening(DynamicTreeNode node, ContextMenu menu);
    
    public abstract class DynamicTreeView<T> : ContentControl where T : BaseObject, new()
    {

        protected abstract Expression<Func<T, Guid>> ID { get; }
        protected abstract Expression<Func<T, Guid>> ParentID { get; }
        protected abstract Expression<Func<T, String>> Description { get; }

        protected CoreTable Data { get; private set; }

        private ContextMenu _menu;
        private SfTreeGrid _tree;
        private DockPanel _dock;
        private Grid _grid;
        private Button _add;
        private Button _edit;
        private Button _delete;
        private Label _spacer;

        public FluentList<DynamicTreeOption> Options { get; private set; }
        public event OnSelectItem OnSelectItem;
        public event OnContextMenuOpening OnContextMenuOpening;

        private double minRowHeight = 30D;
        private double maxRowHeight = 30D;

        public double MinRowHeight 
        { 
            get => minRowHeight;
            set
            {
                minRowHeight = value;
                CalculateRowHeight();
            }
        }
        public double MaxRowHeight 
        { 
            get => maxRowHeight;
            set
            {
                maxRowHeight = value;
                CalculateRowHeight();
            }
        }

        /*public double RowHeight
        {
            get => _tree.RowHeight;
            set => _tree.RowHeight = value;
        }*/
        
        public DynamicTreeView() : base()
        {
            
            Options = new FluentList<DynamicTreeOption>();
            Options.OnChanged += OptionsChanged;
            
            _grid = new Grid();
            _grid.ColumnDefinitions.Add(new ColumnDefinition() { Width = new GridLength(1D, GridUnitType.Star) });
            _grid.RowDefinitions.Add(new RowDefinition() { Height = new GridLength(1D, GridUnitType.Star) });
            _grid.RowDefinitions.Add(new RowDefinition() { Height = new GridLength(1D, GridUnitType.Auto) });
            
            _tree = new SfTreeGrid();
            _tree.ChildPropertyName = "Children";
            //_tree.ParentPropertyName = "Parent";
            _tree.AutoGenerateColumns = false;
            _tree.AutoExpandMode = AutoExpandMode.AllNodesExpanded;
            //_tree.NodeCollapsing += (o, e) => { e.Cancel = true; };
            _tree.HeaderRowHeight = 0D;
            _tree.SelectionChanged += (o,e) => OnSelectItem?.Invoke((_tree.SelectedItem as DynamicTreeNode)!);
            _tree.AllowSelectionOnExpanderClick = false;

            _menu = new ContextMenu();
            var additem = new MenuItem() { Header = "Add Child Folder" };
            additem.Click += (o, e) => { AddItem((_tree.SelectedItem as DynamicTreeNode)!.ID); };
            _menu.Items.Add(additem);

            _tree.ContextMenuOpening += _tree_ContextMenuOpening;
            _tree.ContextMenu = _menu;
            _tree.Background = new SolidColorBrush(Colors.DimGray);
            
            var cellStyle = new Style(typeof(TreeGridRowControl));
            cellStyle.Setters.Add(new Setter(BackgroundProperty, new SolidColorBrush(Colors.White)));
            _tree.RowStyle = cellStyle;            
            
            _tree.SelectionBackground = new SolidColorBrush(System.Windows.Media.Color.FromArgb(0xFF, 0x11, 0x9E, 0xD9));
            _tree.Columns.Add(new TreeGridTextColumn()
                {
                    MappingName = "Description"
                }
            );
            _tree.ColumnSizer = TreeColumnSizer.Star;
            _tree.RowHeight = 30D;
            _tree.SetValue(Grid.RowProperty, 0);
            _grid.Children.Add(_tree);
            
            _dock = new DockPanel();
            _dock.SetValue(Grid.RowProperty, 1);
            _grid.Children.Add(_dock);

            _add = CreateButton(Properties.Resources.add.AsBitmapImage(System.Drawing.Color.White), "", "Add Item", (o) => AddItem(Guid.Empty));
            _add.Margin = new Thickness(0, 2, 2, 0);
            _add.Visibility = Visibility.Collapsed;
            _add.SetValue(DockPanel.DockProperty, Dock.Left);
            _dock.Children.Add(_add);
            
            _edit = CreateButton(Properties.Resources.pencil.AsBitmapImage(System.Drawing.Color.White), "", "Edit Item", EditItem);
            _edit.Margin = new Thickness(0, 2, 2, 0);
            _edit.Visibility = Visibility.Collapsed;
            _edit.SetValue(DockPanel.DockProperty, Dock.Left);
            _dock.Children.Add(_edit);

            _delete = CreateButton(Properties.Resources.delete.AsBitmapImage(System.Drawing.Color.White), "", "Delete Item", DeleteItem);
            _delete.Margin = new Thickness(2, 2, 0, 0);
            _delete.Visibility = Visibility.Collapsed;
            _delete.SetValue(DockPanel.DockProperty, Dock.Right);
            _dock.Children.Add(_delete);
            
            _spacer = new Label();
            _spacer.SetValue(DockPanel.DockProperty, Dock.Left);
            _dock.Children.Add(_spacer);

            Content = _grid;

            SizeChanged += DynamicTreeView_SizeChanged;
        }

        #region Public Interface

        public void AddItem(DynamicTreeNode? parentNode = null, bool edit = true)
        {
            var id = parentNode?.ID ?? Guid.Empty;

            try
            {
                T item = DoCreateItem(id);
                if (edit)
                {
                    if (DoEditItem(item))
                    {
                        DoSaveItem(item);
                        Refresh();
                    }
                }
                else
                {
                    DoSaveItem(item);
                    Refresh();
                }
            }
            catch (Exception e)
            {
                MessageBox.Show(e.Message);
            }
        }

        #endregion

        private void _tree_ContextMenuOpening(object sender, ContextMenuEventArgs e)
        {
            _menu.Items.Clear();
            if (OnContextMenuOpening is not null)
            {
                OnContextMenuOpening.Invoke((_tree.SelectedItem as DynamicTreeNode)!, _menu);
                if(_menu.Items.Count == 0)
                {
                    e.Handled = true;
                }
            }
            else
            {
                _menu.AddItem("Add Item", null, (_tree.SelectedItem as DynamicTreeNode)!.ID, AddItem);
            }
        }

        private void DynamicTreeView_SizeChanged(object sender, SizeChangedEventArgs e)
        {
            CalculateRowHeight();
        }

        private void CalculateRowHeight()
        {
            if(Data != null && Data.Rows.Count > 0)
            {
                var contentHeight = _tree.ActualHeight - (_tree.Padding.Top + _tree.Padding.Bottom) - 2; // Two extra pixels of space
                var targetHeight = contentHeight / Data.Rows.Count;
                _tree.RowHeight = Math.Max(Math.Min(targetHeight, MaxRowHeight), MinRowHeight);
            }
        }
        
        private Button CreateButton(BitmapImage? image = null, string? text = null, string?  tooltip = null, Action<Button>? action = null)
        {
            var button = new Button();
            button.SetValue(BorderBrushProperty, new SolidColorBrush(Colors.Gray));
            button.SetValue(BorderThicknessProperty, new Thickness(0.75));
            button.Height = 30;
            button.MinWidth = 30;
            button.Click += (o, e) => action?.Invoke(button);
            UpdateButton(button, image, text, tooltip);
            return button;
        }

        protected void UpdateButton(Button button, BitmapImage? image, string? text, string? tooltip = null)
        {
            var stackPnl = new StackPanel();
            stackPnl.Orientation = Orientation.Horizontal;

            if (image != null)
            {
                var img = new Image();
                img.Source = image;
                img.Margin = new Thickness(2);
                img.ToolTip = tooltip;
                stackPnl.Children.Add(img);
            }

            if (!string.IsNullOrEmpty(text))
            {
                var lbl = new Label();
                lbl.Content = text;
                lbl.VerticalAlignment = VerticalAlignment.Stretch;
                lbl.VerticalContentAlignment = VerticalAlignment.Center;
                lbl.Margin = new Thickness(2, 0, 5, 0);
                lbl.ToolTip = ToolTip;
                stackPnl.Children.Add(lbl);
            }

            button.Content = stackPnl;
            button.ToolTip = tooltip;
        }
        
        private void OptionsChanged(object sender, EventArgs args)
        {
            _add.Visibility = Options.Contains(DynamicTreeOption.Add) ? Visibility.Visible : Visibility.Collapsed;
            _edit.Visibility = Options.Contains(DynamicTreeOption.Edit) ? Visibility.Visible : Visibility.Collapsed;
            _delete.Visibility = Options.Contains(DynamicTreeOption.Delete) ? Visibility.Visible : Visibility.Collapsed;
        }

        protected virtual T DoCreateItem(Guid parent)
        {
            T result = new T();
            CoreUtils.SetPropertyValue(result, CoreUtils.GetFullPropertyName(ParentID, "."), parent);
            return result;
        }

        protected abstract T? DoLoadItem(Guid id);

        protected virtual bool DoEditItem(T item)
        {
            var form = new DynamicEditorForm(typeof(T));
            form.Items = new T[] { item };
            return form.ShowDialog() == true;
        }

        protected abstract void DoSaveItem(T item);

        protected abstract bool DoDeleteItem(Guid id);

        private void AddItem(Guid id)
        {
            try
            {
                T item = DoCreateItem(id);
                if (DoEditItem(item))
                {
                    DoSaveItem(item);
                    Refresh();
                }
            }
            catch (Exception e)
            {
                MessageBox.Show(e.Message);
            }
        }
        
        private void EditItem(Button button)
        {
            var node = _tree.SelectedItem as DynamicTreeNode;
            if (node == null)
            {
                MessageBox.Show("Please Select an item to edit!");
                return;
            }
            var item = DoLoadItem(node.ID);
            if (item != null && DoEditItem(item))
            {
                DoSaveItem(item);
                Refresh();
            }
        }

        private  void DeleteItem(Button button)
        {
            var node = _tree.SelectedItem as DynamicTreeNode;
            if (node == null)
            {
                MessageBox.Show("Please Select an item to edit!");
                return;
            }

            if (DoDeleteItem(node.ID))
            {
                Refresh();
            }
        }
        
        public DynamicTreeNodes Nodes { get; set; }

        protected abstract void DoRefresh(Action<CoreTable?, Exception?> action);

        private void AfterRefresh()
        {
            var nodes = new DynamicTreeNodes();
            foreach (var row in Data.Rows)
            {
                var _id = row.Get(ID);
                var _parent = row.Get(ParentID);
                var _description = row.Get(Description);
                nodes.Add(_id, _parent).Description = _description;
            }
            Nodes = nodes;
            _tree.ItemsSource = nodes.Nodes;
            CalculateRowHeight();
        }

        public void Refresh()
        {
            DoRefresh((table, exception) =>
            {
                if(exception != null)
                {
                    Dispatcher.Invoke(() =>
                    {
                        MessageBox.Show(String.Format("Error: {0}", exception.Message));
                    });
                }
                else if(table is not null)
                {
                    Data = table;
                    Dispatcher.Invoke(() =>
                    {
                        AfterRefresh();
                    });
                }
            });
        }


    }
}