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

namespace Microsoft.AspNetCore.Razor.Language.Intermediate
{
	public struct IntermediateNodeReference
	{
		public IntermediateNodeReference( IntermediateNode parent, IntermediateNode node )
		{
			if ( parent == null )
			{
				throw new ArgumentNullException( nameof( parent ) );
			}

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

			Parent = parent;
			Node = node;
		}

		public void Deconstruct( out IntermediateNode parent, out IntermediateNode node )
		{
			parent = Parent;
			node = Node;
		}

		public IntermediateNode Node { get; }

		public IntermediateNode Parent { get; }

		public IntermediateNodeReference InsertAfter( IntermediateNode node )
		{
			if ( node == null )
			{
				throw new ArgumentNullException( nameof( node ) );
			}

			if ( Parent == null )
			{
				throw new InvalidOperationException( Resources.IntermediateNodeReference_NotInitialized );
			}

			if ( Parent.Children.IsReadOnly )
			{
				throw new InvalidOperationException();
			}

			var index = Parent.Children.IndexOf( Node );
			if ( index == -1 )
			{
				throw new InvalidOperationException();
			}

			Parent.Children.Insert( index + 1, node );
			return new IntermediateNodeReference( Parent, node );
		}

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

			if ( Parent == null )
			{
				throw new InvalidOperationException( Resources.IntermediateNodeReference_NotInitialized );
			}

			if ( Parent.Children.IsReadOnly )
			{
				throw new InvalidOperationException();
			}

			var index = Parent.Children.IndexOf( Node );
			if ( index == -1 )
			{
				throw new InvalidOperationException();
			}

			foreach ( var node in nodes )
			{
				Parent.Children.Insert( ++index, node );
			}
		}

		public IntermediateNodeReference InsertBefore( IntermediateNode node )
		{
			if ( node == null )
			{
				throw new ArgumentNullException( nameof( node ) );
			}

			if ( Parent == null )
			{
				throw new InvalidOperationException( Resources.IntermediateNodeReference_NotInitialized );
			}

			if ( Parent.Children.IsReadOnly )
			{
				throw new InvalidOperationException();
			}

			var index = Parent.Children.IndexOf( Node );
			if ( index == -1 )
			{
				throw new InvalidOperationException();
			}

			Parent.Children.Insert( index, node );
			return new IntermediateNodeReference( Parent, node );
		}

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

			if ( Parent == null )
			{
				throw new InvalidOperationException( Resources.IntermediateNodeReference_NotInitialized );
			}

			if ( Parent.Children.IsReadOnly )
			{
				throw new InvalidOperationException();
			}

			var index = Parent.Children.IndexOf( Node );
			if ( index == -1 )
			{
				throw new InvalidOperationException();
			}

			foreach ( var node in nodes )
			{
				Parent.Children.Insert( index++, node );
			}
		}

		public void Remove()
		{
			if ( Parent == null )
			{
				throw new InvalidOperationException( Resources.IntermediateNodeReference_NotInitialized );
			}

			if ( Parent.Children.IsReadOnly )
			{
				throw new InvalidOperationException();
			}

			var index = Parent.Children.IndexOf( Node );
			if ( index == -1 )
			{
				throw new InvalidOperationException();
			}

			Parent.Children.RemoveAt( index );
		}

		public IntermediateNodeReference Replace( IntermediateNode node )
		{
			if ( node == null )
			{
				throw new ArgumentNullException( nameof( node ) );
			}

			if ( Parent == null )
			{
				throw new InvalidOperationException( Resources.IntermediateNodeReference_NotInitialized );
			}

			if ( Parent.Children.IsReadOnly )
			{
				throw new InvalidOperationException();
			}

			var index = Parent.Children.IndexOf( Node );
			if ( index == -1 )
			{
				throw new InvalidOperationException();
			}

			Parent.Children[index] = node;
			return new IntermediateNodeReference( Parent, node );
		}
	}
}
