//
// Created by Eugeny Grishul
//
// See license at http://bamelg.com/license.txt
//

namespace System.Collections {
	public class List<T> {
		private T[] _items;
		private int _count, _version;

		public List() {
			_items = new[16] T;
		}

		public List( int capacity ) {
			_items = new[capacity] T;
		}

		public List( T* source, int capacity )
			: this( capacity ) {
			AddRange( source, capacity );
		}

		public List( List<T> source )
			: this( source.GetBuffer(), source.Count ) {
		}

		public void Add( T item ) {
			if( _count == _items.Length )
				EnsureCapacity( _count + 1 );

			_items[_count++] = item;
			CommonCollectionOperations.UpdateVersion( &_version );
		}

		public void AddRange( T* items, int count ) {
			EnsureCapacity( _count + count );
			CommonCollectionOperations.Copy<T>( &_items[_count], items, count );
			_count += count;
			CommonCollectionOperations.UpdateVersion( &_version );
		}

		public void AddRange( List<T> values ) { AddRange( values.GetBuffer(), values.Count ); }

		public void Insert( int index, T item ) {
			if( !Assert.IsFalse( index > _count ) ) return;

			if( _count == _items.Length )
				EnsureCapacity( _count + 1 );

			if( index < _count )
				CommonCollectionOperations.Copy<T>( &_items[index + 1], &_items[index], _count - index );

			_items[index] = item;
			++_count;
			CommonCollectionOperations.UpdateVersion( &_version );
		}

		public void Clear() {
			CommonCollectionOperations.Clear<T>( &_items[0], _count );

			_count = 0;
			CommonCollectionOperations.UpdateVersion( &_version );
		}

		public int Count { get { return _count; } }

		public T this[int index] {
			get { return _items[index]; }
			set { _items[index] = value; }
		}

		public void Reserve( int newCount ) {
			if( newCount <= _count ) return;

			if( newCount < _items.Length ) {
				_count = newCount;
				return;
			}

			EnsureCapacity( newCount );
			_count = newCount;
		}

		public int Capacity {
			get {
				return ( int ) _items.Length;
			}
			set {
				if( value != _items.Length ) {
					if( !Assert.IsFalse( value < _count ) )
						return;

					if( value > 0 ) {
						var destinationArray = new[value] T;
						CommonCollectionOperations.Copy<T>( &destinationArray[0], &_items[0], _count );
						_items = destinationArray;
					}
				}
			}
		}

		public int IndexOf( T value ) {
			for( int i = 0; i < _count; ++i )
				if( _items[i] == value ) return i;

			return -1;
		}

		public bool Contains( T value ) { return IndexOf( value ) != -1; }

		public int LastIndexOf( T value ) {
			for( int i = _count - 1; i >= 0; --i )
				if( _items[i] == value ) return i;

			return -1;
		}

		public int IndexOf( T value, int startIndex ) {
			if( !Assert.IsFalse( startIndex < 0 | startIndex >= _count ) )
				return -1;

			for( int i = startIndex; i < _count; ++i )
				if( _items[i] == value ) return i;

			return -1;
		}

		public int LastIndexOf( T value, int startIndex ) {
			if( !Assert.IsFalse( startIndex < 0 | startIndex >= _count ) )
				return -1;

			for( int i = _count - 1; i >= startIndex; --i )
				if( _items[i] == value ) return i;

			return -1;
		}

		public int IndexOf( T value, int startIndex, int count ) {
			if( !Assert.IsFalse( startIndex < 0 | startIndex >= _count ) )
				return -1;

			if( !Assert.IsFalse( count < 0 | startIndex + count > _count ) )
				return -1;

			for( int i = startIndex; i < _count; ++i )
				if( _items[i] == value ) return i;

			return -1;
		}

		public int LastIndexOf( T value, int startIndex, int count ) {
			if( !Assert.IsFalse( startIndex < 0 | startIndex >= _count ) )
				return -1;

			if( !Assert.IsFalse( count < 0 | startIndex + count > _count ) )
				return -1;

			for( int i = _count - 1; i >= startIndex | count > 0; --i, --count )
				if( _items[i] == value ) return i;

			return -1;
		}

		public void RemoveAt( int index ) {
			RemoveAt( index, 1 );
		}

		public void RemoveAt( int index, int count ) {
			if( !Assert.IsFalse( index < 0 || index >= _count ) )
				return;

			if( !Assert.IsFalse( count < 0 || index + count > _count ) )
				return;

			_count -= count;

			if( index < _count )
				CommonCollectionOperations.Copy<T>( &_items[index], &_items[index + count], _count - index );

			CommonCollectionOperations.Clear<T>( &_items[_count], count );
			CommonCollectionOperations.UpdateVersion( &_version );
		}

		public bool Remove( T item ) {
			var index = IndexOf( item );

			if( index >= 0 ) {
				RemoveAt( index );
				return true;
			}

			return false;
		}

		public void Reverse() {
			for( int i = 0; i < _count / 2; ++i )
				CommonCollectionOperations.Swap<T>( _items[i], _items[_count - i - 1] );
		}

		public T[] ToArray() {
			var result = new[_count] T;
			CommonCollectionOperations.Copy<T>( &result[0], &_items[0], _count );
			return result;
		}

		// Result will be invalid after resizing
		public T* GetBuffer() {
			return &_items[0];
		}

		private void EnsureCapacity( int recommendedCapacity ) {
			if( _items.Length >= recommendedCapacity ) return;

			Capacity = Math.Max( _items.Length * 2, ( int ) recommendedCapacity );
		}

		public void RemoveRange( int startIndex, int length ) {
			if( !Assert.IsFalse( startIndex < 0 | startIndex > _count ) )
				return;

			if( !Assert.IsFalse( length < 0 | startIndex > _count - length ) )
				return;

			CommonCollectionOperations.Copy<T>( &_items[startIndex], &_items[startIndex + length], _count - length - startIndex );
			_count -= length;
		}

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

		[EnumeratorAttribute]
		public struct Enumerator {
			private readonly declaringtype _parent;
			private readonly int _version;
			private int _index;

			internal Enumerator( declaringtype parent ) {
				_index = -1;
				_parent = parent;
				_version = parent._version;
			}

			public bool MoveNext() {
				CommonCollectionOperations.VersionCheck( _version == _parent._version );

				return ++_index < _parent.Count;
			}

			public T Current { get { return _parent[_index]; } }
		}
	}
}