using Microsoft.UI.Xaml;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
using Telerik.UI.Xaml.Controls.Grid;

namespace TelerikWinUIApp18
{
    public static class DatasGridUtilities
    {
        public static readonly DependencyProperty SelectedItemsProperty = DependencyProperty.RegisterAttached(
            "SelectedItems",
            typeof(INotifyCollectionChanged),
            typeof(DatasGridUtilities),
            new PropertyMetadata(null, OnSelectedItemsChanged));

        private static bool isSyncingSelection;
        private static List<Tuple<WeakReference, List<RadDataGrid>>> collectionToDataGrids = new List<Tuple<WeakReference, List<RadDataGrid>>>();

        public static INotifyCollectionChanged GetSelectedItems(DependencyObject obj)
        {
            return (INotifyCollectionChanged)obj.GetValue(SelectedItemsProperty);
        }

        public static void SetSelectedItems(DependencyObject obj, INotifyCollectionChanged value)
        {
            obj.SetValue(SelectedItemsProperty, value);
        }

        private static void OnSelectedItemsChanged(DependencyObject target, DependencyPropertyChangedEventArgs args)
        {
            var dataGrid = (RadDataGrid)target;

            var oldCollection = args.OldValue as INotifyCollectionChanged;
            if (oldCollection != null)
            {
                dataGrid.SelectionChanged -= DataGrid_SelectionChanged;
                oldCollection.CollectionChanged -= SelectedItems_CollectionChanged;
                RemoveAssociation(oldCollection, dataGrid);
            }

            var newCollection = args.NewValue as INotifyCollectionChanged;
            if (newCollection != null)
            {
                dataGrid.SelectionChanged += DataGrid_SelectionChanged;
                newCollection.CollectionChanged += SelectedItems_CollectionChanged;
                AddAssociation(newCollection, dataGrid);
                OnSelectedItemsChanged(newCollection, null, (IList)newCollection);
            }
        }

        private static void SelectedItems_CollectionChanged(object sender, NotifyCollectionChangedEventArgs args)
        {
            INotifyCollectionChanged collection = (INotifyCollectionChanged)sender;
            OnSelectedItemsChanged(collection, args.OldItems, args.NewItems);
        }

        private static void OnSelectedItemsChanged(INotifyCollectionChanged collection, IList oldItems, IList newItems)
        {
            isSyncingSelection = true;

            var dataGrids = GetOrCreateDataGrids(collection);
            foreach (var dataGrid in dataGrids)
            {
                SyncSelection(dataGrid, oldItems, newItems);
            }

            isSyncingSelection = false;
        }

        private static void DataGrid_SelectionChanged(object sender, DataGridSelectionChangedEventArgs args)
        {
            if (isSyncingSelection)
            {
                return;
            }

            var collection = (IList)GetSelectedItems((RadDataGrid)sender);
            foreach (object item in args.RemovedItems)
            {
                collection.Remove(item);
            }
            foreach (object item in args.AddedItems)
            {
                collection.Add(item);
            }
        }

        private static void SyncSelection(RadDataGrid dataGrid, IList oldItems, IList newItems)
        {
            if (oldItems != null)
            {
                SetItemsSelection(dataGrid, oldItems, false);
            }

            if (newItems != null)
            {
                SetItemsSelection(dataGrid, newItems, true);
            }
        }

        private static void SetItemsSelection(RadDataGrid dataGrid, IList items, bool shouldSelect)
        {
            foreach (var item in items)
            {
                bool contains = dataGrid.SelectedItems.Contains(item);
                if (shouldSelect && !contains)
                {
                    dataGrid.SelectedItems.Add(item);
                }
                else if (contains && !shouldSelect)
                {
                    dataGrid.SelectedItems.Remove(item);
                }
            }
        }

        private static void AddAssociation(INotifyCollectionChanged collection, RadDataGrid dataGrid)
        {
            List<RadDataGrid> dataGrids = GetOrCreateDataGrids(collection);
            dataGrids.Add(dataGrid);
        }

        private static void RemoveAssociation(INotifyCollectionChanged collection, RadDataGrid dataGrid)
        {
            List<RadDataGrid> dataGrids = GetOrCreateDataGrids(collection);
            dataGrids.Remove(dataGrid);

            if (dataGrids.Count == 0)
            {
                CleanUp();
            }
        }

        private static List<RadDataGrid> GetOrCreateDataGrids(INotifyCollectionChanged collection)
        {
            for (int i = 0; i < collectionToDataGrids.Count; i++)
            {
                var wr = collectionToDataGrids[i].Item1;
                if (wr.Target == collection)
                {
                    return collectionToDataGrids[i].Item2;
                }
            }

            collectionToDataGrids.Add(new Tuple<WeakReference, List<RadDataGrid>>(new WeakReference(collection), new List<RadDataGrid>()));
            return collectionToDataGrids[collectionToDataGrids.Count - 1].Item2;
        }

        private static void CleanUp()
        {
            for (int i = collectionToDataGrids.Count - 1; i >= 0; i--)
            {
                bool isAlive = collectionToDataGrids[i].Item1.IsAlive;
                var behaviors = collectionToDataGrids[i].Item2;
                if (behaviors.Count == 0 || !isAlive)
                {
                    collectionToDataGrids.RemoveAt(i);
                }
            }
        }
    }
}
