// 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.Diagnostics;

namespace Microsoft.AspNetCore.Razor.Language.Syntax.InternalSyntax
{
	internal struct SyntaxList<TNode> : IEquatable<SyntaxList<TNode>>
		where TNode : GreenNode
	{
		private readonly GreenNode _node;

		public SyntaxList( GreenNode node )
		{
			_node = node;
		}

		internal GreenNode Node => _node;

		public int Count
		{
			get
			{
				return (_node == null) ? 0 : _node.IsList ? _node.SlotCount : 1;
			}
		}

		public TNode this[int index]
		{
			get
			{
				if ( _node == null )
				{
					return null;
				}
				else if ( _node.IsList )
				{
					Debug.Assert( index >= 0 );
					Debug.Assert( index <= _node.SlotCount );

					return ((TNode)_node.GetSlot( index ));
				}
				else if ( index == 0 )
				{
					return ((TNode)_node);
				}
				else
				{
					throw new InvalidOperationException( "This program location is thought to be unreachable." );
				}
			}
		}

		public GreenNode ItemUntyped( int index )
		{
			var node = _node;
			if ( node.IsList )
			{
				return node.GetSlot( index );
			}

			Debug.Assert( index == 0 );
			return node;
		}

		public bool Any()
		{
			return _node != null;
		}

		public bool Any( SyntaxKind kind )
		{
			for ( var i = 0; i < Count; i++ )
			{
				var element = ItemUntyped( i );
				if ( (element.Kind == kind) )
				{
					return true;
				}
			}

			return false;
		}

		public TNode[] Nodes
		{
			get
			{
				var arr = new TNode[Count];
				for ( var i = 0; i < Count; i++ )
				{
					arr[i] = this[i];
				}

				return arr;
			}
		}

		public TNode Last
		{
			get
			{
				var node = _node;
				if ( node.IsList )
				{
					return ((TNode)node.GetSlot( node.SlotCount - 1 ));
				}

				return ((TNode)node);
			}
		}

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

		public static bool operator ==( SyntaxList<TNode> left, SyntaxList<TNode> right )
		{
			return (left._node == right._node);
		}

		public static bool operator !=( SyntaxList<TNode> left, SyntaxList<TNode> right )
		{
			return !(left._node == right._node);
		}

		public bool Equals( SyntaxList<TNode> other )
		{
			return _node == other._node;
		}

		public override bool Equals( object obj )
		{
			return (obj is SyntaxList<TNode> && (_node == ((SyntaxList<TNode>)obj)._node));
		}

		public override int GetHashCode()
		{
			return _node != null ? _node.GetHashCode() : 0;
		}

		public static implicit operator SyntaxList<TNode>( TNode node )
		{
			return new SyntaxList<TNode>( node );
		}

		public static implicit operator SyntaxList<GreenNode>( SyntaxList<TNode> nodes )
		{
			return new SyntaxList<GreenNode>( nodes._node );
		}

		internal struct Enumerator
		{
			private readonly SyntaxList<TNode> _list;
			private int _index;

			internal Enumerator( SyntaxList<TNode> list )
			{
				_list = list;
				_index = -1;
			}

			public bool MoveNext()
			{
				var newIndex = _index + 1;
				if ( newIndex < _list.Count )
				{
					_index = newIndex;
					return true;
				}

				return false;
			}

			public TNode Current
			{
				get
				{
					return _list[_index];
				}
			}
		}
	}
}
