using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;

namespace IMLibrary.Controls.Toolbox
{
    partial class Toolbox
    {
        /// <summary>
        /// Represents a collection of the <see cref="Item">items</see> in the <see cref="Toolbox"/>.
        /// </summary>
        [Serializable]
        public class ItemCollection : CollectionBase, ICollection, ICollection<Item>, IEnumerable<Item>
        {
            private ITab _owner;
            private bool _suspendInvalidate = false;

            /// <summary>
            /// Initializes a new instance of the <see cref="ItemCollection"/> class using the specified <see cref="ITab">owner</see>.
            /// </summary>
            /// <param name="owner">The owner of the collection</param>
            public ItemCollection(ITab owner)
            {
                _owner = owner;
            }

            internal Tab Tab
            {
                get { return _owner as Tab; }
            }

            #region Overridables
            /// <summary>
            /// Overriden.
            /// </summary>
            protected override void OnInsertComplete(int index, object value)
            {
                Item item = value as Item;
                if (item == null)
                    throw new Exception();
                item.Owner = _owner;

                OnCountChanged(EventArgs.Empty);

                if (Tab != null && !Tab.PointerItem.Enabled)
                    Tab.PointerItem.Enabled = true;

                if (!_suspendInvalidate)
                {
                    _owner.Invalidate();
                }
            }

            /// <summary>
            /// Overriden.
            /// </summary>
            protected override void OnRemoveComplete(int index, object value)
            {
                Item item = value as Item;
                item.Owner = null;

                OnCountChanged(EventArgs.Empty);

                if (Count == 0)
                {
                    OnEmptied(EventArgs.Empty);
                    if (Tab != null)
                        Tab.PointerItem.Enabled = false;
                }
                _owner.Invalidate();
            }

            /// <summary>
            /// Overriden.
            /// </summary>
            protected override void OnClear()
            {
                foreach (Item item in this.List)
                    item.Owner = null;
            }

            /// <summary>
            /// Overriden.
            /// </summary>
            protected override void OnClearComplete()
            {
                OnCountChanged(EventArgs.Empty);
                OnEmptied(EventArgs.Empty);

                _owner.Invalidate();
            }

            /// <summary>
            /// Overriden.
            /// </summary>
            protected override void OnSetComplete(int index, object oldValue, object newValue)
            {
                Item value = newValue as Item;
                if (value == null)
                    throw new ArgumentException();
                if (value.Owner != null)
                    throw new ArgumentException(Properties.Resources.ToolboxExceptionItemHasOtherOwner, "newValue");
                value.Owner = _owner;
                ((Item)oldValue).Owner = null;
                _owner.Invalidate();
            }
            #endregion

            #region ICollection<Item> Members

            /// <summary>
            /// Adds an <paramref name="item"/> to the collection.
            /// </summary>
            /// <param name="item">An <see cref="Item"/> to add.</param>
            public void Add(Item item)
            {
                if (item == null)
                    throw new ArgumentNullException("item");

                if (Contains(item))
                    throw new ArgumentException(Properties.Resources.ToolboxExceptionItemIsInCollection, "item");

                if (item.Owner != null)
                    throw new ArgumentException(Properties.Resources.ToolboxExceptionItemHasOtherOwner, "item");

                item.Owner = _owner;
                this.List.Add(item);
            }

            /// <summary>
            /// Adds a range of <paramref name="items"/> to the collection.
            /// </summary>
            /// <param name="items">An array of <see cref="Item"/> objects to add.</param>
            public void AddRange(Item[] items)
            {
                if (items == null)
                    throw new ArgumentNullException("items");

                this._suspendInvalidate = true;
                try
                {
                    foreach (Item item in items)
                        Add(item);
                }
                finally
                {
                    this._suspendInvalidate = false;
                }
            }

            /// <summary>
            /// Determines whether an <paramref name="item"/> is in the collection.
            /// </summary>
            /// <param name="item">The <see cref="Item"/> to locate in the collection.</param>
            /// <returns><b>true</b> if item is found in the collection; otherwise, <b>false</b>.</returns>
            public bool Contains(Item item)
            {
                return this.InnerList.Contains(item);
            }

            void ICollection<Item>.CopyTo(Item[] array, int arrayIndex)
            {
                this.InnerList.CopyTo(array, arrayIndex);
            }

            bool ICollection<Item>.IsReadOnly
            {
                get { return this.InnerList.IsReadOnly; }
            }

            /// <summary>
            /// Removes an <paramref name="item"/> from the collection.
            /// </summary>
            /// <param name="item">An <see cref="Item"/> to remove.</param>
            /// <returns><b>true</b> if item is found in the collection and successfully removed; otherwise <b>false</b>.</returns>
            public bool Remove(Item item)
            {
                if (item == null)
                    throw new ArgumentNullException("item");

                if (this.Contains(item))
                {
                    this.List.Remove(item);
                    item.Owner = null;
                    _owner.Invalidate();
                    return true;
                }
                return false;
            }

            /// <summary>
            /// Sorts the items in the collection using the default comparer.
            /// </summary>
            public void Sort()
            {
                this.Sort(0, this.Count, null);
            }

            /// <summary>
            /// Sorts the items in the collection using the specified <paramref name="comparer"/>.
            /// </summary>
            /// <param name="comparer"></param>
            public void Sort(IComparer<Item> comparer)
            {
                this.Sort(0, this.Count, comparer);
            }

            /// <summary>
            /// Sorts the items in the collection using the specified <paramref name="comparison"/> delegate.
            /// </summary>
            /// <param name="comparison"></param>
            public void Sort(Comparison<Item> comparison)
            {
                if (comparison == null)
                {
                    throw new ArgumentNullException("comparison");
                }
                if (Count > 0)
                {
                    Item[] items = new Item[Count];
                    this.InnerList.CopyTo(items, 0);
                    Array.Sort<Item>(items, comparison);
                    this.InnerList.Clear();
                    this.InnerList.AddRange(items);
                    _owner.Invalidate();
                }
            }

            /// <summary>
            /// Sorts the specified range of items in the collection using the specified <paramref name="comparer"/>.
            /// </summary>
            /// <param name="index"></param>
            /// <param name="count"></param>
            /// <param name="comparer"></param>
            public void Sort(int index, int count, IComparer<Item> comparer)
            {
                if ((index < 0) || (count < 0))
                {
                    throw new ArgumentOutOfRangeException();
                }
                if ((this.Count - index) < count)
                {
                    throw new ArgumentException();
                }
                Item[] items = new Item[Count];
                this.InnerList.CopyTo(items, 0);
                Array.Sort<Item>(items, index, count, comparer);
                this.InnerList.Clear();
                this.InnerList.AddRange(items);
                _owner.Invalidate();
            }

            #endregion

            #region IEnumerable<Item> Members

            /// <summary>
            /// Returns an <see cref="IEnumerator"/> for the <see cref="ItemCollection"/>.
            /// </summary>
            public new IEnumerator<Item> GetEnumerator()
            {
                return new ItemEnumerator(this.List);
            }

            #endregion

            #region Public Methods
            /// <summary>
            /// Searches the specified <paramref name="item"/> in the collection and returns a zero-based index of it.
            /// </summary>
            /// <param name="item">An <see cref="Item"/> to search.</param>
            /// <returns></returns>
            public int IndexOf(Item item)
            {
                if (item == null)
                    throw new ArgumentNullException("item");
                return this.InnerList.IndexOf(item);
            }

            /// <summary>
            /// Inserts an <paramref name="item"/> into the collection at the specified <paramref name="index"/>.
            /// </summary>
            /// <param name="index">The zero-based index.</param>
            /// <param name="item">An <see cref="Item"/> to insert.</param>
            public void Insert(int index, Item item)
            {
                if (Contains(item))
                    throw new ArgumentException(Properties.Resources.ToolboxExceptionItemIsInCollection, "item");

                this.List.Insert(index, item);
            }

            /// <summary>
            /// Inserts an <paramref name="item"/> into the collection just after the specified item.
            /// </summary>
            /// <param name="after">An <see cref="Item"/> after which to insert new item.</param>
            /// <param name="item">An <see cref="Item"/> to insert.</param>
            public void InsertAfter(Item after, Item item)
            {
                if (after.Owner != _owner)
                    throw new ArgumentException(Properties.Resources.ToolboxExceptionItemHasOtherOwner, "after");

                if (Tab != null && after == Tab.PointerItem)
                    Insert(0, item);
                else
                {
                    int index = IndexOf(after);
                    Insert(index + 1, item);
                }
            }
            #endregion

            #region Public Properties
            /// <summary>
            /// Gets or sets an <see cref="Item"/> on the specified index.
            /// </summary>
            /// <param name="index">The zero-based index of the <see cref="Item"/> to get or set.</param>
            /// <returns>An <see cref="Item"/> on the specified index.</returns>
            public Item this[int index]
            {
                get
                {
                    return (Item)List[index];
                }
                set
                {
                    List[index] = value;
                }
            }

            /// <summary>
            /// Gets the <see cref="ITab">object</see> that contains the <see cref="Item"/>.
            /// </summary>
            public ITab Owner
            {
                get { return _owner; }
            }
            #endregion

            #region Events
            /// <summary>
            /// Occurs when the <see cref="ItemCollection"/> is emptied.
            /// </summary>
            public event EventHandler Emptied;

            /// <summary>
            /// Occurs when the <see cref="ItemCollection"/> is changed.
            /// </summary>
            public event EventHandler CountChanged;

            /// <summary>
            /// Raises an <see cref="Emptied"/> event.
            /// </summary>
            protected virtual void OnEmptied(EventArgs e)
            {
                if (Emptied != null)
                    Emptied(this.Owner, e);
            }

            /// <summary>
            /// Raises a <see cref="CountChanged"/> event.
            /// </summary>
            /// <param name="e"></param>
            protected virtual void OnCountChanged(EventArgs e)
            {
                if (CountChanged != null)
                    CountChanged(this.Owner, e);
            }
            #endregion

            private class ItemEnumerator : IEnumerator<Item>
            {
                private IList _items;
                private int _currentIndex = -1;
                private Item _currentItem = null;

                public ItemEnumerator(IList items)
                {
                    _items = items;
                }

                #region IEnumerator<Item> Members

                public Item Current
                {
                    get 
                    {
                        if (_currentItem != null)
                            return _currentItem;
                        throw new InvalidOperationException();
                    }
                }

                #endregion

                #region IDisposable Members

                public void Dispose()
                {
                }

                #endregion

                #region IEnumerator Members

                object IEnumerator.Current
                {
                    get { return this.Current; }
                }

                public bool MoveNext()
                {
                    if (_currentIndex < _items.Count - 1)
                    {
                        _currentIndex++;
                        _currentItem = (Item)_items[_currentIndex];
                        return true;
                    }
                    _currentItem = null;
                    _currentIndex = _items.Count;
                    return false;
                }

                public void Reset()
                {
                    _currentItem = null;
                    _currentIndex = -1;
                }

                #endregion
            }
        }
    }
}
