// 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;
using System.Linq;

namespace Microsoft.AspNetCore.Razor.Language.Intermediate
{
	public static class IntermediateNodeExtensions
	{
		private static readonly IReadOnlyList<RazorDiagnostic> EmptyDiagnostics = Array.Empty<RazorDiagnostic>();

		public static bool IsImported( this IntermediateNode node )
		{
			return ReferenceEquals( node.Annotations[CommonAnnotations.Imported], CommonAnnotations.Imported );
		}

		public static IReadOnlyList<RazorDiagnostic> GetAllDiagnostics( this IntermediateNode node )
		{
			if ( node == null )
			{
				throw new ArgumentNullException( nameof( node ) );
			}

			HashSet<RazorDiagnostic> diagnostics = null;

			AddAllDiagnostics( node );

			var allOrderedDiagnostics = diagnostics?.OrderBy( diagnostic => diagnostic.Span.AbsoluteIndex );

			return allOrderedDiagnostics?.ToList() ?? EmptyDiagnostics;

			void AddAllDiagnostics( IntermediateNode n )
			{
				if ( n.HasDiagnostics )
				{
					if ( diagnostics == null )
					{
						diagnostics = new HashSet<RazorDiagnostic>();
					}

					diagnostics.UnionWith( n.Diagnostics );
				}

				for ( var i = 0; i < n.Children.Count; i++ )
				{
					AddAllDiagnostics( n.Children[i] );
				}
			}
		}

		public static IReadOnlyList<TNode> FindDescendantNodes<TNode>( this IntermediateNode node )
			where TNode : IntermediateNode
		{
			var visitor = new Visitor<TNode>();
			visitor.Visit( node );

			if ( visitor.Results.Count > 0 && visitor.Results[0] == node )
			{
				// Don't put the node itself in the results
				visitor.Results.Remove( (TNode)node );
			}

			return visitor.Results;
		}

		private class Visitor<TNode> : IntermediateNodeWalker where TNode : IntermediateNode
		{
			public List<TNode> Results { get; } = new List<TNode>();

			public override void VisitDefault( IntermediateNode node )
			{
				if ( node is TNode match )
				{
					Results.Add( match );
				}

				base.VisitDefault( node );
			}
		}
	}
}
