using com.sun.corba.se.impl.oa.poa;
using InABox.Core;
using InABox.WPF;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Globalization;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;

namespace InABox.DynamicGrid;

public class DateTreeDynamicGridColumnFilter : IDynamicGridColumnFilter
{
    public event Action<IDynamicGridColumnFilter>? FilterChanged;

    public IBaseDynamicGrid Grid { get; set; }
    public DynamicColumnBase Column { get; set; }

    private DateTime[] _dates = [];
    private List<HeaderItem> _items = [];
    private HashSet<DateTime>? _selectedDates = null;

    private enum DateLevel
    {
        Year,
        Month,
        Day,
        Date,
        All
    }

    private class HeaderItem(DateLevel level, object? value, string display, DateTreeDynamicGridColumnFilter filter) : INotifyPropertyChanged
    {
        public event Action<HeaderItem, bool>? CheckedChanged;

        public DateLevel Level { get; set; } = level;

        public object? Value { get; set; } = value;

        public string Display { get; set; } = display;

        private bool? _isChecked;
        public bool? IsChecked
        {
            get => _isChecked;
            set
            {
                if(value != _isChecked)
                {
                    _isChecked = value;
                    OnPropertyChanged();

                    filter.BeginUpdate();
                    if(value.HasValue)
                    {
                        CheckedChanged?.Invoke(this, value.Value);

                        if(Parent is not null)
                        {
                            if(Parent.IsChecked != value)
                            {
                                Parent.CalculateChecked();
                            }
                        }
                        foreach(var item in Items)
                        {
                            item.IsChecked = value;
                        }
                    }
                    else
                    {
                        Parent?.CalculateChecked();
                    }
                    filter.EndUpdate();
                }
            }
        }

        public List<HeaderItem> Items { get; set; } = new();

        public HeaderItem? Parent { get; set; } = null;

        public void CalculateChecked(bool includeChildren = false)
        {
            if (Items.Count == 0)
            {
                if (includeChildren)
                {
                    if(Value is DateTime date)
                    {
                        IsChecked = filter.IsSelected(date);
                    }
                    else if(Level == DateLevel.All)
                    {
                        IsChecked = filter._selectedDates is null;
                    }
                }
                return;
            }

            if (includeChildren)
            {
                foreach(var item in Items)
                {
                    item.CalculateChecked(true);
                }
            }

            if(Items.All(x => x.IsChecked == true))
            {
                IsChecked = true;
            }
            else if(Items.All(x => x.IsChecked == false))
            {
                IsChecked = false;
            }
            else
            {
                IsChecked = null;
            }
        }

        public void AddItem(HeaderItem item)
        {
            item.CalculateChecked();
            Items.Add(item);
            item.Parent = this;
        }

        public event PropertyChangedEventHandler? PropertyChanged;

        private void OnPropertyChanged([CallerMemberName] string propertyName = "")
        {
            PropertyChanged?.Invoke(this, new(propertyName));
        }
    }

    public DateTreeDynamicGridColumnFilter(IBaseDynamicGrid grid, DynamicColumnBase column)
    {
        Grid = grid;
        Column = column;
    }

    public void ClearFilter()
    {
        BeginUpdate();
        _selectedDates = null;
        foreach(var item in _items)
        {
            item.CalculateChecked(includeChildren: true);
        }
        Changed();
        EndUpdate();
    }

    public FrameworkElement CreateControl()
    {
        var treeView = new TreeView();

        var rows = (this as IDynamicGridColumnFilter).GetRowsToFilter(Grid);
        var data = rows.Select(x => Grid.GetData(x, Column)).OfType<DateTime>().Distinct();

        var dateData = new SortedDictionary<int, SortedDictionary<int, SortedDictionary<int, List<DateTime>>>>();
        _dates = data.ToArray();

        bool hasBlank = false;
        foreach(var date in _dates)
        {
            if(date == DateTime.MinValue)
            {
                hasBlank = true;
            }
            else
            {
                var yearData = dateData.GetValueOrAdd(date.Year);
                var monthData = yearData.GetValueOrAdd(date.Month);
                var dayData = monthData.GetValueOrAdd(date.Day);
                dayData.Add(date);
            }
        }

        var items = new List<HeaderItem>();

        var selectAll = new HeaderItem(DateLevel.All, null, "Select All", this) { IsChecked = _selectedDates is null };
        selectAll.CheckedChanged += SelectAll_CheckedChanged;
        items.Add(selectAll);

        if (hasBlank)
        {
            var item = new HeaderItem(DateLevel.Date, DateTime.MinValue, "(Blank)", this) { IsChecked = IsSelected(DateTime.MinValue) };
            item.CheckedChanged += Item_CheckedChanged;
            items.Add(item);
        }

        foreach(var (year, yearData) in dateData)
        {
            var yearItem = new HeaderItem(DateLevel.Year, year, year.ToString(), this);
            foreach(var (month, monthData) in yearData)
            {
                var monthItem = new HeaderItem(DateLevel.Month, month, CultureInfo.CurrentCulture.DateTimeFormat.GetAbbreviatedMonthName(month), this);
                foreach (var (day, dayData) in monthData)
                {
                    var dayItem = new HeaderItem(DateLevel.Day, day, day.ToString(), this);
                    foreach(var time in dayData)
                    {
                        var item = new HeaderItem(DateLevel.Date, time, time.ToString("HH:mm:ss"), this);
                        item.IsChecked = IsSelected(time);
                        item.CheckedChanged += Item_CheckedChanged;
                        dayItem.AddItem(item);
                    }
                    monthItem.AddItem(dayItem);
                }
                yearItem.AddItem(monthItem);
            }
            yearItem.CalculateChecked();
            items.Add(yearItem);
        }

        var itemFactory = TemplateGenerator.CreateFactory(() =>
        {
            var panel = new DockPanel();
            panel.LastChildFill = true;

            var box = new CheckBox();
            box.VerticalContentAlignment = VerticalAlignment.Center;
            box.HorizontalAlignment = HorizontalAlignment.Stretch;
            box.Bind<HeaderItem, bool?>(CheckBox.IsCheckedProperty, x => x.IsChecked);
            box.Bind<HeaderItem, HeaderItem>(CheckBox.TagProperty, x => x);
            DockPanel.SetDock(box, Dock.Left);

            var label = new Label();
            label.Bind<HeaderItem, string>(Label.ContentProperty, x => x.Display);
            label.IsHitTestVisible = false;
            DockPanel.SetDock(label, Dock.Right);

            panel.Children.Add(box);
            panel.Children.Add(label);

            return panel;
        });
        treeView.HorizontalContentAlignment = HorizontalAlignment.Stretch;
        treeView.ItemTemplate = new HierarchicalDataTemplate
        {
            VisualTree = itemFactory,
            ItemsSource = WPFUtils.CreateBinding<HeaderItem, List<HeaderItem>>(x => x.Items),
        };
        treeView.ItemsSource = items;
        _items = items;

        return treeView;
    }

    private void SelectAll_CheckedChanged(HeaderItem _item, bool value)
    {
        if (value)
        {
            ClearFilter();
        }
        else
        {
            BeginUpdate();
            _selectedDates = new();
            foreach(var item in _items)
            {
                item.CalculateChecked(includeChildren: true);
            }
            Changed();
            EndUpdate();
        }
    }

    private void Item_CheckedChanged(HeaderItem item, bool value)
    {
        if (item.Level != DateLevel.Date || item.Value is not DateTime date) return;

        if (value)
        {
            if(_selectedDates is not null)
            {
                if (_selectedDates.Add(date))
                {
                    var selectAll = _items.First(x => x.Level == DateLevel.All);
                    selectAll.IsChecked = null;

                    Changed();
                }
            }
        }
        else
        {
            if(_selectedDates is null)
            {
                _selectedDates = _dates.ToHashSet();
                _selectedDates.Remove(date);

                var selectAll = _items.First(x => x.Level == DateLevel.All);
                selectAll.IsChecked = _selectedDates.Count == 0 ? false : null;

                Changed();
            }
            else if(_selectedDates.Remove(date))
            {
                var selectAll = _items.First(x => x.Level == DateLevel.All);
                selectAll.IsChecked = _selectedDates.Count == 0 ? false : null;

                Changed();
            }
        }
    }

    private int _updating = 0;
    private bool _changed = false;
    public void BeginUpdate()
    {
        _updating++;
    }
    public void EndUpdate()
    {
        _updating--;
        if (_updating == 0 && _changed)
        {
            _changed = false;
            FilterChanged?.Invoke(this);
        }
    }

    public void Changed()
    {
        if (_updating > 0)
        {
            _changed = true;
        }
        else
        {
            FilterChanged?.Invoke(this);
        }
    }

    public bool IsSelected(DateTime date)
    {
        return _selectedDates is null || _selectedDates.Contains(date);
    }

    public bool FilterRow(CoreRow row)
    {
        if(Grid.GetData(row, Column) is DateTime date)
        {
            return IsSelected(date);
        }
        else
        {
            return true;
        }
    }

    public bool IsFiltered()
    {
        return _selectedDates is not null;
    }
}
