#region License

/*
 * Copyright 2002-2004 the original author or authors.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#endregion

#region Imports

using System;
using System.Collections;

#endregion

namespace Spring.Objects
{
	public interface ITestObjectCollection
	{
		int Count { get; }

		bool IsSynchronized { get; }

		object SyncRoot { get; }

		void CopyTo(TestObject[] array, int arrayIndex);

		ITestObjectEnumerator GetEnumerator();
	}

	public interface ITestObjectList : ITestObjectCollection
	{
		bool IsFixedSize { get; }

		bool IsReadOnly { get; }

		TestObject this[int index] { get; set; }

		int Add(TestObject value);

		void Clear();

		bool Contains(TestObject value);

		int IndexOf(TestObject value);

		void Insert(int index, TestObject value);

		void Remove(TestObject value);

		void RemoveAt(int index);
	}

	public interface ITestObjectEnumerator
	{
		TestObject Current { get; }

		bool MoveNext();

		void Reset();
	}

	[Serializable]
	public class TestObjectList : ITestObjectList, IList, ICloneable
	{
		private const int _defaultCapacity = 16;

		private TestObject[] _array = null;
		private int _count = 0;

		[NonSerialized] private int _version = 0;

		public TestObjectList()
		{
			this._array = new TestObject[_defaultCapacity];
		}

		public TestObjectList(int capacity)
		{
			if (capacity < 0)
				throw new ArgumentOutOfRangeException("capacity",
				                                      capacity, "Argument cannot be negative.");

			this._array = new TestObject[capacity];
		}

		public TestObjectList(TestObjectList collection)
		{
			if (collection == null)
				throw new ArgumentNullException("collection");

			this._array = new TestObject[collection.Count];
			AddRange(collection);
		}

		public TestObjectList(TestObject[] array)
		{
			if (array == null)
				throw new ArgumentNullException("array");

			this._array = new TestObject[array.Length];
			AddRange(array);
		}

		protected virtual TestObject[] InnerArray
		{
			get { return this._array; }
		}

		public virtual int Capacity
		{
			get { return this._array.Length; }
			set
			{
				if (value == this._array.Length) return;

				if (value < this._count)
					throw new ArgumentOutOfRangeException("Capacity",
					                                      value, "Value cannot be less than Count.");

				if (value == 0)
				{
					this._array = new TestObject[_defaultCapacity];
					return;
				}

				TestObject[] newArray = new TestObject[value];
				Array.Copy(this._array, newArray, this._count);
				this._array = newArray;
			}
		}

		public virtual int Count
		{
			get { return this._count; }
		}

		public virtual bool IsFixedSize
		{
			get { return false; }
		}

		public virtual bool IsReadOnly
		{
			get { return false; }
		}

		public virtual bool IsSynchronized
		{
			get { return false; }
		}

		public virtual bool IsUnique
		{
			get { return false; }
		}

		public virtual TestObject this[int index]
		{
			get
			{
				ValidateIndex(index);
				return this._array[index];
			}
			set
			{
				ValidateIndex(index);
				++this._version;
				this._array[index] = value;
			}
		}

		object IList.this[int index]
		{
			get { return this[index]; }
			set { this[index] = (TestObject) value; }
		}

		public virtual object SyncRoot
		{
			get { return this; }
		}

		public virtual int Add(TestObject value)
		{
			if (this._count == this._array.Length)
				EnsureCapacity(this._count + 1);

			++this._version;
			this._array[this._count] = value;
			return this._count++;
		}

		int IList.Add(object value)
		{
			return Add((TestObject) value);
		}

		public virtual void AddRange(TestObjectList collection)
		{
			if (collection == null)
				throw new ArgumentNullException("collection");

			if (collection.Count == 0) return;
			if (this._count + collection.Count > this._array.Length)
				EnsureCapacity(this._count + collection.Count);

			++this._version;
			Array.Copy(collection.InnerArray, 0,
			           this._array, this._count, collection.Count);
			this._count += collection.Count;
		}

		public virtual void AddRange(TestObject[] array)
		{
			if (array == null)
				throw new ArgumentNullException("array");

			if (array.Length == 0) return;
			if (this._count + array.Length > this._array.Length)
				EnsureCapacity(this._count + array.Length);

			++this._version;
			Array.Copy(array, 0, this._array, this._count, array.Length);
			this._count += array.Length;
		}

		public virtual int BinarySearch(TestObject value)
		{
			return Array.BinarySearch(this._array, 0, this._count, value);
		}

		public virtual void Clear()
		{
			if (this._count == 0) return;

			++this._version;
			Array.Clear(this._array, 0, this._count);
			this._count = 0;
		}

		public virtual object Clone()
		{
			TestObjectList collection = new TestObjectList(this._count);

			Array.Copy(this._array, 0, collection._array, 0, this._count);
			collection._count = this._count;
			collection._version = this._version;

			return collection;
		}

		public bool Contains(TestObject value)
		{
			return (IndexOf(value) >= 0);
		}

		bool IList.Contains(object value)
		{
			return Contains((TestObject) value);
		}

		public virtual void CopyTo(TestObject[] array)
		{
			CheckTargetArray(array, 0);
			Array.Copy(this._array, array, this._count);
		}

		public virtual void CopyTo(TestObject[] array, int arrayIndex)
		{
			CheckTargetArray(array, arrayIndex);
			Array.Copy(this._array, 0, array, arrayIndex, this._count);
		}

		void ICollection.CopyTo(Array array, int arrayIndex)
		{
			CheckTargetArray(array, arrayIndex);
			CopyTo((TestObject[]) array, arrayIndex);
		}

		public virtual ITestObjectEnumerator GetEnumerator()
		{
			return new Enumerator(this);
		}

		IEnumerator IEnumerable.GetEnumerator()
		{
			return (IEnumerator) GetEnumerator();
		}

		public virtual int IndexOf(TestObject value)
		{
			return Array.IndexOf(this._array, value, 0, this._count);
		}

		int IList.IndexOf(object value)
		{
			return IndexOf((TestObject) value);
		}

		public virtual void Insert(int index, TestObject value)
		{
			if (index < 0)
				throw new ArgumentOutOfRangeException("index",
				                                      index, "Argument cannot be negative.");

			if (index > this._count)
				throw new ArgumentOutOfRangeException("index",
				                                      index, "Argument cannot exceed Count.");

			if (this._count == this._array.Length)
				EnsureCapacity(this._count + 1);

			++this._version;
			if (index < this._count)
				Array.Copy(this._array, index,
				           this._array, index + 1, this._count - index);

			this._array[index] = value;
			++this._count;
		}

		void IList.Insert(int index, object value)
		{
			Insert(index, (TestObject) value);
		}

		public virtual void Remove(TestObject value)
		{
			int index = IndexOf(value);
			if (index >= 0) RemoveAt(index);
		}

		void IList.Remove(object value)
		{
			Remove((TestObject) value);
		}

		public virtual void RemoveAt(int index)
		{
			ValidateIndex(index);

			++this._version;
			if (index < --this._count)
				Array.Copy(this._array, index + 1,
				           this._array, index, this._count - index);

			this._array[this._count] = null;
		}

		public virtual void RemoveRange(int index, int count)
		{
			if (index < 0)
				throw new ArgumentOutOfRangeException("index",
				                                      index, "Argument cannot be negative.");

			if (count < 0)
				throw new ArgumentOutOfRangeException("count",
				                                      count, "Argument cannot be negative.");

			if (index + count > this._count)
				throw new ArgumentException(
					"Arguments denote invalid range of elements.");

			if (count == 0) return;

			++this._version;
			this._count -= count;

			if (index < this._count)
				Array.Copy(this._array, index + count,
				           this._array, index, this._count - index);

			Array.Clear(this._array, this._count, count);
		}

		public virtual void Reverse()
		{
			if (this._count <= 1) return;
			++this._version;
			Array.Reverse(this._array, 0, this._count);
		}

		public virtual void Reverse(int index, int count)
		{
			if (index < 0)
				throw new ArgumentOutOfRangeException("index",
				                                      index, "Argument cannot be negative.");

			if (count < 0)
				throw new ArgumentOutOfRangeException("count",
				                                      count, "Argument cannot be negative.");

			if (index + count > this._count)
				throw new ArgumentException(
					"Arguments denote invalid range of elements.");

			if (count <= 1 || this._count <= 1) return;
			++this._version;
			Array.Reverse(this._array, index, count);
		}

		public virtual void Sort()
		{
			if (this._count <= 1) return;
			++this._version;
			Array.Sort(this._array, 0, this._count);
		}

		public virtual void Sort(IComparer comparer)
		{
			if (this._count <= 1) return;
			++this._version;
			Array.Sort(this._array, 0, this._count, comparer);
		}

		public virtual void Sort(int index, int count, IComparer comparer)
		{
			if (index < 0)
				throw new ArgumentOutOfRangeException("index",
				                                      index, "Argument cannot be negative.");

			if (count < 0)
				throw new ArgumentOutOfRangeException("count",
				                                      count, "Argument cannot be negative.");

			if (index + count > this._count)
				throw new ArgumentException(
					"Arguments denote invalid range of elements.");

			if (count <= 1 || this._count <= 1) return;
			++this._version;
			Array.Sort(this._array, index, count, comparer);
		}

		public virtual TestObject[] ToArray()
		{
			TestObject[] array = new TestObject[this._count];
			Array.Copy(this._array, array, this._count);
			return array;
		}

		public virtual void TrimToSize()
		{
			Capacity = this._count;
		}

		private void CheckEnumIndex(int index)
		{
			if (index < 0 || index >= this._count)
				throw new InvalidOperationException(
					"Enumerator is not on a collection element.");
		}

		private void CheckEnumVersion(int version)
		{
			if (version != this._version)
				throw new InvalidOperationException(
					"Enumerator invalidated by modification to collection.");
		}

		private void CheckTargetArray(Array array, int arrayIndex)
		{
			if (array == null)
				throw new ArgumentNullException("array");
			if (array.Rank > 1)
				throw new ArgumentException(
					"Argument cannot be multidimensional.", "array");

			if (arrayIndex < 0)
				throw new ArgumentOutOfRangeException("arrayIndex",
				                                      arrayIndex, "Argument cannot be negative.");
			if (arrayIndex >= array.Length)
				throw new ArgumentException(
					"Argument must be less than array length.", "arrayIndex");

			if (this._count > array.Length - arrayIndex)
				throw new ArgumentException(
					"Argument section must be large enough for collection.", "array");
		}

		private void EnsureCapacity(int minimum)
		{
			int newCapacity = (this._array.Length == 0 ?
				_defaultCapacity : this._array.Length*2);

			if (newCapacity < minimum) newCapacity = minimum;
			Capacity = newCapacity;
		}

		private void ValidateIndex(int index)
		{
			if (index < 0)
				throw new ArgumentOutOfRangeException("index",
				                                      index, "Argument cannot be negative.");

			if (index >= this._count)
				throw new ArgumentOutOfRangeException("index",
				                                      index, "Argument must be less than Count.");
		}

		[Serializable]
		private sealed class Enumerator : ITestObjectEnumerator, IEnumerator
		{
			private readonly TestObjectList _collection;
			private readonly int _version;
			private int _index;

			internal Enumerator(TestObjectList collection)
			{
				this._collection = collection;
				this._version = collection._version;
				this._index = -1;
			}

			public TestObject Current
			{
				get
				{
					this._collection.CheckEnumIndex(this._index);
					this._collection.CheckEnumVersion(this._version);
					return this._collection[this._index];
				}
			}

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

			public bool MoveNext()
			{
				this._collection.CheckEnumVersion(this._version);
				return (++this._index < this._collection.Count);
			}

			public void Reset()
			{
				this._collection.CheckEnumVersion(this._version);
				this._index = -1;
			}
		}
	}
}
