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

namespace IMLibrary.Controls.Toolbox
{
    partial class Toolbox
    {
        /// <summary>
        /// Represents a collection of the <see cref="Tab">categories</see> of the <see cref="T:IMLibrary.Components.Toolbox"/> control.
        /// </summary>
        [Serializable]
        public sealed class TabCollection : CollectionBase, ICollection, IEnumerable, ICollection<Tab>, IEnumerable<Tab>
        {
            private ITab _owner;

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

            internal Toolbox Toolbox
            {
                get
                {
                    Tab tab = _owner as Tab;
                    if (tab != null)
                    {
                        return tab.Toolbox;
                    }
                    return (Toolbox)_owner;
                }
            }

            #region IList<Tab> Members

            /// <summary>
            /// Searches the specified <paramref name="tab"/> in the collection and returns a zero-based index of it.
            /// </summary>
            /// <param name="tab">A <see cref="Tab"/> to search.</param>
            /// <returns></returns>
            public int IndexOf(Tab tab)
            {
                return InnerList.IndexOf(tab);
            }

            /// <summary>
            /// Inserts a <paramref name="tab"/> into the collection at the specified <paramref name="index"/>.
            /// </summary>
            /// <param name="index">The zero-based index.</param>
            /// <param name="tab">A <see cref="Tab"/> to insert.</param>
            public void Insert(int index, Tab tab)
            {
                try
                {
                    this.List.Insert(index, tab);
                }
                catch (DuplicateNameException)
                {
                    throw;
                }
            }

            /// <summary>
            /// Gets or sets the <see cref="Tab"/> at the specified index.
            /// </summary>
            /// <param name="index">The zero-based index of the <see cref="Tab"/> to get or set.</param>
            /// <returns>The <see cref="Tab"/> at the specified index.</returns>
            public Tab this[int index]
            {
                get
                {
                    return (Tab)InnerList[index];
                }
                set
                {
                    InnerList[index] = value;
                }
            }

            /// <summary>
            /// Gets the <see cref="Tab"/> with the specified name.
            /// </summary>
            /// <param name="name">The name of the <see cref="Tab"/> to get.</param>
            /// <returns>The <see cref="Tab"/> with the specified name.</returns>
            public Tab this[string name]
            {
                get
                {
                    foreach (Tab tab in InnerList)
                        if (tab.Text == name)
                            return tab;
                    return null;
                }
            }

            #endregion

            #region ICollection<Tab> Members

            /// <summary>
            /// Adds a <paramref name="tab"/> to the collection.
            /// </summary>
            /// <param name="tab">A <see cref="Tab"/> to add.</param>
            public void Add(Tab tab)
            {
                if (this._owner == tab)
                    throw new ArgumentException(Properties.Resources.ToolboxExceptionTabAddSelf, "tab");

                try
                {
                    if (this[tab.Text] != null)
                        throw new DuplicateNameException();

                    this.List.Add(tab);
                    tab.Owner = _owner;
                }
                catch (DuplicateNameException)
                {
                    throw;
                }
                _owner.Invalidate();
            }

            /// <summary>
            /// Adds a range of <paramref name="tabs"/> to the collection.
            /// </summary>
            /// <param name="tabs">An array of <see cref="Tab"/> objects to add.</param>
            public void AddRange(Tab[] tabs)
            {
                foreach (Tab tab in tabs)
                {
                    try
                    {
                        this.List.Add(tab);
                        tab.Owner = _owner;
                    }
                    catch (DuplicateNameException)
                    {
                        throw;
                    }
                }
                _owner.Invalidate();
            }

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

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

            /// <summary>
            /// Removes a <paramref name="tab"/> from the collection.
            /// </summary>
            /// <param name="tab">A <see cref="Tab"/> to remove.</param>
            /// <returns><b>true</b> if tab is found in the collection and successfully removed; otherwise <b>false</b>.</returns>
            public bool Remove(Tab tab)
            {
                try
                {
                    int index = this.IndexOf(tab);
                    if (index >= 0)
                        this.RemoveAt(index);
                }
                catch { }
                return tab.Owner == null;
            }

            bool ICollection<Tab>.IsReadOnly
            {
                get { return false; }
            }

            #endregion

            #region IEnumerable<Tab> Members

            IEnumerator<Tab> IEnumerable<Tab>.GetEnumerator()
            {
                return new Enumerator(this);
            }

            #endregion

            #region Overridables

            /// <summary>
            /// Overriden.
            /// </summary>
            protected override void OnInsert(int index, object value)
            {
                Tab tab = value as Tab;
                if (tab == null)
                    throw new ArgumentNullException("value");

                if (this[tab.Text] != null)
                {
                    Toolbox toolbox = this.Toolbox;
                    if (toolbox != null)
                    {
                        tab.Text = Toolbox.GetUnusedCategoryName(_owner);
                    }
                    else
                    {
                        throw new System.Data.DuplicateNameException();
                    }
                }

                base.OnInsert(index, value);
            }

            /// <summary>
            /// Overriden.
            /// </summary>
            protected override void OnInsertComplete(int index, object value)
            {
                Tab tab = value as Tab;                
                tab.Owner = this._owner;
                this._owner.Invalidate();
            }

            /// <summary>
            /// Overriden.
            /// </summary>
            protected override void OnRemoveComplete(int index, object value)
            {
                Tab tab = value as Tab;
                Toolbox toolbox = this.Toolbox;
                if (toolbox != null && (toolbox.Site == null || !toolbox.Site.DesignMode) && !tab.AllowDelete)
                    throw new Exception();
                
                tab.Owner = null;
                _owner.Invalidate();
            }

            /// <summary>
            /// Overriden.
            /// </summary>
            protected override void OnClearComplete()
            {
                Toolbox toolbox = _owner as Toolbox;
                if (toolbox != null && toolbox.GeneralCategory != null && toolbox.GeneralCategory.Owner == toolbox)
                {
                    this.List.Add(toolbox.GeneralCategory);
                }
                _owner.Invalidate();
            }

            /// <summary>
            /// Overriden.
            /// </summary>
            protected override void OnSetComplete(int index, object oldValue, object newValue)
            {
                Tab oldTab = oldValue as Tab;
                Tab newTab = newValue as Tab;
                Insert(index + 1, oldTab);
            }

            /// <summary>
            /// Overriden.
            /// </summary>
            protected override void OnValidate(object value)
            {
                if (!(value is Tab))
                    throw new InvalidCastException();
            }
            #endregion

            #region Enumerator
            private struct Enumerator : IEnumerator<Tab>, IDisposable, IEnumerator
            {
                private TabCollection list;
                private int index;
                private Tab current;

                internal Enumerator(TabCollection list)
                {
                    this.list = list;
                    this.index = 0;
                    this.current = null;
                }

                public void Dispose()
                {
                }

                public bool MoveNext()
                {
                    if (this.index < this.list.Count)
                    {
                        this.current = this.list[this.index];
                        this.index++;
                        return true;
                    }
                    this.index = this.list.Count + 1;
                    this.current = null;
                    return false;
                }
                public Tab Current
                {
                    get
                    {
                        return this.current;
                    }
                }
                object IEnumerator.Current
                {
                    get
                    {
                        if ((this.index == 0) || (this.index == (this.list.Count + 1)))
                        {
                            throw new ArgumentOutOfRangeException("index");
                        }
                        return this.Current;
                    }
                }
                void IEnumerator.Reset()
                {
                    this.index = 0;
                    this.current = null;
                }
            }
            #endregion
        }
    }
}
