// using System;
// using System.Collections;
// using System.Collections.ObjectModel;
//
// namespace InABox.Avalonia
// {
// // Licensed to the .NET Foundation under one or more agreements.
// // The .NET Foundation licenses this file to you under the MIT license.
// // See the LICENSE file in the project root for more information.
//
// using System.Collections.Generic;
// using System.Collections.Specialized;
// using System.ComponentModel;
// using System.Diagnostics;
// using System.Linq;
//
// ///
// /// Implementation of a dynamic data collection based on generic Collection<T>,
// /// implementing INotifyCollectionChanged to notify listeners
// /// when items get added, removed or the whole list is refreshed.
// ///
// /// Modified to Implement Cross-thread Synchronization of events
// /// Will trigger event callbacks on creating, rather than current thread.
// /// Collections should be created on Msin UI thread the to prevent exceptions
// ///
// public class CoreObservableCollection : ObservableCollection
// {
// //------------------------------------------------------
// //
// // Private Fields
// //
// //------------------------------------------------------
//
// #region Private Fields
//
// [NonSerialized] private DeferredEventsCollection? _deferredEvents;
//
// [NonSerialized] private SynchronizationContext? _synchronizationContext = SynchronizationContext.Current;
//
// #endregion Private Fields
//
//
// //------------------------------------------------------
// //
// // Constructors
// //
// //------------------------------------------------------
//
// #region Constructors
//
// ///
// /// Initializes a new instance of ObservableCollection that is empty and has default initial capacity.
// ///
// public CoreObservableCollection()
// {
// }
//
// ///
// /// Initializes a new instance of the ObservableCollection class that contains
// /// elements copied from the specified collection and has sufficient capacity
// /// to accommodate the number of elements copied.
// ///
// /// The collection whose elements are copied to the new list.
// ///
// /// The elements are copied onto the ObservableCollection in the
// /// same order they are read by the enumerator of the collection.
// ///
// /// collection is a null reference
// public CoreObservableCollection(IEnumerable collection) : base(collection)
// {
// }
//
// ///
// /// Initializes a new instance of the ObservableCollection class
// /// that contains elements copied from the specified list
// ///
// /// The list whose elements are copied to the new list.
// ///
// /// The elements are copied onto the ObservableCollection in the
// /// same order they are read by the enumerator of the list.
// ///
// /// list is a null reference
// public CoreObservableCollection(List list) : base(list)
// {
// }
//
// #endregion Constructors
//
// //------------------------------------------------------
// //
// // Public Properties
// //
// //------------------------------------------------------
//
// #region Public Properties
//
// EqualityComparer? _Comparer;
//
// public EqualityComparer Comparer
// {
// get => _Comparer ??= EqualityComparer.Default;
// private set => _Comparer = value;
// }
//
// ///
// /// Gets or sets a value indicating whether this collection acts as a ,
// /// disallowing duplicate items, based on .
// /// This might indeed consume background performance, but in the other hand,
// /// it will pay off in UI performance as less required UI updates are required.
// ///
// public bool AllowDuplicates { get; set; } = true;
//
// // If the collection is created on a non-UI thread, but needs to update the UI,
// // we should be able to set the UI context here to prevent exceptions
// public SynchronizationContext? SynchronizationContext
// {
// get => _synchronizationContext;
// set => _synchronizationContext = value;
// }
//
// #endregion Public Properties
//
// //------------------------------------------------------
// //
// // Public Methods
// //
// //------------------------------------------------------
//
// #region Public Methods
//
// ///
// /// Adds the elements of the specified collection to the end of the .
// ///
// ///
// /// The collection whose elements should be added to the end of the .
// /// The collection itself cannot be null, but it can contain elements that are null, if type T is a reference type.
// ///
// /// is null.
// public void AddRange(IEnumerable collection)
// {
// InsertRange(Count, collection);
// }
//
// ///
// /// Inserts the elements of a collection into the at the specified index.
// ///
// /// The zero-based index at which the new elements should be inserted.
// /// The collection whose elements should be inserted into the List.
// /// The collection itself cannot be null, but it can contain elements that are null, if type T is a reference type.
// /// is null.
// /// is not in the collection range.
// public void InsertRange(int index, IEnumerable collection)
// {
// if (collection == null)
// throw new ArgumentNullException(nameof(collection));
// if (index < 0)
// throw new ArgumentOutOfRangeException(nameof(index));
// if (index > Count)
// throw new ArgumentOutOfRangeException(nameof(index));
//
// if (!AllowDuplicates)
// collection =
// collection
// .Distinct(Comparer)
// .Where(item => !Items.Contains(item, Comparer))
// .ToList();
//
// if (collection is ICollection countable)
// {
// if (countable.Count == 0)
// return;
// }
// else if (!collection.Any())
// return;
//
// CheckReentrancy();
//
// //expand the following couple of lines when adding more constructors.
// var target = (List)Items;
// target.InsertRange(index, collection);
//
// OnEssentialPropertiesChanged();
//
// if (!(collection is IList list))
// list = new List(collection);
//
// OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, list, index));
// }
//
//
// ///
// /// Removes the first occurence of each item in the specified collection from the .
// ///
// /// The items to remove.
// /// is null.
// public void RemoveRange(IEnumerable collection)
// {
// if (collection == null)
// throw new ArgumentNullException(nameof(collection));
//
// if (Count == 0)
// return;
// else if (collection is ICollection countable)
// {
// if (countable.Count == 0)
// return;
// else if (countable.Count == 1)
// using (IEnumerator enumerator = countable.GetEnumerator())
// {
// enumerator.MoveNext();
// Remove(enumerator.Current);
// return;
// }
// }
// else if (!collection.Any())
// return;
//
// CheckReentrancy();
//
// var clusters = new Dictionary>();
// var lastIndex = -1;
// List? lastCluster = null;
// foreach (T item in collection)
// {
// var index = IndexOf(item);
// if (index < 0)
// continue;
//
// Items.RemoveAt(index);
//
// if (lastIndex == index && lastCluster != null)
// lastCluster.Add(item);
// else
// clusters[lastIndex = index] = lastCluster = new List { item };
// }
//
// OnEssentialPropertiesChanged();
//
// if (Count == 0)
// OnCollectionReset();
// else
// foreach (KeyValuePair> cluster in clusters)
// OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove,
// cluster.Value, cluster.Key));
// }
//
// ///
// /// Iterates over the collection and removes all items that satisfy the specified match.
// ///
// /// The complexity is O(n).
// ///
// /// Returns the number of elements that where
// /// is null.
// public int RemoveAll(Predicate match)
// {
// return RemoveAll(0, Count, match);
// }
//
// ///
// /// Iterates over the specified range within the collection and removes all items that satisfy the specified match.
// ///
// /// The complexity is O(n).
// /// The index of where to start performing the search.
// /// The number of items to iterate on.
// ///
// /// Returns the number of elements that where
// /// is out of range.
// /// is out of range.
// /// is null.
// public int RemoveAll(int index, int count, Predicate match)
// {
// if (index < 0)
// throw new ArgumentOutOfRangeException(nameof(index));
// if (count < 0)
// throw new ArgumentOutOfRangeException(nameof(count));
// if (index + count > Count)
// throw new ArgumentOutOfRangeException(nameof(index));
// if (match == null)
// throw new ArgumentNullException(nameof(match));
//
// if (Count == 0)
// return 0;
//
// List? cluster = null;
// var clusterIndex = -1;
// var removedCount = 0;
//
// using (BlockReentrancy())
// using (DeferEvents())
// {
// for (var i = 0; i < count; i++, index++)
// {
// T item = Items[index];
// if (match(item))
// {
// Items.RemoveAt(index);
// removedCount++;
//
// if (clusterIndex == index)
// {
// Debug.Assert(cluster != null);
// cluster!.Add(item);
// }
// else
// {
// cluster = new List { item };
// clusterIndex = index;
// }
//
// index--;
// }
// else if (clusterIndex > -1)
// {
// OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove,
// cluster, clusterIndex));
// clusterIndex = -1;
// cluster = null;
// }
// }
//
// if (clusterIndex > -1)
// OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove,
// cluster, clusterIndex));
// }
//
// if (removedCount > 0)
// OnEssentialPropertiesChanged();
//
// return removedCount;
// }
//
// ///
// /// Removes a range of elements from the >.
// ///
// /// The zero-based starting index of the range of elements to remove.
// /// The number of elements to remove.
// /// The specified range is exceeding the collection.
// public void RemoveRange(int index, int count)
// {
// if (index < 0)
// throw new ArgumentOutOfRangeException(nameof(index));
// if (count < 0)
// throw new ArgumentOutOfRangeException(nameof(count));
// if (index + count > Count)
// throw new ArgumentOutOfRangeException(nameof(index));
//
// if (count == 0)
// return;
//
// if (count == 1)
// {
// RemoveItem(index);
// return;
// }
//
// //Items will always be List, see constructors
// var items = (List)Items;
// List removedItems = items.GetRange(index, count);
//
// CheckReentrancy();
//
// items.RemoveRange(index, count);
//
// OnEssentialPropertiesChanged();
//
// if (Count == 0)
// OnCollectionReset();
// else
// OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove,
// removedItems, index));
// }
//
// ///
// /// Clears the current collection and replaces it with the specified collection,
// /// using .
// ///
// /// The items to fill the collection with, after clearing it.
// /// is null.
// public void ReplaceRange(IEnumerable collection)
// {
// ReplaceRange(0, Count, collection);
// }
//
// ///
// /// Removes the specified range and inserts the specified collection in its position, leaving equal items in equal positions intact.
// ///
// /// The index of where to start the replacement.
// /// The number of items to be replaced.
// /// The collection to insert in that location.
// /// is out of range.
// /// is out of range.
// /// is null.
// /// is null.
// public void ReplaceRange(int index, int count, IEnumerable collection)
// {
// if (index < 0)
// throw new ArgumentOutOfRangeException(nameof(index));
// if (count < 0)
// throw new ArgumentOutOfRangeException(nameof(count));
// if (index + count > Count)
// throw new ArgumentOutOfRangeException(nameof(index));
//
// if (collection == null)
// throw new ArgumentNullException(nameof(collection));
//
// if (!AllowDuplicates)
// collection =
// collection
// .Distinct(Comparer)
// .ToList();
//
// if (collection is ICollection countable)
// {
// if (countable.Count == 0)
// {
// RemoveRange(index, count);
// return;
// }
// }
// else if (!collection.Any())
// {
// RemoveRange(index, count);
// return;
// }
//
// if (index + count == 0)
// {
// InsertRange(0, collection);
// return;
// }
//
// if (!(collection is IList list))
// list = new List(collection);
//
// using (BlockReentrancy())
// using (DeferEvents())
// {
// var rangeCount = index + count;
// var addedCount = list.Count;
//
// var changesMade = false;
// List?
// newCluster = null,
// oldCluster = null;
//
//
// int i = index;
// for (; i < rangeCount && i - index < addedCount; i++)
// {
// //parallel position
// T old = this[i], @new = list[i - index];
// if (Comparer.Equals(old, @new))
// {
// OnRangeReplaced(i, newCluster!, oldCluster!);
// continue;
// }
// else
// {
// Items[i] = @new;
//
// if (newCluster == null)
// {
// Debug.Assert(oldCluster == null);
// newCluster = new List { @new };
// oldCluster = new List { old };
// }
// else
// {
// newCluster.Add(@new);
// oldCluster!.Add(old);
// }
//
// changesMade = true;
// }
// }
//
// OnRangeReplaced(i, newCluster!, oldCluster!);
//
// //exceeding position
// if (count != addedCount)
// {
// var items = (List)Items;
// if (count > addedCount)
// {
// var removedCount = rangeCount - addedCount;
// T[] removed = new T[removedCount];
// items.CopyTo(i, removed, 0, removed.Length);
// items.RemoveRange(i, removedCount);
// OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove,
// removed, i));
// }
// else
// {
// var k = i - index;
// T[] added = new T[addedCount - k];
// for (int j = k; j < addedCount; j++)
// {
// T @new = list[j];
// added[j - k] = @new;
// }
//
// items.InsertRange(i, added);
// OnCollectionChanged(
// new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, added, i));
// }
//
// OnEssentialPropertiesChanged();
// }
// else if (changesMade)
// {
// OnIndexerPropertyChanged();
// }
// }
// }
//
// #endregion Public Methods
//
//
// //------------------------------------------------------
// //
// // Protected Methods
// //
// //------------------------------------------------------
//
// #region Protected Methods
//
// ///
// /// Called by base class Collection<T> when the list is being cleared;
// /// raises a CollectionChanged event to any listeners.
// ///
// protected override void ClearItems()
// {
// if (Count == 0)
// return;
//
// CheckReentrancy();
// base.ClearItems();
// OnEssentialPropertiesChanged();
// OnCollectionReset();
// }
//
// ///
// protected override void InsertItem(int index, T item)
// {
// if (!AllowDuplicates && Items.Contains(item))
// return;
//
// base.InsertItem(index, item);
// }
//
// ///
// protected override void SetItem(int index, T item)
// {
// if (AllowDuplicates)
// {
// if (Comparer.Equals(this[index], item))
// return;
// }
// else if (Items.Contains(item, Comparer))
// return;
//
// CheckReentrancy();
// T oldItem = this[index];
// base.SetItem(index, item);
//
// OnIndexerPropertyChanged();
// OnCollectionChanged(NotifyCollectionChangedAction.Replace, oldItem!, item!, index);
// }
//
// ///
// /// Raise CollectionChanged event to any listeners.
// /// Properties/methods modifying this ObservableCollection will raise
// /// a collection changed event through this virtual method.
// ///
// ///
// /// When overriding this method, either call its base implementation
// /// or call to guard against reentrant collection changes.
// ///
// protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
// {
// if (_synchronizationContext == null || SynchronizationContext.Current == _synchronizationContext)
// {
// // Execute the CollectionChanged event on the current thread
// RaiseCollectionChanged(e);
// }
// else
// {
// // Raises the CollectionChanged event on the creator thread
// _synchronizationContext.Send(RaiseCollectionChanged, e);
// }
// }
//
// private void RaiseCollectionChanged(object? param)
// {
// // We are in the creator thread, call the base implementation directly
// if (param is NotifyCollectionChangedEventArgs args)
// {
// if (_deferredEvents != null)
// {
// _deferredEvents.Add(args);
// return;
// }
//
// base.OnCollectionChanged(args);
// }
// }
//
// protected virtual IDisposable DeferEvents() => new DeferredEventsCollection(this);
//
// #endregion Protected Methods
//
//
// //------------------------------------------------------
// //
// // Private Methods
// //
// //------------------------------------------------------
//
// #region Private Methods
//
// ///
// /// Helper to raise Count property and the Indexer property.
// ///
// void OnEssentialPropertiesChanged()
// {
// OnPropertyChanged(EventArgsCache.CountPropertyChanged);
// OnIndexerPropertyChanged();
// }
//
// ///
// /// /// Helper to raise a PropertyChanged event for the Indexer property
// /// ///
// void OnIndexerPropertyChanged() =>
// OnPropertyChanged(EventArgsCache.IndexerPropertyChanged);
//
// protected override void OnPropertyChanged(PropertyChangedEventArgs e)
// {
// if (_synchronizationContext == null || SynchronizationContext.Current == _synchronizationContext)
// {
// // Execute the PropertyChanged event on the current thread
// RaisePropertyChanged(e);
// }
// else
// {
// // Raises the PropertyChanged event on the creator thread
// _synchronizationContext.Send(RaisePropertyChanged, e);
// }
// }
//
// private void RaisePropertyChanged(object? param)
// {
// // We are in the creator thread, call the base implementation directly
// if (param is PropertyChangedEventArgs args)
// base.OnPropertyChanged(args);
// }
//
// ///
// /// Helper to raise CollectionChanged event to any listeners
// ///
// void OnCollectionChanged(NotifyCollectionChangedAction action, object oldItem, object newItem, int index) =>
// OnCollectionChanged(new NotifyCollectionChangedEventArgs(action, newItem, oldItem, index));
//
// ///
// /// Helper to raise CollectionChanged event with action == Reset to any listeners
// ///
// void OnCollectionReset() =>
// OnCollectionChanged(EventArgsCache.ResetCollectionChanged);
//
// ///
// /// Helper to raise event for clustered action and clear cluster.
// ///
// /// The index of the item following the replacement block.
// ///
// ///
// //TODO should have really been a local method inside ReplaceRange(int index, int count, IEnumerable collection, IEqualityComparer comparer),
// //move when supported language version updated.
// void OnRangeReplaced(int followingItemIndex, ICollection newCluster, ICollection oldCluster)
// {
// if (oldCluster == null || oldCluster.Count == 0)
// {
// Debug.Assert(newCluster == null || newCluster.Count == 0);
// return;
// }
//
// OnCollectionChanged(
// new NotifyCollectionChangedEventArgs(
// NotifyCollectionChangedAction.Replace,
// new List(newCluster),
// new List(oldCluster),
// followingItemIndex - oldCluster.Count));
//
// oldCluster.Clear();
// newCluster.Clear();
// }
//
// #endregion Private Methods
//
// //------------------------------------------------------
// //
// // Private Types
// //
// //------------------------------------------------------
//
// #region Private Types
//
// sealed class DeferredEventsCollection : List, IDisposable
// {
// readonly CoreObservableCollection _collection;
//
// public DeferredEventsCollection(CoreObservableCollection collection)
// {
// Debug.Assert(collection != null);
// Debug.Assert(collection._deferredEvents == null);
// _collection = collection;
// _collection._deferredEvents = this;
// }
//
// public void Dispose()
// {
// _collection._deferredEvents = null;
// foreach (var args in this)
// _collection.OnCollectionChanged(args);
// }
// }
//
// #endregion Private Types
// }
//
// ///
// /// To be kept outside , since otherwise, a new instance will be created for each generic type used.
// ///
// internal static class EventArgsCache
// {
// internal static readonly PropertyChangedEventArgs CountPropertyChanged = new PropertyChangedEventArgs("Count");
//
// internal static readonly PropertyChangedEventArgs IndexerPropertyChanged =
// new PropertyChangedEventArgs("Item[]");
//
// internal static readonly NotifyCollectionChangedEventArgs ResetCollectionChanged =
// new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset);
// }
// }