using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Svg
{
    /// 
    /// Represents a collection of s.
    /// 
    public sealed class SvgElementCollection : IList
    {
        private List _elements;
        private SvgElement _owner;
        private bool _mock;
        /// 
        /// Initialises a new instance of an  class.
        /// 
        /// The owner  of the collection.
        internal SvgElementCollection(SvgElement owner)
            : this(owner, false)
        {
        }
        internal SvgElementCollection(SvgElement owner, bool mock)
        {
            if (owner == null)
            {
                throw new ArgumentNullException("owner");
            }
            this._elements = new List();
            this._owner = owner;
            this._mock = mock;
        }
        /// 
        /// Returns the index of the specified  in the collection.
        /// 
        /// The  to search for.
        /// The index of the element if it is present; otherwise -1.
        public int IndexOf(SvgElement item)
        {
            return this._elements.IndexOf(item);
        }
        /// 
        /// Inserts the given  to the collection at the specified index.
        /// 
        /// The index that the  should be added at.
        /// The  to be added.
        public void Insert(int index, SvgElement item)
        {
            InsertAndForceUniqueID(index, item, false, false);
        }
        public void InsertAndForceUniqueID(int index, SvgElement item, bool autoForceUniqueID = true, bool autoFixChildrenID = true, Action logElementOldIDNewID = null)
        {
            AddToIdManager(item, this._elements[index], autoForceUniqueID, autoFixChildrenID, logElementOldIDNewID);
            this._elements.Insert(index, item);
            item._parent.OnElementAdded(item, index);
        }
        public void RemoveAt(int index)
        {
            SvgElement element = this[index];
            if (element != null)
            {
                this.Remove(element);
            }
        }
        public SvgElement this[int index]
        {
            get { return this._elements[index]; }
            set { this._elements[index] = value; }
        }
        public void Add(SvgElement item)
        {
            this.AddAndForceUniqueID(item, false, false);
        }
        public void AddAndForceUniqueID(SvgElement item, bool autoForceUniqueID = true, bool autoFixChildrenID = true, Action logElementOldIDNewID = null)
        {
            AddToIdManager(item, null, autoForceUniqueID, autoFixChildrenID, logElementOldIDNewID);
            this._elements.Add(item);
            item._parent.OnElementAdded(item, this.Count - 1);
        }
        private void AddToIdManager(SvgElement item, SvgElement sibling, bool autoForceUniqueID = true, bool autoFixChildrenID = true, Action logElementOldIDNewID = null)
        {
            if (!this._mock)
            {
            	if (this._owner.OwnerDocument != null)
            	{
                    this._owner.OwnerDocument.IdManager.AddAndForceUniqueID(item, sibling, autoForceUniqueID, logElementOldIDNewID);
                    if (!(item is SvgDocument)) //don't add subtree of a document to parent document
                    {
                        foreach (var child in item.Children)
                        {
                            child.ApplyRecursive(e => this._owner.OwnerDocument.IdManager.AddAndForceUniqueID(e, null, autoFixChildrenID, logElementOldIDNewID));
                        }
                    }
                }
                
                //if all checked, set parent
                item._parent = this._owner;
            }
        }
        public void Clear()
        {
            while (this.Count > 0)
            {
                SvgElement element = this[0];
                this.Remove(element);
            }
        }
        public bool Contains(SvgElement item)
        {
            return this._elements.Contains(item);
        }
        public void CopyTo(SvgElement[] array, int arrayIndex)
        {
            this._elements.CopyTo(array, arrayIndex);
        }
        public int Count
        {
            get { return this._elements.Count; }
        }
        public bool IsReadOnly
        {
            get { return false; }
        }
        public bool Remove(SvgElement item)
        {
            bool removed = this._elements.Remove(item);
            if (removed)
            {
                this._owner.OnElementRemoved(item);
                if (!this._mock)
                {
                    item._parent = null;
                    if (this._owner.OwnerDocument != null)
                    {
                        item.ApplyRecursive(this._owner.OwnerDocument.IdManager.Remove);
                    }
                }
            }
            return removed;
        }
		/// 
		/// expensive recursive search for nodes of type T
		/// 
		/// 
		/// 
		public IEnumerable FindSvgElementsOf() where T : SvgElement
		{
			return _elements.Where(x => x is T).Select(x => x as T).Concat(_elements.SelectMany(x => x.Children.FindSvgElementsOf()));
		}
		/// 
		/// expensive recursive search for first node of type T
		/// 
		/// 
		/// 
		public T FindSvgElementOf() where T : SvgElement
		{
			return _elements.OfType().FirstOrDefault() ?? _elements.Select(x => x.Children.FindSvgElementOf()).FirstOrDefault(x => x != null);
		}
		public T GetSvgElementOf() where T : SvgElement
		{
			return _elements.FirstOrDefault(x => x is T) as T;
		}
        public IEnumerator GetEnumerator()
        {
            return this._elements.GetEnumerator();
        }
        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
        {
            return this._elements.GetEnumerator();
        }
    }
}