using InABox.Clients;
using InABox.Core;
using InABox.Wpf;
using InABox.WPF;
using Syncfusion.Data;
using Syncfusion.UI.Xaml.Grid;
using Syncfusion.UI.Xaml.ScrollAxis;
using Syncfusion.UI.Xaml.TreeGrid;
using Syncfusion.UI.Xaml.TreeGrid.Helpers;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using Syncfusion.UI.Xaml.TreeGrid.Filtering;
using Syncfusion.UI.Xaml.TreeGrid.Cells;

namespace InABox.DynamicGrid;

public enum DynamicTreeGridLines
{
    Both,
    Horizontal,
    Vertical,
    None
}

public enum DynamicTreeGridExpandMode
{
    All,
    Root,
    None
}

public class DynamicGridTreeUIComponent<T> : IDynamicGridUIComponent<T>, IDynamicGridGridUIComponent<T>
    where T : BaseObject, new()
{
    private IDynamicGridUIComponentParent<T> _parent;
    public IDynamicGridUIComponentParent<T> Parent
    {
        get => _parent;
        set
        {
            _parent = value;

            CellBackgroundConverter = new DynamicGridCellStyleConverter<System.Windows.Media.Brush?>(Parent, GetCellBackground);
            CellForegroundConverter = new DynamicGridCellStyleConverter<System.Windows.Media.Brush?>(Parent, GetCellForeground);
            CellFontSizeConverter = new DynamicGridCellStyleConverter<double?>(Parent, GetCellFontSize);
            CellFontStyleConverter = new DynamicGridCellStyleConverter<System.Windows.FontStyle?>(Parent, GetCellFontStyle);
            CellFontWeightConverter = new DynamicGridCellStyleConverter<System.Windows.FontWeight?>(Parent, GetCellFontWeight);

            Parent.AddHiddenColumn(IDColumn.Property);
            Parent.AddHiddenColumn(ParentColumn.Property);
        }
    }

    private Column<T> IDColumn;
    private Column<T> ParentColumn;

    private ContextMenu _menu;
    private SfTreeGrid _tree;
    private readonly ContextMenu ColumnsMenu;

    public event OnContextMenuOpening OnContextMenuOpening;

    FrameworkElement IDynamicGridUIComponent<T>.Control => _tree;

    private bool _shownumbers = false;
    public bool ShowNumbers
    {
        get => _shownumbers;
        set
        {
            _shownumbers = value;
            //_tree.Columns[1].Width = value ? 50 : 0;
        } 
    }

    private bool _showHeader = false;
    public bool ShowHeader
    {
        get => _showHeader;
        set
        {
            _showHeader = value;
            _tree.HeaderRowHeight = value ? 30 : 0;
        }
    }

    private bool _autoSizeExpander = false;
    public bool AutoSizeExpander
    {
        get => _autoSizeExpander;
        set
        {
            _autoSizeExpander = value;
            _tree.AllowAutoSizingExpanderColumn = value;
        }
    }

    private DynamicTreeGridLines _gridLines = DynamicTreeGridLines.Both;
    public DynamicTreeGridLines GridLines
    {
        get => _gridLines;
        set
        {
            _gridLines = value;
            _tree.GridLinesVisibility = value switch
            {
                DynamicTreeGridLines.Both => GridLinesVisibility.Both,
                DynamicTreeGridLines.Vertical => GridLinesVisibility.Vertical,
                DynamicTreeGridLines.Horizontal => GridLinesVisibility.Horizontal,
                _ => GridLinesVisibility.None,
            };
        }
    }

    public DynamicTreeGridExpandMode ExpandMode
    {
        get
        {
            return _tree.AutoExpandMode switch
            {
                AutoExpandMode.AllNodesExpanded => DynamicTreeGridExpandMode.All,
                AutoExpandMode.RootNodesExpanded => DynamicTreeGridExpandMode.Root,
                _ => DynamicTreeGridExpandMode.None,
            };
        }
        set
        {
            _tree.AutoExpandMode = value switch
            {
                DynamicTreeGridExpandMode.All => AutoExpandMode.AllNodesExpanded,
                DynamicTreeGridExpandMode.Root => AutoExpandMode.RootNodesExpanded,
                _ => AutoExpandMode.None
            };
        }
    }

    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();
        }
    }

    #region IDynamicGridGridUIComponent

    IList<DynamicColumnBase> IDynamicGridGridUIComponent<T>.ColumnList => ColumnList;

    int IDynamicGridGridUIComponent<T>.RowHeight => (int)RowHeight;

    #endregion

    private DynamicGridCellStyleConverter<System.Windows.Media.Brush?> CellBackgroundConverter;
    private DynamicGridCellStyleConverter<System.Windows.Media.Brush?> CellForegroundConverter;
    private DynamicGridCellStyleConverter<double?> CellFontSizeConverter;
    private DynamicGridCellStyleConverter<System.Windows.FontStyle?> CellFontStyleConverter;
    private DynamicGridCellStyleConverter<System.Windows.FontWeight?> CellFontWeightConverter;

    protected virtual Brush? GetCellBackground(CoreRow row, DynamicColumnBase column) => null;
    protected virtual Brush? GetCellForeground(CoreRow row, DynamicColumnBase column) => null;
    protected virtual double? GetCellFontSize(CoreRow row, DynamicColumnBase column) => null;
    protected virtual FontStyle? GetCellFontStyle(CoreRow row, DynamicColumnBase column) => null;
    protected virtual FontWeight? GetCellFontWeight(CoreRow row, DynamicColumnBase column) => null;


    public DynamicGridTreeUIComponent(Expression<Func<T, Guid>> idColumn, Expression<Func<T, Guid>> parentIDColumn)
    {
        IDColumn = new Column<T>(CoreUtils.GetFullPropertyName(idColumn, "."));
        ParentColumn = new Column<T>(CoreUtils.GetFullPropertyName(parentIDColumn, "."));

        ColumnsMenu = new ContextMenu();
        ColumnsMenu.Opened += ColumnsMenu_ContextMenuOpening;

        _tree = new SfTreeGrid();
        _tree.ChildPropertyName = "Children";
        //_tree.ParentPropertyName = "Parent";
        _tree.AutoGenerateColumns = false;
        ExpandMode = DynamicTreeGridExpandMode.All;
        //_tree.NodeCollapsing += (o, e) => { e.Cancel = true; };
        //_tree.HeaderRowHeight = 0D;
        _tree.HeaderRowHeight = 30;
        _tree.HeaderContextMenu = ColumnsMenu;

        _tree.SelectionChanging += _tree_SelectionChanging;
        _tree.SelectionChanged += _tree_SelectionChanged;
        _tree.AllowSelectionOnExpanderClick = false;

        _tree.AllowAutoSizingExpanderColumn = false;

        _tree.CellTapped += _tree_CellTapped;
        _tree.CellDoubleTapped += _tree_CellDoubleTapped;
        _tree.KeyUp += _tree_KeyUp;

        _tree.CellToolTipOpening += _tree_CellToolTipOpening;

        _menu = new ContextMenu();
        var additem = new MenuItem() { Header = "Add Child Folder" };
        additem.Click += (o, e) => { DoAddItem((_tree.SelectedItem as CoreTreeNode)!.ID, true); };
        _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(Control.BackgroundProperty, new SolidColorBrush(Colors.White)));
        _tree.RowStyle = cellStyle;

        var filterstyle = new Style(typeof(TreeGridFilterControl));
        filterstyle.Setters.Add(new Setter(TreeGridFilterControl.SortOptionVisibilityProperty, Visibility.Collapsed));
        filterstyle.Setters.Add(new Setter(TreeGridFilterControl.ImmediateUpdateColumnFilterProperty, false));
        _tree.FilterPopupStyle = filterstyle;
        
        _tree.FilterChanged += _tree_FilterChanged;
        _tree.FilterItemsPopulating += _tree_FilterItemsPopulating;
        _tree.FilterLevel = FilterLevel.Extended;
        _tree.SelectionForeground = DynamicGridUtils.SelectionForeground;
        _tree.SelectionBackground = DynamicGridUtils.SelectionBackground;
        
        _tree.ColumnSizer = TreeColumnSizer.None;
        _tree.RowHeight = 30D;
        _tree.SetValue(Grid.RowProperty, 0);

        _tree.SetValue(ScrollViewer.VerticalScrollBarVisibilityProperty, ScrollBarVisibility.Visible);

        _tree.AllowDraggingRows = false;
        _tree.Drop += _tree_Drop;
        _tree.DragOver += _tree_DragOver;
        _tree.RowDragDropTemplate = TemplateGenerator.CreateDataTemplate(() =>
        {
            var border = new Border();
            border.Width = 100;
            border.Height = 100;
            border.BorderBrush = new SolidColorBrush(Colors.Firebrick);
            border.Background = new SolidColorBrush(Colors.Red);
            border.CornerRadius = new CornerRadius(5);
            return border;
        });

        _tree.SizeChanged += _tree_SizeChanged;
    }

    #region Public Interface

    public IEnumerable<CoreRow> GetChildren(Guid id)
    {
        return Nodes.GetChildren(id).Select(x => MapRow(x.Row)).NotNull();
    }

    #endregion

    #region Input

    private CoreRow? GetRowFromIndex(int rowIndex)
    {
        // Syncfusion has given us the row index, so it also will give us the correct row, after sorting.
        // Hence, here we use the syncfusion DataGrid.GetRecordAtRowIndex, which *should* always return a DataRowView.
        var row = _tree.GetNodeAtRowIndex(rowIndex);
        return MapRow((row.Item as CoreTreeNode)?.Row);
    }

    private void _tree_CellDoubleTapped(object? sender, TreeGridCellDoubleTappedEventArgs e)
    {
        _tree.Dispatcher.BeginInvoke(() =>
        {
            // This needs to happen outside the event handler, because the items source for the tree view might change during this method, and that causes an internal exception in Syncfusion. We need to finish the event before resetting the items source.
            Parent.DoubleClickCell(GetRowFromIndex(e.RowColumnIndex.RowIndex), GetColumn(e.RowColumnIndex.ColumnIndex));
        });
    }

    private void _tree_KeyUp(object sender, System.Windows.Input.KeyEventArgs e)
    {
        if (sender != _tree) return;

        Parent.HandleKey(e);
    }

    private void HeaderCell_LeftMouseButtonEvent(object sender, MouseButtonEventArgs e)
    {
        if (sender is not TreeGridHeaderCell header) return;

        var index = _tree.Columns.IndexOf(header.Column);
        if (GetColumn(index) is not DynamicColumnBase column)
            return;

        if(column is DynamicActionColumn dac)
        {
            Parent.ExecuteActionColumn(dac, null);
        }
    }

    private void _tree_CellTapped(object? sender, TreeGridCellTappedEventArgs e)
    {
        if (!_tree.IsEnabled)
            return;

        if (GetColumn(e.RowColumnIndex.ColumnIndex) is not DynamicColumnBase column)
            return;

        if(e.ChangedButton == MouseButton.Left)
        {
            if(column is DynamicActionColumn dac)
            {
                Parent.ExecuteActionColumn(dac, SelectedRows);
            }
        }
        else if(e.ChangedButton == MouseButton.Right)
        {
            if(column is DynamicMenuColumn dmc)
            {
                Parent.ExecuteActionColumn(dmc, null);
            }
            else
            {
                Parent.OpenColumnMenu(column);
            }
        }
    }

    #endregion

    #region Selection

    public CoreRow[] SelectedRows
    {
        get
        {
            return _tree.SelectedItems.OfType<CoreTreeNode>()
                .Select(x => GetRow(x)).NotNull().ToArray();
        }
        set
        {
            _tree.SelectedItems.Clear();
            foreach (var row in value)
            {
                _tree.SelectedItems.Add(Nodes.Find(row.Get<Guid>(IDColumn.Property)));
            }
        }
    }

    private void _tree_SelectionChanged(object? sender, GridSelectionChangedEventArgs e)
    {
        if(Parent.IsReady && !Parent.IsRefreshing)
        {
            Parent.SelectItems(SelectedRows);
        }
    }

    private void _tree_SelectionChanging(object? sender, GridSelectionChangingEventArgs e)
    {
        var cancel = new CancelEventArgs();
        Parent.BeforeSelection(cancel);
        if (cancel.Cancel)
        {
            e.Cancel = true;
        }
    }

    #endregion

    private void _tree_FilterItemsPopulating(object? sender, Syncfusion.UI.Xaml.TreeGrid.Filtering.TreeGridFilterItemsPopulatingEventArgs e)
    {
        var colIdx = _tree.Columns.IndexOf(e.Column);
        var column = GetColumn(colIdx);
        if(column is not null)
        {
            var filterItems = Parent.GetColumnFilterItems(column);
            if(filterItems is not null)
            {
                e.ItemsSource = filterItems.Select(x => new FilterElement
                {
                    DisplayText = x, ActualValue = x
                });
            }
            else if (column is DynamicActionColumn dac && dac.Filters is not null)
            {
                e.ItemsSource = dac.Filters.Select(x => new FilterElement
                {
                    DisplayText = x, ActualValue = x,
                    IsSelected = (dac.SelectedFilters is null || dac.SelectedFilters.Contains(x))
                        && (dac.ExcludeFilters is null || dac.ExcludeFilters.Contains(x))
                });
            }
            else if (column is DynamicGridColumn dgc)
            {
                var preds = e.Column.FilterPredicates.Select(x => x.FilterValue).ToArray();
                var data = Parent.Data.Rows.Select(r => r.Get<String>(dgc.ColumnName)).OrderBy(x=>x);
                
                e.ItemsSource = data.Select(x => new FilterElement()
                    { DisplayText = x ?? "(Blanks)", ActualValue = x,  IsSelected = preds.Length == 0 || preds.Contains(x) });
            }
        }
    }

    private void _tree_FilterChanged(object? sender, Syncfusion.UI.Xaml.TreeGrid.Filtering.TreeGridFilterChangedEventArgs e)
    {
        var col = _tree.Columns.IndexOf(e.Column);
        if (GetColumn(col) is DynamicActionColumn column)
        {
            if (e.FilterPredicates != null)
            {
                var filter = e.FilterPredicates.Select(x => x.FilterValue.ToString()!).ToArray();
                var include = e.FilterPredicates.Any(x => x.FilterType == FilterType.Equals);
                if (include)
                {
                    column.SelectedFilters = filter;
                    column.ExcludeFilters = null;
                }
                else if(column.Filters is not null)
                {
                    column.SelectedFilters = column.Filters.Except(filter).ToArray();
                    column.ExcludeFilters = null;
                }
                else
                {
                    column.SelectedFilters = null;
                    column.ExcludeFilters = filter;
                }
            }
            else
                column.SelectedFilters = Array.Empty<string>();
            _tree.ClearFilter(e.Column);
            //e.FilterPredicates?.Clear();
            //e.FilterPredicates?.Add(new FilterPredicate() { PredicateType = PredicateType.Or, FilterBehavior = Syncfusion.Data.FilterBehavior.StringTyped, FilterMode = ColumnFilter.DisplayText, FilterType = Syncfusion.Data.FilterType.NotEquals, FilterValue = "" });
            //e.FilterPredicates?.Add(new FilterPredicate() { PredicateType = PredicateType.Or, FilterBehavior = Syncfusion.Data.FilterBehavior.StringTyped, FilterMode = ColumnFilter.DisplayText, FilterType = Syncfusion.Data.FilterType.Equals, FilterValue = "" });
            Parent.Refresh(false, false);
        }

        if (e.FilterPredicates == null)
        {
            if (FilterPredicates.ContainsKey(e.Column.MappingName))
                FilterPredicates.Remove(e.Column.MappingName);
        }
        else
        {
            FilterPredicates[e.Column.MappingName] = Serialization.Serialize(e.FilterPredicates, true);
        }
        
        Parent.UIFilterChanged(this);

        UpdateRecordCount();
    }

    private CoreRow? GetRow(CoreTreeNode? node)
    {
        return MapRow(node?.Row);
    }

    private CoreRow? MapRow(CoreRow? row)
    {
        if (row is null) return null;

        var index = row.Index;
        if (index < 0 || index >= Parent.Data.Rows.Count) return null;

        return Parent.Data.Rows[row.Index];
    }

    private void ColumnsMenu_ContextMenuOpening(object sender, RoutedEventArgs e)
    {
        if (sender is not ContextMenu menu) return;
        menu.Items.Clear();

        Parent.LoadColumnsMenu(menu);
    }

    public bool OptionsChanged()
    {
        ColumnsMenu.Visibility = Parent.Options.SelectColumns ? Visibility.Visible : Visibility.Hidden;

        _tree.AllowFiltering = Parent.Options.FilterRows;

        if (Parent.Options.DragSource)
        {
            if (!_tree.AllowDraggingRows)
            {
                _tree.AllowDraggingRows = true;
                _tree.RowDragDropController.DragStart += RowDragDropController_DragStart;
            }
        }
        else
        {
            if (_tree.AllowDraggingRows)
            {
                _tree.AllowDraggingRows = false;
                _tree.RowDragDropController.DragStart -= RowDragDropController_DragStart;
            }
        }
        
        _tree.AllowDrop = Parent.Options.DragTarget;
        _tree.SelectionMode = Parent.Options.MultiSelect ? GridSelectionMode.Extended : GridSelectionMode.Single;

        return false;
    }

    private void _tree_CellToolTipOpening(object? sender, TreeGridCellToolTipOpeningEventArgs e)
    {
        if (GetColumn(e.RowColumnIndex.ColumnIndex) is not DynamicActionColumn col)
            return;
        var toolTip = col.ToolTip;
        if (toolTip is null)
            return;

        var row = GetRowFromIndex(e.RowColumnIndex.RowIndex);

        e.ToolTip.Template = TemplateGenerator.CreateControlTemplate(
            typeof(ToolTip),
            () => toolTip.Invoke(col, row)
        );
    }

    #region Sizing

    public double RowHeight
    {
        get => _tree.RowHeight;
        set => _tree.RowHeight = value;
    }
    public double HeaderRowHeight
    {
        get => _tree.HeaderRowHeight;
        set => _tree.HeaderRowHeight = value;
    }

    private void _tree_SizeChanged(object sender, SizeChangedEventArgs e)
    {
        CalculateRowHeight();
        if (Parent.IsReady && !Parent.IsRefreshing)
            ResizeColumns(_tree, e.NewSize.Width - 2, e.NewSize.Height - 2);
    }

    int IDynamicGridUIComponent<T>.DesiredWidth()
    {
        return this.DesiredWidth();
    }

    #endregion

    #region Context Menu

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

    #endregion

    #region CRUD

    protected T DoCreateItem(Guid parent)
    {
        var result = Parent.CreateItem();
        CoreUtils.SetPropertyValue(result, ParentColumn.Property, parent);
        return result;
    }
    
    protected void DoAddItem(Guid id, bool edit)
    {
        try
        {
            var item = DoCreateItem(id);
            if (edit)
            {
                if (Parent.EditItems(new[] { item }))
                {
                    Parent.DoChanged();
                    Parent.Refresh(false, true);
                }
            }
            else
            {
                Parent.SaveItem(item);
                Parent.DoChanged();
                Parent.Refresh(false, true);
            }
        }
        catch (Exception e)
        {
            MessageWindow.ShowError("An error occurred while adding an item", e);
        }
    }

    #endregion

    #region Columns

    private class StackedHeaderRenderer : TreeGridStackedHeaderCellRenderer
    {
        private Style Style;

        public StackedHeaderRenderer()
        {
            var headstyle = new Style(typeof(TreeGridStackedHeaderCell));
            headstyle.Setters.Add(new Setter(Control.BackgroundProperty, new SolidColorBrush(Colors.Gainsboro)));
            headstyle.Setters.Add(new Setter(Control.ForegroundProperty, new SolidColorBrush(Colors.Black)));
            headstyle.Setters.Add(new Setter(Control.FontSizeProperty, 12D));
            headstyle.Setters.Add(new Setter(Control.BorderThicknessProperty, new Thickness(0.0, 0.0, 0, 0)));
            headstyle.Setters.Add(new Setter(Control.MarginProperty, new Thickness(0, 0, 1, 1)));

            Style = headstyle;
        }

        public override void OnInitializeEditElement(TreeDataColumnBase dataColumn, TreeGridStackedHeaderCell uiElement, object dataContext)
        {
            uiElement.Style = Style;
            base.OnInitializeEditElement(dataColumn, uiElement, dataContext);
        }
    }

    private readonly List<DynamicColumnBase> ColumnList = new();

    private List<DynamicActionColumn> ActionColumns = new();

    private readonly Dictionary<string, string> FilterPredicates = new();

    private DynamicColumnBase? GetColumn(int index) =>
        index >= 0 && index < ColumnList.Count ? ColumnList[index] : null;

    private void ApplyFilterStyle(TreeGridColumn column, bool filtering, bool isactioncolumn)
    {
        var filterstyle = new Style();
        if (filtering)
        {
            filterstyle.Setters.Add(new Setter(Control.BackgroundProperty, DynamicGridUtils.FilterBackground));
            column.ImmediateUpdateColumnFilter = true;
            column.ColumnFilter = ColumnFilter.DisplayText;
            column.AllowBlankFilters = true;
            column.AllowSorting = isactioncolumn
                ? false
                : Parent.CanSort();
        }
        else
        {
            filterstyle.Setters.Add(new Setter(Control.BackgroundProperty, new SolidColorBrush(Colors.Gainsboro)));
            filterstyle.Setters.Add(new Setter(Control.IsEnabledProperty, false));
            column.ColumnFilter = ColumnFilter.Value;
            column.AllowFiltering = false;
            column.AllowSorting = false;
        }
    }

    public class TemplateColumnSelector(DynamicGridTreeUIComponent<T> parent, Func<CoreRow, FrameworkElement?> dataTemplate) : DataTemplateSelector
    {
        public Func<CoreRow, FrameworkElement?> DataTemplate { get; init; } = dataTemplate;

        public DynamicGridTreeUIComponent<T> Parent { get; init; } = parent;

        public override DataTemplate? SelectTemplate(object item, DependencyObject container)
        {
            if (item is not CoreTreeNode node) return null;

            var row = Parent.MapRow(node.Row);

            if (row is null) return null;

            return TemplateGenerator.CreateDataTemplate(() =>
            {
                return DataTemplate(row);
            });
        }
    }

    private void LoadActionColumns(DynamicActionColumnPosition position)
    {
        for (var i = 0; i < ActionColumns.Count; i++)
        {
            var column = ActionColumns[i];
            if (column.Position == position)
            {
                var sColName = $"[_ActionColumn{i}]";

                if (column is DynamicImageColumn imgcol)
                {
                    var newcol = new TreeGridTemplateColumn();

                    newcol.CellTemplateSelector = new TemplateColumnSelector(this, row =>
                    {
                        var image = new Image
                        {
                            Width = _tree.RowHeight - 8,
                            Height = _tree.RowHeight - 8,
                        };
                        image.SetBinding(Image.SourceProperty, new Binding(sColName));
                        return image;
                    });

                    newcol.AllowEditing = false;
                    newcol.UpdateTrigger = UpdateSourceTrigger.PropertyChanged;
                    newcol.Width = column.Width == 0 ? _tree.RowHeight : column.Width;
                    newcol.Padding = new Thickness(4);
                    newcol.ColumnSizer = TreeColumnSizer.None;
                    newcol.HeaderText = column.HeaderText;
                    newcol.MappingName = sColName;

                    ApplyFilterStyle(newcol, false, true);

                    newcol.ShowToolTip = column.ToolTip != null;
                    newcol.ShowHeaderToolTip = column.ToolTip != null;

                    var headstyle = new Style(typeof(TreeGridHeaderCell));
                    headstyle.Setters.Add(new Setter(Control.BackgroundProperty, new SolidColorBrush(Colors.Gainsboro)));
                    headstyle.Setters.Add(new Setter(Control.ForegroundProperty, new SolidColorBrush(Colors.Black)));
                    headstyle.Setters.Add(new Setter(Control.FontSizeProperty, 12D));
                    headstyle.Setters.Add(new EventSetter(Control.MouseLeftButtonUpEvent, new MouseButtonEventHandler(HeaderCell_LeftMouseButtonEvent)));
                    if (!string.IsNullOrWhiteSpace(column.HeaderText))
                    {
                        //headstyle.Setters.Add(new Setter(LayoutTransformProperty, new RotateTransform(270.0F)));
                        headstyle.Setters.Add(new Setter(Control.BorderThicknessProperty, new Thickness(0.0, 0.0, 0, 0)));
                        headstyle.Setters.Add(new Setter(Control.MarginProperty, new Thickness(0, 0, 1, 1)));
                        if (imgcol.VerticalHeader)
                            headstyle.Setters.Add(new Setter(Control.TemplateProperty,
                                Application.Current.Resources["VerticalColumnHeader"] as ControlTemplate));
                    }
                    else
                    {
                        var image = imgcol.Image?.Invoke(null);
                        if (image != null)
                        {
                            var template = new DataTemplate(typeof(TreeGridHeaderCell));
                            var border = new FrameworkElementFactory(typeof(Border));
                            border.SetValue(Border.BackgroundProperty, new SolidColorBrush(Colors.Gainsboro));
                            border.SetValue(Border.PaddingProperty, new Thickness(4));
                            border.SetValue(Control.MarginProperty, new Thickness(0, 0, 1, 1));
                            var img = new FrameworkElementFactory(typeof(Image));
                            img.SetValue(Image.SourceProperty, image);
                            border.AppendChild(img);
                            template.VisualTree = border;
                            headstyle.Setters.Add(new Setter(TreeGridHeaderCell.PaddingProperty, new Thickness(0)));
                            headstyle.Setters.Add(new Setter(TreeGridHeaderCell.ContentTemplateProperty, template));
                        }
                    }

                    newcol.HeaderStyle = headstyle;

                    _tree.Columns.Add(newcol);
                    ColumnList.Add(column);
                }
                else if (column is DynamicTextColumn txtCol)
                {
                    var newcol = new TreeGridTextColumn();
                    newcol.TextWrapping = TextWrapping.NoWrap;

                    newcol.TextAlignment = txtCol.Alignment == Alignment.NotSet
                        ? TextAlignment.Left
                        : txtCol.Alignment == Alignment.BottomLeft || txtCol.Alignment == Alignment.MiddleLeft ||
                          txtCol.Alignment == Alignment.TopLeft
                            ? TextAlignment.Left
                            : txtCol.Alignment == Alignment.BottomCenter || txtCol.Alignment == Alignment.MiddleCenter ||
                              txtCol.Alignment == Alignment.TopCenter
                                ? TextAlignment.Center
                                : TextAlignment.Right;

                    newcol.AllowEditing = false;
                    newcol.UpdateTrigger = UpdateSourceTrigger.PropertyChanged;
                    newcol.MappingName = sColName;
                    newcol.Width = column.Width;
                    newcol.ColumnSizer = TreeColumnSizer.None;
                    newcol.HeaderText = column.HeaderText;
                    //newcol.AllowFiltering = column.Filters != null && column.Filters.Any();
                    newcol.AllowSorting = false;
                    newcol.ShowHeaderToolTip = column.ToolTip != null;
                    
                    ApplyFilterStyle(newcol, false, true);

                    var headstyle = new Style(typeof(TreeGridHeaderCell));
                    headstyle.Setters.Add(new Setter(Control.BackgroundProperty, new SolidColorBrush(Colors.Gainsboro)));
                    headstyle.Setters.Add(new Setter(Control.ForegroundProperty, new SolidColorBrush(Colors.Black)));
                    headstyle.Setters.Add(new Setter(Control.FontSizeProperty, 12D));
                    headstyle.Setters.Add(new Setter(Control.MarginProperty, new Thickness(0, -0.75, 0, 0.75)));
                    headstyle.Setters.Add(new Setter(Control.BorderThicknessProperty, new Thickness(0.75)));
                    headstyle.Setters.Add(new EventSetter(Control.MouseLeftButtonUpEvent, new MouseButtonEventHandler(HeaderCell_LeftMouseButtonEvent)));
                    if (txtCol.VerticalHeader)
                    {
                        headstyle.Setters.Add(new Setter(Control.HorizontalContentAlignmentProperty, HorizontalAlignment.Left));
                        headstyle.Setters.Add(new Setter(Control.TemplateProperty,
                            Application.Current.Resources["VerticalColumnHeader"] as ControlTemplate));
                    }

                    newcol.HeaderStyle = headstyle;

                    _tree.Columns.Add(newcol);
                    ColumnList.Add(column);
                }
                else if (column is DynamicTemplateColumn tmplCol)
                {
                    var newcol = new TreeGridTemplateColumn();
                    newcol.CellTemplateSelector = new TemplateColumnSelector(this, tmplCol.Template);
                    newcol.AllowEditing = false;
                    newcol.UpdateTrigger = UpdateSourceTrigger.PropertyChanged;
                    
                    newcol.Width = tmplCol.Width;
                    newcol.ColumnSizer = TreeColumnSizer.None;
                    newcol.HeaderText = column.HeaderText;
                    //newcol.AllowFiltering = false;
                    newcol.AllowSorting = false;
                    newcol.ShowToolTip = false;
                    newcol.ShowHeaderToolTip = false;
                    newcol.MappingName = sColName;
                    
                    ApplyFilterStyle(newcol, false, true);

                    var headstyle = new Style(typeof(TreeGridHeaderCell));
                    headstyle.Setters.Add(new Setter(Control.BackgroundProperty, new SolidColorBrush(Colors.Gainsboro)));
                    headstyle.Setters.Add(new Setter(Control.ForegroundProperty, new SolidColorBrush(Colors.Black)));
                    headstyle.Setters.Add(new Setter(Control.FontSizeProperty, 12D));
                    headstyle.Setters.Add(new Setter(Control.MarginProperty, new Thickness(0, -0.75, 0, 0.75)));
                    headstyle.Setters.Add(new Setter(Control.BorderThicknessProperty, new Thickness(0.75)));
                    headstyle.Setters.Add(new EventSetter(Control.MouseLeftButtonUpEvent, new MouseButtonEventHandler(HeaderCell_LeftMouseButtonEvent)));
                    newcol.HeaderStyle = headstyle;

                    _tree.Columns.Add(newcol);
                    ColumnList.Add(column);
                }
            }
        }
    }

    private void LoadDataColumns(DynamicGridColumns columns)
    {
        foreach (var column in columns)
        {
            if(this.CreateEditorColumn(column, out var newcol, out var prop))
            {
                //newcol.GetEntity = () => _editingObject.Object;
                //newcol.EntityChanged += DoEntityChanged;

                var newColumn = newcol.CreateTreeGridColumn();

                //newColumn.AllowEditing = newcol.Editable && Parent.IsDirectEditMode();

                ApplyFilterStyle(newColumn, newcol.Filtered, false);
                
                var headstyle = new Style(typeof(TreeGridHeaderCell));
                headstyle.Setters.Add(new Setter(Control.BackgroundProperty, new SolidColorBrush(Colors.Gainsboro)));
                headstyle.Setters.Add(new Setter(Control.ForegroundProperty, new SolidColorBrush(Colors.Black)));
                headstyle.Setters.Add(new Setter(Control.FontSizeProperty, 12D));
                newColumn.HeaderStyle = headstyle;

                var cellstyle = new Style();
                if (Parent.IsDirectEditMode())
                {
                    if (prop.Editor is null || !prop.Editor.Editable.IsDirectEditable())
                    {
                        cellstyle.Setters.Add(new Setter(Control.BackgroundProperty,
                            new SolidColorBrush(Colors.WhiteSmoke)));
                        newColumn.AllowEditing = false;
                    }
                    else
                    {
                        cellstyle.Setters.Add(new Setter(Control.BackgroundProperty,
                            new SolidColorBrush(Colors.LightYellow)));
                        newColumn.AllowEditing = true;
                    }

                    cellstyle.Setters.Add(new Setter(Control.ForegroundProperty, new SolidColorBrush(Colors.Black)));
                    newColumn.CellStyle = cellstyle;
                }
                else
                {
                    cellstyle.Setters.Add(new Setter(Control.BackgroundProperty,
                        new Binding()
                        {
                            Path = new PropertyPath("."), Converter = CellBackgroundConverter,
                            ConverterParameter = new DynamicGridCellStyleParameters(column,DependencyProperty.UnsetValue)
                        }));
                    cellstyle.Setters.Add(new Setter(Control.ForegroundProperty,
                        new Binding()
                        {
                            Converter = CellForegroundConverter, 
                            ConverterParameter = new DynamicGridCellStyleParameters(column,DependencyProperty.UnsetValue)
                        }));
                    cellstyle.Setters.Add(new Setter(Control.FontSizeProperty,
                        new Binding()
                        {
                            Converter = CellFontSizeConverter, 
                            ConverterParameter = new DynamicGridCellStyleParameters(column,DependencyProperty.UnsetValue)
                        }));
                    cellstyle.Setters.Add(new Setter(Control.FontStyleProperty,
                        new Binding()
                        {
                            Converter = CellFontStyleConverter, 
                            ConverterParameter = new DynamicGridCellStyleParameters(column,DependencyProperty.UnsetValue)
                        }));
                    cellstyle.Setters.Add(new Setter(Control.FontWeightProperty,
                        new Binding()
                        {
                            Converter = CellFontWeightConverter, 
                            ConverterParameter = new DynamicGridCellStyleParameters(column,DependencyProperty.UnsetValue)
                        }));
                    newColumn.CellStyle = cellstyle;
                }
                
                _tree.Columns.Add(newColumn);
                ColumnList.Add(column);
                foreach (var extra in newcol.ExtraColumns)
                    Parent.AddHiddenColumn(extra);
            }
        }
    }

    private void LoadStackedHeaders(DynamicGridColumnGroupings groupings)
    {
        _tree.StackedHeaderRows.Clear();
        foreach(var grouping in groupings)
        {
            var row = new StackedHeaderRow();
            var i = 0;
            foreach(var group in grouping.Groups)
            {
                var start = Math.Max(i, ColumnList.IndexOf(group.StartColumn));
                var end = Math.Max(start, ColumnList.IndexOf(group.EndColumn));

                if(end < start)
                {
                    i = end + 1;
                    continue;
                }

                var cols = Enumerable.Range(start, end - start + 1).Select(i => _tree.Columns[i]).ToArray();

                var stackedColumn = new StackedColumn
                {
                    HeaderText = group.Header,
                    ChildColumns = string.Join(',', cols.Select(x => x.MappingName))
                };

                row.StackedColumns.Add(stackedColumn);

                i = end + 1;
            }

            _tree.StackedHeaderRows.Add(row);
        }
        if(groupings.Count > 0)
        {
            _tree.CellRenderers.Remove("StackedHeader");
            _tree.CellRenderers.Add("StackedHeader", new StackedHeaderRenderer());
        }
    }

    public void RefreshColumns(DynamicGridColumns columns, DynamicActionColumns actionColumns, DynamicGridColumnGroupings groupings)
    {
        _tree.ItemsSource = null;
        _tree.Columns.Suspend();

        ColumnList.Clear();
        _tree.Columns.Clear();

        ActionColumns = actionColumns.ToList();
        
        //_tree.Columns.Add(new TreeGridTextColumn()
        //    {
        //        MappingName = "Number",
        //        Width = _shownumbers ? 50 : 0,
        //        TextAlignment = TextAlignment.Right
        //    }
        //);

        LoadActionColumns(DynamicActionColumnPosition.Start);
        LoadDataColumns(columns);
        LoadActionColumns(DynamicActionColumnPosition.End);
        LoadStackedHeaders(groupings);

        _tree.Columns.Resume();
        _tree.RefreshColumns();

        foreach (var key in FilterPredicates.Keys.ToArray())
            if (_tree.Columns.Any(x => string.Equals(x.MappingName, key)))
            {
                var predicates = Serialization.Deserialize<List<FilterPredicate>>(FilterPredicates[key]);
                foreach (var predicate in predicates)
                {
                    _tree.Columns[key].FilterPredicates.Add(predicate);
                }
            }
            else
            {
                FilterPredicates.Remove(key);
            }

        ResizeColumns(_tree, _tree.ActualWidth - 2, _tree.ActualHeight - 2);
    }

    private void ResizeColumns(SfTreeGrid grid, double width, double height)
    {
        if (Parent.Data == null || width <= 0)
            return;

        grid.Dispatcher.BeginInvoke(() =>
        {
            foreach (var (index, size) in this.CalculateColumnSizes(width))
                _tree.Columns[index].Width = Math.Max(0.0F, size);
        });
    }

    #endregion

    #region Refresh

    public CoreTreeNodes Nodes { get; set; }

    private CoreTable? _innerTable;

    public void BeforeRefresh()
    {
        _tree.SelectionForeground = DynamicGridUtils.SelectionForeground;
        _tree.SelectionBackground = DynamicGridUtils.SelectionBackground;
    }

    public void RefreshData(CoreTable data)
    {
        var nodes = new CoreTreeNodes();

        _innerTable = new CoreTable();
        _innerTable.LoadColumns(data.Columns);

        for (var i = 0; i < ActionColumns.Count; i++)
            _innerTable.Columns.Add(
                new CoreColumn
                {
                    ColumnName = $"_ActionColumn{i}",
                    DataType = ActionColumns[i] is DynamicImageColumn
                        ? typeof(BitmapImage)
                        : typeof(String)
                });

        foreach (var row in data.Rows)
        {
            var newRow = _innerTable.NewRow();
            ProcessRow(newRow, row);
            _innerTable.Rows.Add(newRow);

            var _id = row.Get<Guid>(IDColumn.Property);
            var _parent = row.Get<Guid>(ParentColumn.Property);
            nodes.Add(_id, _parent, newRow);
        }
        Nodes = nodes;
        _tree.ItemsSource = nodes.Nodes;

        CalculateRowHeight();
        ResizeColumns(_tree, _tree.ActualWidth - 1, _tree.ActualHeight);

        UpdateRecordCount();
    }

    private void ProcessRow(CoreRow innerRow, CoreRow row)
    {
        innerRow.LoadValues(row.Values);

        for (var i = 0; i < ActionColumns.Count; i++)
        {
            var ac = ActionColumns[i];
            innerRow[$"_ActionColumn{i}"] = ac.Data(row);
        }
    }

    private void CalculateRowHeight()
    {
        if(Parent.Data != null && Parent.Data.Rows.Count > 0)
        {
            var contentHeight = _tree.ActualHeight - (_tree.Padding.Top + _tree.Padding.Bottom) - 2; // Two extra pixels of space
            var targetHeight = contentHeight / Parent.Data.Rows.Count;
            _tree.RowHeight = Math.Max(Math.Min(targetHeight, MaxRowHeight), MinRowHeight);
        }
    }

    private void UpdateRecordCount()
    {
        var count = _tree.View != null ? _tree.View.Nodes.Count : Parent.Data.Rows.Count;
        Parent.UpdateRecordCount(count);
    }

    #endregion


    public void AddVisualFilter(string column, string value, FilterType filtertype = FilterType.Contains)
    {
        if (value.IsNullOrWhiteSpace())
            return;
        var col = _tree.Columns.FirstOrDefault((x => string.Equals(x.MappingName?.ToUpper(),column?.Replace(".", "_").ToUpper())));
        if (col != null)
        {
            col.FilterPredicates.Add(new FilterPredicate { FilterType = filtertype, FilterValue = value });
        }
    }
    public List<Tuple<string, Func<CoreRow, bool>>> GetFilterPredicates()
    {
        var list = new List<Tuple<string, Func<CoreRow, bool>>>();
        foreach (var column in _tree.Columns)
        {
            var colIndex = _tree.Columns.IndexOf(column);
            var col = ColumnList[colIndex];
            if (col is DynamicGridColumn gridColumn)
            {
                var rowPredicate = DynamicGridGridUIComponentExtension.ConvertColumnPredicates(gridColumn, column.FilterPredicates);
                if(rowPredicate is not null)
                {
                    list.Add(new(gridColumn.ColumnName, rowPredicate));
                }
            }
            else if(col is DynamicActionColumn dac && dac.FilterRecord is not null)
            {
                if(dac.SelectedFilters is not null && dac.SelectedFilters.Length > 0)
                {
                    list.Add(new(column.MappingName, (row) => dac.FilterRecord(row, dac.SelectedFilters)));
                }
                if(dac.ExcludeFilters is not null && dac.ExcludeFilters.Length > 0)
                {
                    list.Add(new(column.MappingName, (row) => !dac.FilterRecord(row, dac.ExcludeFilters)));
                }
            }
        }
        return list;
    }

    public CoreRow[] GetVisibleRows()
    {
        return _tree.View?.Nodes.Select(x => MapRow((x.Item as CoreTreeNode)?.Row)).NotNull().ToArray() ?? new CoreRow[] { };
    }

    public void InvalidateRow(CoreRow row)
    {
        if (_innerTable is null || row.Index < 0 || row.Index >= _innerTable.Rows.Count) return;

        var _innerRow = _innerTable.Rows[row.Index];
        ProcessRow(_innerRow, row);

        var coreTreeNode = Nodes.Find(_innerRow);
        coreTreeNode?.InvalidateData();
    }


    public void ScrollIntoView(CoreRow row)
    {
        _tree.ScrollInView(new RowColumnIndex(row.Index + 1, 0));
    }

    public void UpdateCell(CoreRow row, string column, object? value)
    {
        throw new NotImplementedException();
    }

    public void UpdateRow(CoreRow row)
    {
        throw new NotImplementedException();
    }

    #region Drag + Drop

    private void _tree_DragOver(object sender, DragEventArgs e)
    {
        Parent.DragOver(sender, e);
    }

    private void _tree_Drop(object sender, DragEventArgs e)
    {
        Parent.Drop(sender, e);
    }

    private void RowDragDropController_DragStart(object? sender, TreeGridRowDragStartEventArgs e)
    {
        var rows = e.DraggingNodes.Select(node => MapRow((node.Item as CoreTreeNode)?.Row)).NotNull().ToArray();
        Parent.DragStart(sender, rows);
    }

    #endregion
}
