// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

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

namespace Microsoft.AspNetCore.Razor.Language.Intermediate
{
	public sealed class IntermediateNodeCollection : IList<IntermediateNode>
	{
		public static readonly IntermediateNodeCollection ReadOnly = new IntermediateNodeCollection( new List<IntermediateNode>().AsReadOnly() );

		private readonly IList<IntermediateNode> _inner;

		public IntermediateNodeCollection()
			: this( new List<IntermediateNode>() )
		{
		}

		private IntermediateNodeCollection( IList<IntermediateNode> inner )
		{
			_inner = inner;
		}

		public IntermediateNode this[int index]
		{
			get
			{
				if ( index < 0 || index >= Count )
				{
					throw new ArgumentOutOfRangeException( nameof( index ) );
				}

				return _inner[index];
			}
			set
			{
				if ( index < 0 || index >= Count )
				{
					throw new ArgumentOutOfRangeException( nameof( index ) );
				}

				_inner[index] = value;
			}
		}

		public int Count => _inner.Count;

		public bool IsReadOnly => _inner.IsReadOnly;

		public void Add( IntermediateNode item )
		{
			if ( item == null )
			{
				throw new ArgumentNullException( nameof( item ) );
			}

			_inner.Add( item );
		}

		public void AddRange( IEnumerable<IntermediateNode> items )
		{
			if ( items == null )
			{
				throw new ArgumentNullException( nameof( items ) );
			}

			foreach ( var item in items )
			{
				_inner.Add( item );
			}
		}

		public void AddRange( IntermediateNodeCollection items )
		{
			if ( items == null )
			{
				throw new ArgumentNullException( nameof( items ) );
			}

			var count = items.Count;
			for ( var i = 0; i < count; i++ )
			{
				_inner.Add( items[i] );
			}
		}

		public void Clear()
		{
			_inner.Clear();
		}

		public bool Contains( IntermediateNode item )
		{
			return _inner.Contains( item );
		}

		public void CopyTo( IntermediateNode[] array, int arrayIndex )
		{
			if ( array == null )
			{
				throw new ArgumentNullException( nameof( array ) );
			}

			if ( arrayIndex < 0 || arrayIndex > array.Length )
			{
				throw new ArgumentOutOfRangeException( nameof( arrayIndex ) );
			}
			else if ( array.Length - arrayIndex < Count )
			{
				throw new ArgumentOutOfRangeException( nameof( arrayIndex ) );
			}

			_inner.CopyTo( array, arrayIndex );
		}

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

		IEnumerator<IntermediateNode> IEnumerable<IntermediateNode>.GetEnumerator()
		{
			return GetEnumerator();
		}

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

		public int IndexOf( IntermediateNode item )
		{
			if ( item == null )
			{
				throw new ArgumentNullException( nameof( item ) );
			}

			return _inner.IndexOf( item );
		}

		public void Insert( int index, IntermediateNode item )
		{
			if ( index < 0 || index > Count )
			{
				throw new ArgumentOutOfRangeException( nameof( index ) );
			}

			if ( item == null )
			{
				throw new ArgumentNullException( nameof( item ) );
			}

			_inner.Insert( index, item );
		}

		public bool Remove( IntermediateNode item )
		{
			if ( item == null )
			{
				throw new ArgumentNullException( nameof( item ) );
			}

			return _inner.Remove( item );
		}

		public void RemoveAt( int index )
		{
			if ( index < 0 || index >= Count )
			{
				throw new ArgumentOutOfRangeException( nameof( index ) );
			}

			_inner.RemoveAt( index );
		}

		public struct Enumerator : IEnumerator<IntermediateNode>
		{
			private readonly IList<IntermediateNode> _items;
			private int _index;

			public Enumerator( IntermediateNodeCollection collection )
			{
				if ( collection == null )
				{
					throw new ArgumentNullException( nameof( collection ) );
				}

				_items = collection._inner;
				_index = -1;
			}

			public IntermediateNode Current
			{
				get
				{
					if ( _index < 0 || _index >= _items.Count )
					{
						return null;
					}

					return _items[_index];
				}
			}

			object IEnumerator.Current => Current;

			public void Dispose()
			{
			}

			public bool MoveNext()
			{
				_index++;
				return _index < _items.Count;
			}

			public void Reset()
			{
				_index = -1;
			}
		}
	}
}
