using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using Loyc;
using Loyc.Collections;
using S = Loyc.Syntax.CodeSymbols;
namespace Loyc.Syntax
{
	/// <summary>Standard extension methods for <see cref="LNode"/>.</summary>
	public static class LNodeExt
	{
		#region Trivia management
		public static VList<LNode> GetTrivia(this LNode node) { return GetTrivia(node.Attrs); }
		public static VList<LNode> GetTrivia(this VList<LNode> attrs)
		{
			var trivia = VList<LNode>.Empty;
			foreach (var a in attrs)
				if (a.IsTrivia)
					trivia.Add(a);
			return trivia;
		}
		/// <summary>Gets all trailing trivia attached to the specified node.</summary>
		public static VList<LNode> GetTrailingTrivia(this LNode node) { return GetTrailingTrivia(node.Attrs); }
		/// <summary>Gets all trailing trivia attached to the specified node.</summary>
		/// <remarks>Trailing trivia is represented by a call to #trivia_trailing in
		/// a node's attribute list; each argument to #trivia_trailing represents one
		/// piece of trivia. If the attribute list has multiple calls to 
		/// #trivia_trailing, this method combines those lists into a single list.</remarks>
		public static VList<LNode> GetTrailingTrivia(this VList<LNode> attrs)
		{
			var trivia = VList<LNode>.Empty;
			foreach (var a in attrs)
				if (a.Calls(S.TriviaTrailing))
					trivia.AddRange(a.Args);
			return trivia;
		}
		/// <summary>Removes a node's trailing trivia and adds a new list of trailing trivia.</summary>
		public static LNode WithTrailingTrivia(this LNode node, VList<LNode> trivia)
		{
			return node.WithAttrs(WithTrailingTrivia(node.Attrs, trivia));
		}
		/// <summary>Removes all existing trailing trivia from an attribute list and adds a new list of trailing trivia.</summary>
		/// <remarks>This method has a side-effect of recreating the #trivia_trailing
		/// node, if there is one, at the end of the attribute list. If <c>trivia</c>
		/// is empty then all calls to #trivia_trailing are removed.</remarks>
		public static VList<LNode> WithTrailingTrivia(this VList<LNode> attrs, VList<LNode> trivia)
		{
			var attrs2 = WithoutTrailingTrivia(attrs);
			if (trivia.IsEmpty)
				return attrs2;
			return attrs2.Add(LNode.Call(S.TriviaTrailing, trivia));
		}
		/// <summary>Gets a new list with any #trivia_trailing attributes removed.</summary>
		public static VList<LNode> WithoutTrailingTrivia(this VList<LNode> attrs)
		{
			return attrs.Transform((int i, ref LNode attr) => attr.Calls(S.TriviaTrailing) ? XfAction.Drop : XfAction.Keep);
		}
		/// <summary>Gets a new list with any #trivia_trailing attributes removed. Those trivia are returned in an `out` parameter.</summary>
		public static VList<LNode> WithoutTrailingTrivia(this VList<LNode> attrs, out VList<LNode> trailingTrivia)
		{
			var trailingTrivia2 = VList<LNode>.Empty;
			attrs = attrs.Transform((int i, ref LNode attr) => {
				if (attr.Calls(S.TriviaTrailing)) {
					trailingTrivia2.AddRange(attr.Args);
					return XfAction.Drop;
				}
				return XfAction.Keep;
			});
			trailingTrivia = trailingTrivia2; // cannot use `out` parameter within lambda method
			return attrs;
		}
		/// <summary>Adds additional trailing trivia to a node.</summary>
		public static LNode PlusTrailingTrivia(this LNode node, VList<LNode> trivia)
		{
			return node.WithAttrs(PlusTrailingTrivia(node.Attrs, trivia));
		}
		/// <summary>Adds additional trailing trivia to a node.</summary>
		public static LNode PlusTrailingTrivia(this LNode node, LNode trivia)
		{
			return node.WithAttrs(PlusTrailingTrivia(node.Attrs, trivia));
		}
		/// <summary>Adds additional trailing trivia to an attribute list. Has no effect if <c>trivia</c> is empty.</summary>
		/// <remarks>
		/// Trailing trivia is represented by a call to #trivia_trailing in a node's 
		/// attribute list; each argument to #trivia_trailing represents one piece of 
		/// trivia.
		/// <para/>
		/// In the current design, this method has a side-effect of recreating the #trivia_trailing
		/// node at the end of the attribute list, and if there are multiple #trivia_trailing
		/// lists, consolidating them into a single list, but only if the specified <c>trivia</c> 
		/// list is not empty.</remarks>
		public static VList<LNode> PlusTrailingTrivia(this VList<LNode> attrs, VList<LNode> trivia)
		{
			if (trivia.IsEmpty)
				return attrs;
			VList<LNode> oldTrivia;
			attrs = WithoutTrailingTrivia(attrs, out oldTrivia);
			return attrs.Add(LNode.Call(S.TriviaTrailing, oldTrivia.AddRange(trivia)));
		}
		/// <summary>Adds additional trailing trivia to an attribute list.</summary>
		public static VList<LNode> PlusTrailingTrivia(this VList<LNode> attrs, LNode trivia)
		{
			VList<LNode> oldTrivia;
			attrs = WithoutTrailingTrivia(attrs, out oldTrivia);
			return attrs.Add(LNode.Call(S.TriviaTrailing, oldTrivia.Add(trivia)));
		}
		#endregion
		/// <summary>Interprets a node as a list by returning <c>block.Args</c> if 
		/// <c>block.Calls(listIdentifier)</c>, otherwise returning a one-item list 
		/// of nodes with <c>block</c> as the only item.</summary>
		public static VList<LNode> AsList(this LNode block, Symbol listIdentifier)
		{
			return block.Calls(listIdentifier) ? block.Args : new VList<LNode>(block);
		}
		/// <summary>Converts a list of LNodes to a single LNode by using the list 
		/// as the argument list in a call to the specified identifier, or, if the 
		/// list contains a single item, by returning that single item.</summary>
		/// <param name="listIdentifier">Target of the node that is created if <c>list</c>
		/// does not contain exactly one item. Typical values include "'{}" and "#splice".</param>
		/// <remarks>This is the reverse of the operation performed by <see cref="AsList(LNode,Symbol)"/>.</remarks>
		public static LNode AsLNode(this VList<LNode> list, Symbol listIdentifier)
		{
			if (list.Count == 1)
				return list[0];
			else {
				var r = SourceRange.Nowhere;
				if (list.Count != 0) {
					r = list[0].Range;
					r = new SourceRange(r.Source, r.StartIndex, list.Last.Range.EndIndex - r.StartIndex);
				}
 				return LNode.Call(listIdentifier, list, r);
			}
		}
		public static VList<LNode> WithSpliced(this VList<LNode> list, int index, LNode node, Symbol listName = null)
		{
			if (node.Calls(listName ?? CodeSymbols.Splice))
				return list.InsertRange(index, node.Args);
			else
				return list.Insert(index, node);
		}
		public static VList<LNode> WithSpliced(this VList<LNode> list, LNode node, Symbol listName = null)
		{
			if (node.Calls(listName ?? CodeSymbols.Splice))
				return list.AddRange(node.Args);
			else
				return list.Add(node);
		}
		public static void SpliceInsert(this WList<LNode> list, int index, LNode node, Symbol listName = null)
		{
			if (node.Calls(listName ?? CodeSymbols.Splice))
				list.InsertRange(index, node.Args);
			else
				list.Insert(index, node);
		}
		public static void SpliceAdd(this WList<LNode> list, LNode node, Symbol listName = null)
		{
			if (node.Calls(listName ?? CodeSymbols.Splice))
				list.AddRange(node.Args);
			else
				list.Add(node);
		}
		public static LNode AttrNamed(this LNode self, Symbol name)
		{
			return self.Attrs.NodeNamed(name);
		}
		public static LNode WithoutAttrNamed(this LNode self, Symbol name)
		{
			LNode _;
			return WithoutAttrNamed(self, name, out _);
		}
		public static VList<LNode> Without(this VList<LNode> list, LNode node)
		{
			int i = list.Count;
			foreach (var item in list.ToFVList()) {
				i--;
				if (item == node) {
					Debug.Assert(list[i] == node);
					return list.RemoveAt(i);
				}
			}
			return list;
		}
		public static LNode WithoutAttr(this LNode self, LNode node)
		{
			return self.WithAttrs(self.Attrs.Without(node));
		}
		public static LNode WithoutAttrNamed(this LNode self, Symbol name, out LNode removedAttr)
		{
			var a = self.Attrs.WithoutNodeNamed(name, out removedAttr);
			if (removedAttr != null)
				return self.WithAttrs(a);
			else
				return self;
		}
		public static VList<LNode> WithoutNodeNamed(this VList<LNode> a, Symbol name)
		{
			LNode _;
			return WithoutNodeNamed(a, name, out _);
		}
		public static VList<LNode> WithoutNodeNamed(this VList<LNode> list, Symbol name, out LNode removedNode)
		{
			removedNode = null;
			for (int i = 0, c = list.Count; i < c; i++)
				if (list[i].Name == name) {
					removedNode = list[i];
					return list.RemoveAt(i);
				}
			return list;
		}
		public static LNode ArgNamed(this LNode self, Symbol name)
		{
			return self.Args.NodeNamed(name);
		}
		public static int IndexWithName(this VList<LNode> self, Symbol name, int resultIfNotFound = -1)
		{
			int i = 0;
			foreach (LNode node in self)
				if (node.Name == name)
					return i;
				else
					i++;
			return resultIfNotFound;
		}
		public static LNode NodeNamed(this VList<LNode> self, Symbol name)
		{
			foreach (LNode node in self)
				if (node.Name == name)
					return node;
			return null;
		}
		#region Parentheses management
		public static bool IsParenthesizedExpr(this LNode node)
		{
			return node.AttrNamed(CodeSymbols.TriviaInParens) != null;
		}
		/// <summary>Returns the same node with a parentheses attribute added.</summary>
		public static LNode InParens(this LNode node)
		{
			return node.PlusAttrBefore(LNode.Id(CodeSymbols.TriviaInParens));
		}
		/// <summary>Returns the same node with a parentheses attribute added.</summary>
		/// <remarks>The node's range is changed to the provided <see cref="SourceRange"/>.</remarks>
		public static LNode InParens(this LNode node, SourceRange range)
		{
			return node.WithRange(range).PlusAttrBefore(LNode.Id(CodeSymbols.TriviaInParens));
		}
		/// <summary>Returns the same node with a parentheses attribute added.</summary>
		public static LNode InParens(this LNode node, ISourceFile file, int startIndex, int endIndex)
		{
            return InParens(node, new SourceRange(file, startIndex, endIndex - startIndex));
		}
		/// <summary>Removes a single pair of parentheses, if the node has a 
		/// #trivia_inParens attribute. Returns the same node when no parens are 
		/// present.</summary>
		public static LNode WithoutOuterParens(this LNode self)
		{
			LNode parens;
			self = WithoutAttrNamed(self, S.TriviaInParens, out parens);
			// Restore original node range
			if (parens != null && self.Range.Contains(parens.Range))
				return self.WithRange(parens.Range);
			return self;
		}
		#endregion
		#region MatchesPattern() and helper methods // Used by replace() macro
		static LNodeFactory F = new LNodeFactory(new EmptySourceFile("LNodeExt.cs"));
		/// <summary>Determines whether one Loyc tree "matches" another. This is 
		/// different from a simple equality test in that (1) trivia atributes do 
		/// not have to match, and (2) the pattern can contain placeholders represented
		/// by calls to $ (the substitution operator) with an identifier as a parameter.
		/// Placeholders match any subtree, and are saved to the <c>captures</c> map.
		/// </summary>
		/// <param name="candidate">A node that you want to compare with a 'pattern'.</param>
		/// <param name="pattern">A syntax tree that may contain placeholders. A 
		/// placeholder is a call to the $ operator with one parameter, which must 
		/// be either (A) a simple identifier, or (B) the ".." operator with a simple
		/// identifier as its single parameter. Otherwise, the $ operator is treated 
		/// literally as something that must exist in <c>candidate</c>). The subtree 
		/// in <c>candidate</c> corresponding to the placeholder is saved in 
		/// <c>captures</c>.</param>
		/// <param name="captures">A table that maps placeholder names from 
		/// <c>pattern</c> to subtrees in <c>candidate</c>. You can set your map to 
		/// null and a map will be created for you if necessary. If you already have
		/// a map, you should clear it before calling this method.</param>
		/// <param name="unmatchedAttrs">On return, a list of trivia attributes in 
		/// <c>candidate</c> that were not present in <c>pattern</c>.</param>
		/// <returns>true if <c>pattern</c> matches <c>candidate</c>, false otherwise.</returns>
		/// <remarks>
		/// Attributes in patterns are not yet supported.
		/// <para/>
		/// This method supports multi-part captures, which are matched to 
		/// placeholders whose identifier either (A) has a #params attribute or
		/// (B) has the unary ".." operator applied to it (for example, if 
		/// the placeholder is called p, this is written as <c>$(params p)</c> in 
		/// EC#.) A placeholder that looks like this can match multiple arguments or
		/// multiple statements in the <c>candidate</c> (or <i>no</i> arguments, or
		/// no statements), and will become a #splice(...) node in <c>captures</c>
		/// if it matches multiple items. Multi-part captures are often useful for
		/// getting lists of statements before and after some required element,
		/// e.g. <c>{ $(params before); MatchThis($something); $(params after); }</c>
		/// <para/>
		/// If the same placeholder appears twice then the two matching items are 
		/// combined into a single output node (calling #splice).
		/// <para/>
		/// If matching is unsuccessful, <c>captures</c> and <c>unmatchedAttrs</c>
		/// may contain irrelevant information gathered during the attempt to match.
		/// <para/>
		/// In EC#, the quote(...) macro can be used to create the LNode object for 
		/// a pattern.
		/// </remarks>
		public static bool MatchesPattern(this LNode candidate, LNode pattern, ref MMap<Symbol, LNode> captures, out VList<LNode> unmatchedAttrs)
		{
			// [$capture] (...)
			if (!AttributesMatch(candidate, pattern, ref captures, out unmatchedAttrs))
				return false;
			// $capture or $(..capture)
			LNode sub = GetCaptureIdentifier(pattern);
			if (sub != null)
			{
				captures = captures ?? new MMap<Symbol, LNode>();
				AddCapture(captures, sub.Name, candidate);
				unmatchedAttrs = VList<LNode>.Empty; // The attrs (if any) were captured
				return true;
			}
			var kind = candidate.Kind;
			if (kind != pattern.Kind)
				return false;


Answer: 			if (kind == LNodeKind.Id && candidate.Name != pattern.Name)