using InABox.Wpf;
using InABox.Core;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;
using Expression = System.Linq.Expressions.Expression;
using System;
using System.Reflection;
using System.Windows.Media;
using Syncfusion.Windows.Shared;
using Xceed.Wpf.Toolkit;
using System.Linq;
using System.Collections;
using Syncfusion.Data.Extensions;
using NPOI.SS.UserModel;
using Syncfusion.Windows.Controls.Grid;
using Syncfusion.Data;
using System.Diagnostics.CodeAnalysis;

namespace InABox.DynamicGrid
{
    /// <summary>
    /// Interaction logic for FilterEditorWindow.xaml
    /// </summary>
    public partial class FilterEditorWindow : ThemableWindow
    {
        private BaseFilterNode? FilterNode;

        public FilterEditorWindow()
        {
            InitializeComponent();
        }

        public void SetFilter<T>(Filter<T> filter)
        {
            FilterNode = new FilterNode<T>(filter);
            FilterNode.Margin = new Thickness(5);

            Content.Content = FilterNode;

            Title = $"Edit {typeof(T).Name} Filter";
        }
        public Filter<T>? GetFilter<T>()
        {
            return (FilterNode as FilterNode<T>)?.GetFilter();
        }

        private void Cancel_Click(object sender, RoutedEventArgs e)
        {
            DialogResult = false;
        }

        private void OK_Click(object sender, RoutedEventArgs e)
        {
            DialogResult = true;
            Close();
        }
    }

    public abstract class BaseFilterNode : Grid
    {
        public abstract IFilter? GetFilter();
    }

    public enum FilterNodeType
    {
        ROOT,
        AND,
        OR
    }

    public class FilterNode<T> : BaseFilterNode
    {

        private Button ClearButton;
        private Button? LabelButton;
        private PropertyNode<T> Property;
        private OperatorNode Operator;
        private ValueNode Value;
        private Button AndButton;
        private Button OrButton;
        private FilterListNode<T> Ands;
        private FilterListNode<T> Ors;

        private FilterNodeType filterType;

        public delegate void RemoveEventHandler();
        public event RemoveEventHandler? Remove;

        public FilterNode(Filter<T> filter, FilterNodeType filterType = FilterNodeType.ROOT)
        {
            ColumnDefinitions.Add(new ColumnDefinition { Width = GridLength.Auto });
            ColumnDefinitions.Add(new ColumnDefinition { Width = GridLength.Auto });
            ColumnDefinitions.Add(new ColumnDefinition { Width = GridLength.Auto });
            ColumnDefinitions.Add(new ColumnDefinition { Width = GridLength.Auto });
            ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(1, GridUnitType.Star) });
            ColumnDefinitions.Add(new ColumnDefinition { Width = GridLength.Auto });
            ColumnDefinitions.Add(new ColumnDefinition { Width = GridLength.Auto });

            RowDefinitions.Add(new RowDefinition { Height = GridLength.Auto});
            RowDefinitions.Add(new RowDefinition { Height = GridLength.Auto});
            RowDefinitions.Add(new RowDefinition { Height = GridLength.Auto});

            ClearButton = new Button { Content = "-", Width = 20, Margin = new Thickness(0, 0, 5, 0), Padding = new Thickness(5) };

            if (filterType != FilterNodeType.ROOT)
            {
                ClearButton.Click += (s, e) => Remove?.Invoke();

                LabelButton = new Button { Content = "-", Width = 50, Margin = new Thickness(0, 0, 5, 0), Padding = new Thickness(5) };

                if (filterType == FilterNodeType.AND)
                    LabelButton.Content = "And";
                else if (filterType == FilterNodeType.OR)
                    LabelButton.Content = "Or";
            }
            else
            {
                ClearButton.Click += ClearButton_Click;
            }

            this.filterType = filterType;

            Property = new(filter.Expression) { Margin = new Thickness(0, 0, 5, 0) };
            Operator = new(filter.Operator) { Margin = new Thickness(0, 0, 5, 0) };

            var valueNode = Property.SelectedProperty != null ? CreateValueNode(Property.SelectedProperty, filter.Operator) : null;
            SetValueNode(valueNode ?? new EmptyValueNode(), filter.Value);

            Ands = new FilterListNode<T>(filter.Ands, FilterNodeType.AND) { Margin = new Thickness(0, 0, 0, 0) };
            Ors = new FilterListNode<T>(filter.Ors, FilterNodeType.OR);

            AndButton = new Button { Content = "And", Width = 40, Margin = new Thickness(0, 0, 5, 0), Padding = new Thickness(5) };
            OrButton = new Button { Content = "Or", Width = 40, Margin = new Thickness(0, 0, 0, 0), Padding = new Thickness(5) };
            AndButton.Click += AndButton_Click;
            OrButton.Click += OrButton_Click;

            Property.PropertyChanged += Property_PropertyChanged;
            Operator.OperatorChanged += Operator_OperatorChanged;

            Add(ClearButton, 0, 0);
            if(LabelButton != null)
            {
                Add(LabelButton, 0, 1);
            }
            Add(Property, 0, 2);
            Add(Operator, 0, 3);
            Add(AndButton, 0, 5);
            Add(OrButton, 0, 6);

            Add(Ands, 1, 1, 6);
            Add(Ors, 2, 1, 6);
        }

        private void ClearButton_Click(object sender, RoutedEventArgs e)
        {
            Property.Clear();
            Operator.Clear();
            Ands.Clear();
            Ors.Clear();
        }

        private void AndButton_Click(object sender, RoutedEventArgs e)
        {
            Ands.AddFilter(new Filter<T>());
        }

        private void OrButton_Click(object sender, RoutedEventArgs e)
        {
            Ors.AddFilter(new Filter<T>());
        }

        public override Filter<T>? GetFilter()
        {
            if (Operator.SelectedOperator == Core.Operator.All)
                return new Filter<T>().All();
            if (Operator.SelectedOperator == Core.Operator.None)
                return new Filter<T>().None();

            if (Property.SelectedProperty is null)
                return null;
            var value = Value.Value;

            var filter = new Filter<T>();
            filter.Expression = CoreUtils.CreateMemberExpression(typeof(T), Property.SelectedProperty);
            filter.Operator = Operator.SelectedOperator;
            filter.Value = value;

            foreach(var and in Ands.GetFilters())
            {
                filter.Ands.Add(and);
            }

            foreach(var or in Ors.GetFilters())
            {
                filter.Ors.Add(or);
            }

            return filter;
        }

        private Type? ValueNodeType(Type propertyType)
        {
            if (propertyType == typeof(bool))
                return typeof(BooleanValueNode);
            if (propertyType == typeof(string))
                return typeof(StringValueNode);
            if (propertyType == typeof(Guid))
                return typeof(GuidValueNode);
            if (propertyType.IsOrdinal())
                return typeof(IntegerValueNode);
            if (propertyType == typeof(double))
                return typeof(DoubleValueNode);
            if (propertyType == typeof(DateTime))
                return typeof(DateTimeValueNode);
            if (propertyType == typeof(TimeSpan))
                return typeof(TimeSpanValueNode);
            if (propertyType.GetTypeInfo().IsEnum)
                return typeof(EnumValueNode<>).MakeGenericType(propertyType);
            return null;
        }

        private ValueNode? CreateValueNode(Type propertyType, Operator op)
        {
            switch (op)
            {
                case Core.Operator.InList:
                case Core.Operator.NotInList:
                    var elementNodeType = ValueNodeType(propertyType);
                    if (elementNodeType == null)
                        return null;
                    return Activator.CreateInstance(typeof(ArrayValueNode<>).MakeGenericType(elementNodeType)) as ValueNode;

                case Core.Operator.BeginsWith:
                case Core.Operator.Contains:
                case Core.Operator.EndsWith:
                    return new StringValueNode();

                case Core.Operator.InQuery:
                    return new SubQueryNode();

                case Core.Operator.All:
                case Core.Operator.None:
                    return null;
            }
            var valueNodeType = ValueNodeType(propertyType);
            if (valueNodeType == null)
                return null;
            return Activator.CreateInstance(valueNodeType) as ValueNode;
        }

        private ValueNode? CreateValueNode(string propertyName, Operator op)
        {
            var property = CoreUtils.GetProperty(typeof(T), propertyName);
            return CreateValueNode(property.PropertyType, op);
        }

        [MemberNotNull(nameof(Value))]
        private void SetValueNode(ValueNode node, object? value)
        {
            Value = node;
            Value.Value = value;
            Value.Margin = new Thickness(0, 0, 5, 0);

            Add(Value, 0, 4);
        }

        private void Property_PropertyChanged(string? property)
        {
            var value = Value.Value;
            Children.Remove(Value);
            var valueNode = property != null ?
                CreateValueNode(property, Operator.SelectedOperator) ?? new EmptyValueNode() :
                new EmptyValueNode();
            SetValueNode(
                valueNode,
                value
            );
        }

        private void Operator_OperatorChanged(Operator op)
        {

            var value = Value.Value;
            Children.Remove(Value);

            ValueNode node;
            if (Property.SelectedProperty == null)
                node = new EmptyValueNode();
            else
                node = CreateValueNode(Property.SelectedProperty, op) ?? new EmptyValueNode();
            SetValueNode(
                node,
                value
            );
        }

        private void Add(UIElement element, int row, int column = 1, int columnSpan = 1)
        {
            element.SetValue(RowProperty, row);
            element.SetValue(ColumnProperty, column);
            element.SetValue(ColumnSpanProperty, columnSpan);
            Children.Add(element);
        }
    }

    public class PropertyNode<T> : ComboBox
    {
        public delegate void PropertyChangedHandler(string? property);

        public event PropertyChangedHandler? PropertyChanged;

        public string? SelectedProperty => SelectedItem as string;

        public PropertyNode(Expression expression)
        {
            SetResourceReference(StyleProperty, typeof(ComboBox));

            VerticalAlignment = System.Windows.VerticalAlignment.Stretch;
            VerticalContentAlignment = System.Windows.VerticalAlignment.Center;

            var properties = CoreUtils.PropertyList(typeof(T), x => x.GetCustomAttribute<DoNotSerialize>() == null, true).Keys.ToList();
            properties.Sort();
            foreach (var property in properties)
            {
                Items.Add(property);
            }
            if (CoreUtils.TryFindMemberExpression(expression, out var mexp))
                SelectedItem = CoreUtils.GetFullPropertyName(mexp, ".");

            SelectionChanged += ComboBox_SelectionChanged;
        }

        public void Clear()
        {
            SelectedIndex = -1;
        }

        private void ComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            var selected = SelectedItem as string;
            PropertyChanged?.Invoke(selected);
        }
    }

    public class OperatorNode : ComboBox
    {
        public delegate void OperatorChangedHandler(Operator op);

        public event OperatorChangedHandler? OperatorChanged;

        public Operator SelectedOperator => (Operator)SelectedItem;

        public OperatorNode(Operator op)
        {
            SetResourceReference(StyleProperty, typeof(ComboBox));

            VerticalAlignment = System.Windows.VerticalAlignment.Stretch;
            VerticalContentAlignment = System.Windows.VerticalAlignment.Center;

            foreach (var value in Enum.GetValues<Operator>())
            {
                Items.Add(value);
            }
            SelectedItem = op;
            SelectionChanged += ComboBox_SelectionChanged;
        }
        public void Clear()
        {
            SelectedIndex = 0;
        }

        private void ComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            var selected = (Operator?)SelectedItem ?? Operator.IsEqualTo;
            OperatorChanged?.Invoke(selected);
        }
    }

    public class FilterListNode<T> : Grid
    {
        private List<FilterNode<T>> Filters;

        public FilterNodeType FilterType;

        public FilterListNode(List<Filter<T>> filters, FilterNodeType filterType)
        {
            ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(1, GridUnitType.Star) });

            FilterType = filterType;

            Filters = new();
            changing = true;
            foreach (var filter in filters)
            {
                AddFilter(filter);
            }
            changing = false;

            RefreshGrid();
        }

        private bool changing = false;

        public void Clear()
        {
            foreach(var element in Filters)
            {
                Children.Remove(element);
            }
            Filters.Clear();
            RefreshGrid();
        }

        public void AddFilter(Filter<T> filter, bool open = false)
        {
            RowDefinitions.Add(new RowDefinition { Height = GridLength.Auto });

            var element = new FilterNode<T>(filter, FilterType)
            {
                Margin = new Thickness(0, 5, 0, 0)
            };
            element.Remove += () =>
            {
                Filters.Remove(element);
                Children.Remove(element);
                RefreshGrid();
            };
            element.SetValue(ColumnProperty, 0);

            Children.Add(element);
            Filters.Add(element);

            if (!changing)
                RefreshGrid();
        }

        public void RefreshGrid()
        {
            RowDefinitions.Clear();

            var row = 0;
            foreach(var element in Filters)
            {
                RowDefinitions.Add(new RowDefinition { Height = GridLength.Auto });

                element.SetValue(RowProperty, row);

                row++;
            }
        }

        public List<Filter<T>> GetFilters()
        {
            return Filters.Select(x => x.GetFilter()).Where(x => x != null).Select(x => x!).ToList();
        }
    }

    public abstract class ValueNode : ContentControl
    {
        public abstract object? Value { get; set; }
    }

    public class BooleanValueNode : ValueNode {

        private ComboBox Editor;

        public override object? Value {
            get => (string)Editor.SelectedItem == "True";
            set {
                if(value is bool b)
                {
                    Editor.SelectedItem = b ? "True" : "False";
                }
                else
                {
                    Editor.SelectedItem = "False";
                }
            }
        }

        public BooleanValueNode()
        {
            Editor = new ComboBox
            {
                VerticalAlignment = System.Windows.VerticalAlignment.Stretch,
                VerticalContentAlignment = System.Windows.VerticalAlignment.Center
            };

            Editor.Items.Add("True");
            Editor.Items.Add("False");

            Content = Editor;
        }
    }
    public class StringValueNode : ValueNode {

        private TextBox Editor;

        public override object? Value {
            get => Editor.Text;
            set
            {
                if (value is string || value?.GetType().IsOrdinal() == true || value is DateTime || value is bool || value is Enum)
                    Editor.Text = value.ToString() ?? "";
                else
                    Editor.Text = "";
            } 
        }

        public StringValueNode()
        {
            Editor = new TextBox
            {
                VerticalAlignment = System.Windows.VerticalAlignment.Stretch,
                VerticalContentAlignment = System.Windows.VerticalAlignment.Center,
                Background = new SolidColorBrush(Colors.LightYellow)
            };
            Content = Editor;
        }

    }
    public class GuidValueNode : ValueNode
    {

        private TextBox Editor;

        public override object? Value
        {
            get {
                if (Guid.TryParse(Editor.Text, out var guid))
                    return guid;
                return Guid.Empty;
            }
            set
            {
                if (value is Guid guid || (value is string str && Guid.TryParse(str, out guid)))
                {
                    if(guid == Guid.Empty)
                    {
                        Editor.Text = "";
                    }
                    else
                    {
                        Editor.Text = guid.ToString();
                    }
                }
                else
                {
                    Editor.Text = "";
                }
            }
        }

        public GuidValueNode()
        {
            Editor = new TextBox
            {
                VerticalAlignment = System.Windows.VerticalAlignment.Stretch,
                VerticalContentAlignment = System.Windows.VerticalAlignment.Center,
                Background = new SolidColorBrush(Colors.LightYellow)
            };
            Content = Editor;
        }
    }
    public class IntegerValueNode : ValueNode {

        private IntegerTextBox TextBox;

        public override object? Value {
            get => TextBox.Value; 
            set {
                if (value?.GetType().IsOrdinal() == true)
                    TextBox.Value = (long)value;
                else if (value is string str && long.TryParse(str, out var num))
                    TextBox.Value = num;
                else
                    TextBox.Value = 0;
            }
        }

        public IntegerValueNode()
        {
            TextBox = new IntegerTextBox
            {
                VerticalAlignment = System.Windows.VerticalAlignment.Stretch,
                VerticalContentAlignment = System.Windows.VerticalAlignment.Center,
                HorizontalAlignment = System.Windows.HorizontalAlignment.Stretch,
                HorizontalContentAlignment = System.Windows.HorizontalAlignment.Center
            };
            Content = TextBox;
        }

    }
    public class DoubleValueNode : ValueNode
    {

        private DoubleTextBox TextBox;

        public override object? Value {
            get => TextBox.Value; 
            set {
                if (value is double d)
                    TextBox.Value = d;
                else if (value?.GetType().IsOrdinal() == true)
                    TextBox.Value = (double)value;
                else if (value is string str && double.TryParse(str, out var num))
                    TextBox.Value = num;
                else
                    TextBox.Value = 0.0;
            }
        }

        public DoubleValueNode()
        {
            TextBox = new DoubleTextBox
            {
                VerticalAlignment = System.Windows.VerticalAlignment.Stretch,
                VerticalContentAlignment = System.Windows.VerticalAlignment.Center,
                HorizontalAlignment = System.Windows.HorizontalAlignment.Stretch,
                HorizontalContentAlignment = System.Windows.HorizontalAlignment.Center
            };
            Content = TextBox;
        }

    }
    public class DateTimeValueNode : ValueNode {

        private DateTimePicker Editor;

        public override object? Value
        {
            get => Editor.Value;
            set
            {
                if (value is DateTime dateTime)
                    Editor.Value = dateTime;
                else if (value is string str && DateTime.TryParse(str, out var date))
                    Editor.Value = date;
                else
                    Editor.Value = DateTime.Today;
            }
        }

        public DateTimeValueNode()
        {
            Editor = new DateTimePicker
            {
                Format = DateTimeFormat.Custom,
                FormatString = "dd MMM yyyy HH:mm",
                VerticalAlignment = System.Windows.VerticalAlignment.Stretch,
                VerticalContentAlignment = System.Windows.VerticalAlignment.Center,
                HorizontalContentAlignment = System.Windows.HorizontalAlignment.Center,
                CalendarDisplayMode = CalendarMode.Month,
                ShowButtonSpinner = false
            };
            Content = Editor;
        }
    }
    public class TimeSpanValueNode : ValueNode {

        private DateTimeEdit Editor;

        public override object? Value {
            get
            {
                var result = new TimeSpan(0);

                if (Editor.DateTime.HasValue)
                    result = Editor.DateTime.Value.TimeOfDay;

                result = new TimeSpan(result.Hours, result.Minutes, 0);

                return result;
            }
            set
            {
                if (value is TimeSpan time)
                    Editor.DateTime = DateTime.MinValue + time;
                else
                    Editor.DateTime = DateTime.MinValue;
            }
        }

        public TimeSpanValueNode()
        {
            Editor = new DateTimeEdit
            {
                Pattern = DateTimePattern.CustomPattern,
                CustomPattern = "HH:mm",
                VerticalAlignment = System.Windows.VerticalAlignment.Stretch,
                VerticalContentAlignment = System.Windows.VerticalAlignment.Center,
                HorizontalAlignment = System.Windows.HorizontalAlignment.Stretch,
                HorizontalContentAlignment = System.Windows.HorizontalAlignment.Center,

                DropDownView = DropDownViews.Clock,
                IsButtonPopUpEnabled = false
            };

            Content = Editor;
        }

    }
    public class EnumValueNode<TEnum> : ValueNode where TEnum : struct, Enum {

        private ComboBox ComboBox;

        public override object? Value {
            get => ComboBox.SelectedItem;
            set {
                if (value is TEnum val)
                    ComboBox.SelectedItem = val;
                else if (value is string str && Enum.TryParse<TEnum>(str, out val))
                    ComboBox.SelectedItem = val;
                else
                    ComboBox.SelectedIndex = 0;
            }
        }

        public EnumValueNode()
        {
            ComboBox = new ComboBox
            {
                VerticalAlignment = System.Windows.VerticalAlignment.Stretch,
                VerticalContentAlignment = System.Windows.VerticalAlignment.Center
            };

            foreach(var value in Enum.GetValues<TEnum>())
            {
                ComboBox.Items.Add(value);
            }

            Content = ComboBox;
        }

    }
    public class ArrayValueNode<TValueNode> : ValueNode where TValueNode : ValueNode, new() {

        private TextBox Display;
        private Button Edit;

        private List<object?>? list;

        public override object? Value {
            get => list?.ToArray();
            set
            {
                if (value is string)
                    list = new List<object?> { value };
                else if(value is IEnumerable objList)
                {
                    list = objList.Cast<object?>().ToList();
                }
                else
                {
                    list = new List<object?> { value };
                }
                UpdateDisplayText();
            }
        }

        public ArrayValueNode()
        {
            var panel = new DockPanel();

            Display = new TextBox
            {
                VerticalContentAlignment = System.Windows.VerticalAlignment.Center,
                Background = new SolidColorBrush(Colors.LightYellow),
                Margin = new Thickness(0, 0, 5, 0),
                IsEnabled = false
            };
            Edit = new Button
            {
                Content = "Edit",
                Padding = new Thickness(5)
            };
            Edit.Click += Edit_Click;

            Display.SetValue(DockPanel.DockProperty, Dock.Left);
            Edit.SetValue(DockPanel.DockProperty, Dock.Right);

            panel.Children.Add(Edit);
            panel.Children.Add(Display);

            Content = panel;
        }

        private void Edit_Click(object sender, RoutedEventArgs e)
        {
            var window = new ListWindow();
            window.SetList<TValueNode>(list ?? new List<object?>());

            if (window.ShowDialog() == true)
            {
                list = window.GetList<TValueNode>();
                UpdateDisplayText();
            }
        }

        private void UpdateDisplayText()
        {
            Display.Text = $"[{(list != null ? string.Join(',', list) : "")}]";
        }
    }
    
    public class FilterComponent : Grid
    {
        private TextBox Display;
        private Button Edit;
        private BaseFilterNode? FilterNode;

        private Type? _filterType;
        public Type? FilterType
        {
            get => _filterType;
            set
            {
                _filterType = value;
                HideFilter(value == null);
                Filter = null;
            }
        }

        private IFilter? _filter;
        private IFilter? Filter
        {
            get => _filter;
            set
            {
                _filter = value;
                Display.Text = _filter?.AsOData() ?? "";
            }
        }
        private bool Hidden = true;

        public FilterComponent(IFilter? filter)
        {
            ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(1, GridUnitType.Star) });
            ColumnDefinitions.Add(new ColumnDefinition { Width = GridLength.Auto });

            RowDefinitions.Add(new RowDefinition { Height = GridLength.Auto });
            RowDefinitions.Add(new RowDefinition { Height = new GridLength(1, GridUnitType.Star) });

            Display = new TextBox
            {
                VerticalAlignment = System.Windows.VerticalAlignment.Stretch,
                VerticalContentAlignment = System.Windows.VerticalAlignment.Center,
                Background = new SolidColorBrush(Colors.LightYellow),
                IsEnabled = false,
                Margin = new Thickness(0, 0, 5, 5)
            };
            Display.SetValue(Grid.RowProperty, 0);
            Display.SetValue(Grid.ColumnProperty, 0);

            Edit = new Button
            {
                Content = "Edit",
                IsEnabled = false,
                Margin = new Thickness(0, 0, 0, 5),
                Padding = new Thickness(5)
            };
            Edit.SetValue(Grid.RowProperty, 0);
            Edit.SetValue(Grid.ColumnProperty, 1);
            Edit.Click += Edit_Click;

            Children.Add(Display);
            Children.Add(Edit);

            Filter = filter;

            HideFilter();
        }

        private void Edit_Click(object sender, RoutedEventArgs e)
        {
            if (Hidden)
            {
                EditFilter();
            }
            else
            {
                HideFilter();
            }
        }

        private void HideFilter(bool disable = false)
        {
            Hidden = true;
            Edit.Content = "Edit";
            Edit.IsEnabled = !disable;

            if (FilterNode != null)
            {
                Filter = FilterNode.GetFilter();

                Children.Remove(FilterNode);
                FilterNode = null;
            }
        }

        public void EditFilter()
        {
            if (FilterType == null)
                return;

            Hidden = false;
            Edit.Content = "Hide";

            var filter = Filter ?? Activator.CreateInstance(typeof(Filter<>).MakeGenericType(FilterType)) as IFilter;

            Filter = filter;
            if (FilterNode != null)
                Children.Remove(FilterNode);

            FilterNode = (Activator.CreateInstance(typeof(FilterNode<>).MakeGenericType(FilterType), filter, FilterNodeType.ROOT) as BaseFilterNode)!;
            FilterNode.SetValue(Grid.RowProperty, 1);
            FilterNode.SetValue(Grid.ColumnProperty, 0);
            FilterNode.SetValue(Grid.ColumnSpanProperty, 2);

            Children.Add(FilterNode);
        }

        public IFilter? GetFilter() => Hidden ? Filter : FilterNode?.GetFilter();

        public void SetFilter(IFilter? filter)
        {
            Filter = filter;
            if (filter == null)
            {
                HideFilter();
            }
            else
            {
                if (!Hidden)
                    EditFilter();
            }
        }
    }

    public class SubQueryNode : ValueNode
    {
        private TextBox Display;
        private Button Edit;

        private Type? QueryType;
        private string? Column;
        private IFilter? Filter;

        public override object? Value
        {
            get => GetSubQuery();
            set
            {
                if (value is not ISubQuery subQuery)
                {
                    QueryType = null;
                    Column = null;
                    Filter = null;
                }
                else
                {
                    QueryType = subQuery.GetQueryType();
                    Column = subQuery.GetColumn().Property;
                    Filter = subQuery.GetFilter();
                }
                Display.Text = value?.ToString() ?? "";
            }
        }

        public SubQueryNode()
        {
            var panel = new DockPanel();

            Display = new TextBox {
                VerticalContentAlignment = System.Windows.VerticalAlignment.Center,
                Background = new SolidColorBrush(Colors.LightYellow),
                Margin = new Thickness(0, 0, 5, 0),
                IsEnabled = false
            };
            Edit = new Button
            {
                Content = "Edit",
                Padding = new Thickness(5)
            };
            Edit.Click += Edit_Click;

            Display.SetValue(DockPanel.DockProperty, Dock.Left);
            Edit.SetValue(DockPanel.DockProperty, Dock.Right);

            panel.Children.Add(Edit);
            panel.Children.Add(Display);

            Content = panel;
        }

        private void Edit_Click(object sender, RoutedEventArgs e)
        {
            var window = new SubQueryWindow();
            window.QueryType = QueryType;
            window.Column = Column;
            window.Filter = Filter ?? (QueryType != null ? (Activator.CreateInstance(typeof(Filter<>).MakeGenericType(QueryType)) as IFilter) : null);
            if(window.ShowDialog() == true)
            {
                QueryType = window.QueryType;
                Column = window.Column;
                Filter = window.Filter;
                Display.Text = GetSubQuery()?.ToString() ?? "";
            }
        }

        private object? GetSubQuery()
        {
            if (QueryType is null)
                return null;

            var column = Column is null ? null : Activator.CreateInstance(typeof(Column<>).MakeGenericType(QueryType), Column);

            return Activator.CreateInstance(typeof(SubQuery<>).MakeGenericType(QueryType), Filter, column);
        }
    }
    
    public class EmptyValueNode : ValueNode
    {
        public override object? Value { get => null; set { } }
    }
}
