using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Microsoft.CodeAnalysis.Text;
using Roslyn.Utilities;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using System.Threading.Tasks;

namespace ICSharpCode.ILSpy.AddIn
{
	static class SyntaxNodeExtensions
    {
        public static IEnumerable<SyntaxNode> GetAncestors(this SyntaxNode node)
        {
            var current = node.Parent;

            while (current != null)
            {
                yield return current;

                current = current is IStructuredTriviaSyntax
                    ? ((IStructuredTriviaSyntax)current).ParentTrivia.Token.Parent
                    : current.Parent;
            }
        }

        public static IEnumerable<TNode> GetAncestors<TNode>(this SyntaxNode node)
            where TNode : SyntaxNode
        {
            var current = node.Parent;
            while (current != null)
            {
                if (current is TNode)
                {
                    yield return (TNode)current;
                }

                current = current is IStructuredTriviaSyntax
                    ? ((IStructuredTriviaSyntax)current).ParentTrivia.Token.Parent
                    : current.Parent;
            }
        }

        public static TNode GetAncestor<TNode>(this SyntaxNode node)
            where TNode : SyntaxNode
        {
            if (node == null)
            {
                return default(TNode);
            }

            return node.GetAncestors<TNode>().FirstOrDefault();
        }

        public static TNode GetAncestorOrThis<TNode>(this SyntaxNode node)
            where TNode : SyntaxNode
        {
            if (node == null)
            {
                return default(TNode);
            }

            return node.GetAncestorsOrThis<TNode>().FirstOrDefault();
        }

        public static IEnumerable<TNode> GetAncestorsOrThis<TNode>(this SyntaxNode node)
            where TNode : SyntaxNode
        {
            var current = node;
            while (current != null)
            {
                if (current is TNode)
                {
                    yield return (TNode)current;
                }

                current = current is IStructuredTriviaSyntax
                    ? ((IStructuredTriviaSyntax)current).ParentTrivia.Token.Parent
                    : current.Parent;
            }
        }

		public static bool HasAncestor<TNode>(this SyntaxNode node)
            where TNode : SyntaxNode
        {
            return node.GetAncestors<TNode>().Any();
        }

        public static bool CheckParent<T>(this SyntaxNode node, Func<T, bool> valueChecker) where T : SyntaxNode
        {
            if (node == null)
            {
                return false;
            }

            var parentNode = node.Parent as T;
            if (parentNode == null)
            {
                return false;
            }

            return valueChecker(parentNode);
        }

        /// <summary>
        /// Returns true if is a given token is a child token of of a certain type of parent node.
        /// </summary>
        /// <typeparam name="TParent">The type of the parent node.</typeparam>
        /// <param name="node">The node that we are testing.</param>
        /// <param name="childGetter">A function that, when given the parent node, returns the child token we are interested in.</param>
        public static bool IsChildNode<TParent>(this SyntaxNode node, Func<TParent, SyntaxNode> childGetter)
            where TParent : SyntaxNode
        {
            var ancestor = node.GetAncestor<TParent>();
            if (ancestor == null)
            {
                return false;
            }

            var ancestorNode = childGetter(ancestor);

            return node == ancestorNode;
        }

        /// <summary>
        /// Returns true if this node is found underneath the specified child in the given parent.
        /// </summary>
        public static bool IsFoundUnder<TParent>(this SyntaxNode node, Func<TParent, SyntaxNode> childGetter)
            where TParent : SyntaxNode
        {
            var ancestor = node.GetAncestor<TParent>();
            if (ancestor == null)
            {
                return false;
            }

            var child = childGetter(ancestor);

            // See if node passes through child on the way up to ancestor.
            return node.GetAncestorsOrThis<SyntaxNode>().Contains(child);
        }

        public static SyntaxNode GetCommonRoot(this SyntaxNode node1, SyntaxNode node2)
        {
            //Contract.ThrowIfTrue(node1.RawKind == 0 || node2.RawKind == 0);

            // find common starting node from two nodes.
            // as long as two nodes belong to same tree, there must be at least one common root (Ex, compilation unit)
            var ancestors = node1.GetAncestorsOrThis<SyntaxNode>();
            var set = new HashSet<SyntaxNode>(node2.GetAncestorsOrThis<SyntaxNode>());

            return ancestors.First(set.Contains);
        }

        public static int Width(this SyntaxNode node)
        {
            return node.Span.Length;
        }

        public static int FullWidth(this SyntaxNode node)
        {
            return node.FullSpan.Length;
        }

        public static SyntaxNode FindInnermostCommonNode(
            this IEnumerable<SyntaxNode> nodes,
            Func<SyntaxNode, bool> predicate)
        {
            IEnumerable<SyntaxNode> blocks = null;
            foreach (var node in nodes)
            {
                blocks = blocks == null
                    ? node.AncestorsAndSelf().Where(predicate)
                    : blocks.Intersect(node.AncestorsAndSelf().Where(predicate));
            }

            return blocks == null ? null : blocks.First();
        }

        public static TSyntaxNode FindInnermostCommonNode<TSyntaxNode>(this IEnumerable<SyntaxNode> nodes)
            where TSyntaxNode : SyntaxNode
        {
            return (TSyntaxNode)nodes.FindInnermostCommonNode(n => n is TSyntaxNode);
        }

        /// <summary>
        /// create a new root node from the given root after adding annotations to the tokens
        /// 
        /// tokens should belong to the given root
        /// </summary>
        public static SyntaxNode AddAnnotations(this SyntaxNode root, IEnumerable<Tuple<SyntaxToken, SyntaxAnnotation>> pairs)
        {
            //			Contract.ThrowIfNull(root);
            //			Contract.ThrowIfNull(pairs);

            var tokenMap = pairs.GroupBy(p => p.Item1, p => p.Item2).ToDictionary(g => g.Key, g => g.ToArray());
            return root.ReplaceTokens(tokenMap.Keys, (o, n) => o.WithAdditionalAnnotations(tokenMap[o]));
        }

        /// <summary>
        /// create a new root node from the given root after adding annotations to the nodes
        /// 
        /// nodes should belong to the given root
        /// </summary>
        public static SyntaxNode AddAnnotations(this SyntaxNode root, IEnumerable<Tuple<SyntaxNode, SyntaxAnnotation>> pairs)
        {
            //			Contract.ThrowIfNull(root);
            //			Contract.ThrowIfNull(pairs);

            var tokenMap = pairs.GroupBy(p => p.Item1, p => p.Item2).ToDictionary(g => g.Key, g => g.ToArray());
            return root.ReplaceNodes(tokenMap.Keys, (o, n) => o.WithAdditionalAnnotations(tokenMap[o]));
        }

        public static TextSpan GetContainedSpan(this IEnumerable<SyntaxNode> nodes)
        {
            //			Contract.ThrowIfNull(nodes);
            //			Contract.ThrowIfFalse(nodes.Any());

            TextSpan fullSpan = nodes.First().Span;
            foreach (var node in nodes)
            {
                fullSpan = TextSpan.FromBounds(
                    Math.Min(fullSpan.Start, node.SpanStart),
                    Math.Max(fullSpan.End, node.Span.End));
            }

            return fullSpan;
        }

        public static IEnumerable<TextSpan> GetContiguousSpans(
            this IEnumerable<SyntaxNode> nodes, Func<SyntaxNode, SyntaxToken> getLastToken = null)
        {
            SyntaxNode lastNode = null;
            TextSpan? textSpan = null;
            foreach (var node in nodes)
            {
                if (lastNode == null)
                {
                    textSpan = node.Span;
                }
                else
                {
                    var lastToken = getLastToken == null
                        ? lastNode.GetLastToken()
                        : getLastToken(lastNode);
                    if (lastToken.GetNextToken(includeDirectives: true) == node.GetFirstToken())
                    {
                        // Expand the span
                        textSpan = TextSpan.FromBounds(textSpan.Value.Start, node.Span.End);
                    }
                    else
                    {
                        // Return the last span, and start a new one
                        yield return textSpan.Value;
                        textSpan = node.Span;
                    }
                }

                lastNode = node;
            }

            if (textSpan.HasValue)
            {
                yield return textSpan.Value;
            }
        }

        //public static bool OverlapsHiddenPosition(this SyntaxNode node, CancellationToken cancellationToken)
        //{
        //	return node.OverlapsHiddenPosition(node.Span, cancellationToken);
        //}

        //public static bool OverlapsHiddenPosition(this SyntaxNode node, TextSpan span, CancellationToken cancellationToken)
        //{
        //	return node.SyntaxTree.OverlapsHiddenPosition(span, cancellationToken);
        //}

        //public static bool OverlapsHiddenPosition(this SyntaxNode declaration, SyntaxNode startNode, SyntaxNode endNode, CancellationToken cancellationToken)
        //{
        //	var start = startNode.Span.End;
        //	var end = endNode.SpanStart;

        //	var textSpan = TextSpan.FromBounds(start, end);
        //	return declaration.OverlapsHiddenPosition(textSpan, cancellationToken);
        //}

        public static IEnumerable<T> GetAnnotatedNodes<T>(this SyntaxNode node, SyntaxAnnotation syntaxAnnotation) where T : SyntaxNode
        {
            return node.GetAnnotatedNodesAndTokens(syntaxAnnotation).Select(n => n.AsNode()).OfType<T>();
        }

        public static bool IsKind(this SyntaxNode node, SyntaxKind kind1, SyntaxKind kind2)
        {
            if (node == null)
            {
                return false;
            }

            var csharpKind = node.Kind();
            return csharpKind == kind1 || csharpKind == kind2;
        }

        public static bool IsKind(this SyntaxNode node, SyntaxKind kind1, SyntaxKind kind2, SyntaxKind kind3)
        {
            if (node == null)
            {
                return false;
            }

            var csharpKind = node.Kind();
            return csharpKind == kind1 || csharpKind == kind2 || csharpKind == kind3;
        }

        public static bool IsKind(this SyntaxNode node, SyntaxKind kind1, SyntaxKind kind2, SyntaxKind kind3, SyntaxKind kind4)
        {
            if (node == null)
            {
                return false;
            }

            var csharpKind = node.Kind();
            return csharpKind == kind1 || csharpKind == kind2 || csharpKind == kind3 || csharpKind == kind4;
        }

        public static bool IsKind(this SyntaxNode node, SyntaxKind kind1, SyntaxKind kind2, SyntaxKind kind3, SyntaxKind kind4, SyntaxKind kind5)
        {
            if (node == null)
            {
                return false;
            }

            var csharpKind = node.Kind();
            return csharpKind == kind1 || csharpKind == kind2 || csharpKind == kind3 || csharpKind == kind4 || csharpKind == kind5;
        }

        /// <summary>
        /// Returns the list of using directives that affect <paramref name="node"/>. The list will be returned in
        /// top down order.  
        /// </summary>
        public static IEnumerable<UsingDirectiveSyntax> GetEnclosingUsingDirectives(this SyntaxNode node)
        {
            return node.GetAncestorOrThis<CompilationUnitSyntax>().Usings
                .Concat(node.GetAncestorsOrThis<NamespaceDeclarationSyntax>()
                    .Reverse()
                    .SelectMany(n => n.Usings));
        }

        public static bool IsUnsafeContext(this SyntaxNode node)
        {
            if (node.GetAncestor<UnsafeStatementSyntax>() != null)
            {
                return true;
            }

            return node.GetAncestors<MemberDeclarationSyntax>().Any(
                m => m.GetModifiers().Any(SyntaxKind.UnsafeKeyword));
        }

        public static bool IsInStaticContext(this SyntaxNode node)
        {
            // this/base calls are always static.
            if (node.FirstAncestorOrSelf<ConstructorInitializerSyntax>() != null)
            {
                return true;
            }

            var memberDeclaration = node.FirstAncestorOrSelf<MemberDeclarationSyntax>();
            if (memberDeclaration == null)
            {
                return false;
            }

            switch (memberDeclaration.Kind())
            {
                case SyntaxKind.MethodDeclaration:
                case SyntaxKind.ConstructorDeclaration:
                case SyntaxKind.PropertyDeclaration:
                case SyntaxKind.EventDeclaration:
                case SyntaxKind.IndexerDeclaration:
                    return memberDeclaration.GetModifiers().Any(SyntaxKind.StaticKeyword);

                case SyntaxKind.FieldDeclaration:
                    // Inside a field one can only access static members of a type.
                    return true;

                case SyntaxKind.DestructorDeclaration:
                    return false;
            }

            // Global statements are not a static context.
            if (node.FirstAncestorOrSelf<GlobalStatementSyntax>() != null)
            {
                return false;
            }

            // any other location is considered static
            return true;
        }

        public static NamespaceDeclarationSyntax GetInnermostNamespaceDeclarationWithUsings(this SyntaxNode contextNode)
        {
            var usingDirectiveAncestor = contextNode.GetAncestor<UsingDirectiveSyntax>();
            if (usingDirectiveAncestor == null)
            {
                return contextNode.GetAncestorsOrThis<NamespaceDeclarationSyntax>().FirstOrDefault(n => n.Usings.Count > 0);
            }
            else
            {
                // We are inside a using directive. In this case, we should find and return the first 'parent' namespace with usings.
                var containingNamespace = usingDirectiveAncestor.GetAncestor<NamespaceDeclarationSyntax>();
                if (containingNamespace == null)
                {
                    // We are inside a top level using directive (i.e. one that's directly in the compilation unit).
                    return null;
                }
                else
                {
                    return containingNamespace.GetAncestors<NamespaceDeclarationSyntax>().FirstOrDefault(n => n.Usings.Count > 0);
                }
            }
        }

        /// <summary>
        /// Returns all of the trivia to the left of this token up to the previous token (concatenates
        /// the previous token's trailing trivia and this token's leading trivia).
        /// </summary>
        public static IEnumerable<SyntaxTrivia> GetAllPrecedingTriviaToPreviousToken(this SyntaxToken token)
        {
            var prevToken = token.GetPreviousToken(includeSkipped: true);
            if (prevToken.Kind() == SyntaxKind.None)
            {
                return token.LeadingTrivia;
            }

            return prevToken.TrailingTrivia.Concat(token.LeadingTrivia);
        }

        public static bool IsBreakableConstruct(this SyntaxNode node)
        {
            switch (node.Kind())
            {
                case SyntaxKind.DoStatement:
                case SyntaxKind.WhileStatement:
                case SyntaxKind.SwitchStatement:
                case SyntaxKind.ForStatement:
                case SyntaxKind.ForEachStatement:
                    return true;
            }

            return false;
        }

        public static bool IsContinuableConstruct(this SyntaxNode node)
        {
            switch (node.Kind())
            {
                case SyntaxKind.DoStatement:
                case SyntaxKind.WhileStatement:
                case SyntaxKind.ForStatement:
                case SyntaxKind.ForEachStatement:
                    return true;
            }

            return false;
        }

        public static bool IsReturnableConstruct(this SyntaxNode node)
        {
            switch (node.Kind())
            {
                case SyntaxKind.AnonymousMethodExpression:
                case SyntaxKind.SimpleLambdaExpression:
                case SyntaxKind.ParenthesizedLambdaExpression:
                case SyntaxKind.MethodDeclaration:
                case SyntaxKind.ConstructorDeclaration:
                case SyntaxKind.DestructorDeclaration:
                case SyntaxKind.GetAccessorDeclaration:
                case SyntaxKind.SetAccessorDeclaration:
                case SyntaxKind.OperatorDeclaration:
                case SyntaxKind.AddAccessorDeclaration:
                case SyntaxKind.RemoveAccessorDeclaration:
                    return true;
            }

            return false;
        }

        public static bool IsAnyArgumentList(this SyntaxNode node)
        {
            return node.IsKind(SyntaxKind.ArgumentList) ||
                node.IsKind(SyntaxKind.AttributeArgumentList) ||
                node.IsKind(SyntaxKind.BracketedArgumentList) ||
                node.IsKind(SyntaxKind.TypeArgumentList);
        }

        public static bool IsAnyLambda(this SyntaxNode node)
        {
            return
                node.IsKind(SyntaxKind.ParenthesizedLambdaExpression) ||
                node.IsKind(SyntaxKind.SimpleLambdaExpression);
        }

        public static bool IsAnyLambdaOrAnonymousMethod(this SyntaxNode node)
        {
            return node.IsAnyLambda() || node.IsKind(SyntaxKind.AnonymousMethodExpression);
        }

        public static bool IsAnyAssignExpression(this SyntaxNode node)
        {
            return SyntaxFacts.IsAssignmentExpression(node.Kind());
        }

		public static bool IsParentKind(this SyntaxNode node, SyntaxKind kind)
		{
			return node != null && node.Parent.IsKind(kind);
		}

		public static bool IsParentKind(this SyntaxToken node, SyntaxKind kind)
		{
			return node.Parent != null && node.Parent.IsKind(kind);
		}

		public static bool IsCompoundAssignExpression(this SyntaxNode node)
        {
            switch (node.Kind())
            {
                case SyntaxKind.AddAssignmentExpression:
                case SyntaxKind.SubtractAssignmentExpression:
                case SyntaxKind.MultiplyAssignmentExpression:
                case SyntaxKind.DivideAssignmentExpression:
                case SyntaxKind.ModuloAssignmentExpression:
                case SyntaxKind.AndAssignmentExpression:
                case SyntaxKind.ExclusiveOrAssignmentExpression:
                case SyntaxKind.OrAssignmentExpression:
                case SyntaxKind.LeftShiftAssignmentExpression:
                case SyntaxKind.RightShiftAssignmentExpression:
                    return true;
            }

            return false;
        }

        public static bool IsLeftSideOfAssignExpression(this SyntaxNode node)
        {
            return node.IsParentKind(SyntaxKind.SimpleAssignmentExpression) &&
                ((AssignmentExpressionSyntax)node.Parent).Left == node;
        }

        public static bool IsLeftSideOfAnyAssignExpression(this SyntaxNode node)
        {
            return node.Parent.IsAnyAssignExpression() &&
                ((AssignmentExpressionSyntax)node.Parent).Left == node;
        }

        public static bool IsRightSideOfAnyAssignExpression(this SyntaxNode node)
        {
            return node.Parent.IsAnyAssignExpression() &&
                ((AssignmentExpressionSyntax)node.Parent).Right == node;
        }

        public static bool IsVariableDeclaratorValue(this SyntaxNode node)
        {
            return
                node.IsParentKind(SyntaxKind.EqualsValueClause) &&
                node.Parent.IsParentKind(SyntaxKind.VariableDeclarator) &&
                ((EqualsValueClauseSyntax)node.Parent).Value == node;
        }

        public static BlockSyntax FindInnermostCommonBlock(this IEnumerable<SyntaxNode> nodes)
        {
            return nodes.FindInnermostCommonNode<BlockSyntax>();
        }

        public static IEnumerable<SyntaxNode> GetAncestorsOrThis(this SyntaxNode node, Func<SyntaxNode, bool> predicate)
        {
            var current = node;
            while (current != null)
            {
                if (predicate(current))
                {
                    yield return current;
                }

                current = current.Parent;
            }
        }

        public static SyntaxNode GetParent(this SyntaxNode node)
        {
            return node != null ? node.Parent : null;
        }

        public static ValueTuple<SyntaxToken, SyntaxToken> GetBraces(this SyntaxNode node)
        {
            var namespaceNode = node as NamespaceDeclarationSyntax;
            if (namespaceNode != null)
            {
                return ValueTuple.Create(namespaceNode.OpenBraceToken, namespaceNode.CloseBraceToken);
            }

            var baseTypeNode = node as BaseTypeDeclarationSyntax;
            if (baseTypeNode != null)
            {
                return ValueTuple.Create(baseTypeNode.OpenBraceToken, baseTypeNode.CloseBraceToken);
            }

            var accessorListNode = node as AccessorListSyntax;
            if (accessorListNode != null)
            {
                return ValueTuple.Create(accessorListNode.OpenBraceToken, accessorListNode.CloseBraceToken);
            }

            var blockNode = node as BlockSyntax;
            if (blockNode != null)
            {
                return ValueTuple.Create(blockNode.OpenBraceToken, blockNode.CloseBraceToken);
            }

            var switchStatementNode = node as SwitchStatementSyntax;
            if (switchStatementNode != null)
            {
                return ValueTuple.Create(switchStatementNode.OpenBraceToken, switchStatementNode.CloseBraceToken);
            }

            var anonymousObjectCreationExpression = node as AnonymousObjectCreationExpressionSyntax;
            if (anonymousObjectCreationExpression != null)
            {
                return ValueTuple.Create(anonymousObjectCreationExpression.OpenBraceToken, anonymousObjectCreationExpression.CloseBraceToken);
            }

            var initializeExpressionNode = node as InitializerExpressionSyntax;
            if (initializeExpressionNode != null)
            {
                return ValueTuple.Create(initializeExpressionNode.OpenBraceToken, initializeExpressionNode.CloseBraceToken);
            }

            return new ValueTuple<SyntaxToken, SyntaxToken>();
        }

        public static SyntaxTokenList GetModifiers(this SyntaxNode member)
        {
            if (member != null)
            {
                switch (member.Kind())
                {
                    case SyntaxKind.EnumDeclaration:
                        return ((EnumDeclarationSyntax)member).Modifiers;
                    case SyntaxKind.ClassDeclaration:
                    case SyntaxKind.InterfaceDeclaration:
                    case SyntaxKind.StructDeclaration:
                        return ((TypeDeclarationSyntax)member).Modifiers;
                    case SyntaxKind.DelegateDeclaration:
                        return ((DelegateDeclarationSyntax)member).Modifiers;
                    case SyntaxKind.FieldDeclaration:
                        return ((FieldDeclarationSyntax)member).Modifiers;
                    case SyntaxKind.EventFieldDeclaration:
                        return ((EventFieldDeclarationSyntax)member).Modifiers;
                    case SyntaxKind.ConstructorDeclaration:
                        return ((ConstructorDeclarationSyntax)member).Modifiers;
                    case SyntaxKind.DestructorDeclaration:
                        return ((DestructorDeclarationSyntax)member).Modifiers;
                    case SyntaxKind.PropertyDeclaration:
                        return ((PropertyDeclarationSyntax)member).Modifiers;
                    case SyntaxKind.EventDeclaration:
                        return ((EventDeclarationSyntax)member).Modifiers;
                    case SyntaxKind.IndexerDeclaration:
                        return ((IndexerDeclarationSyntax)member).Modifiers;
                    case SyntaxKind.OperatorDeclaration:
                        return ((OperatorDeclarationSyntax)member).Modifiers;
                    case SyntaxKind.ConversionOperatorDeclaration:
                        return ((ConversionOperatorDeclarationSyntax)member).Modifiers;
                    case SyntaxKind.MethodDeclaration:
                        return ((MethodDeclarationSyntax)member).Modifiers;
                    case SyntaxKind.GetAccessorDeclaration:
                    case SyntaxKind.SetAccessorDeclaration:
                    case SyntaxKind.AddAccessorDeclaration:
                    case SyntaxKind.RemoveAccessorDeclaration:
                        return ((AccessorDeclarationSyntax)member).Modifiers;
                }
            }

            return default(SyntaxTokenList);
        }

        public static SyntaxNode WithModifiers(this SyntaxNode member, SyntaxTokenList modifiers)
        {
            if (member != null)
            {
                switch (member.Kind())
                {
                    case SyntaxKind.EnumDeclaration:
                        return ((EnumDeclarationSyntax)member).WithModifiers(modifiers);
                    case SyntaxKind.ClassDeclaration:
                    case SyntaxKind.InterfaceDeclaration:
                    case SyntaxKind.StructDeclaration:
                        return ((TypeDeclarationSyntax)member).WithModifiers(modifiers);
                    case SyntaxKind.DelegateDeclaration:
                        return ((DelegateDeclarationSyntax)member).WithModifiers(modifiers);
                    case SyntaxKind.FieldDeclaration:
                        return ((FieldDeclarationSyntax)member).WithModifiers(modifiers);
                    case SyntaxKind.EventFieldDeclaration:
                        return ((EventFieldDeclarationSyntax)member).WithModifiers(modifiers);
                    case SyntaxKind.ConstructorDeclaration:
                        return ((ConstructorDeclarationSyntax)member).WithModifiers(modifiers);
                    case SyntaxKind.DestructorDeclaration:
                        return ((DestructorDeclarationSyntax)member).WithModifiers(modifiers);
                    case SyntaxKind.PropertyDeclaration:
                        return ((PropertyDeclarationSyntax)member).WithModifiers(modifiers);
                    case SyntaxKind.EventDeclaration:
                        return ((EventDeclarationSyntax)member).WithModifiers(modifiers);
                    case SyntaxKind.IndexerDeclaration:
                        return ((IndexerDeclarationSyntax)member).WithModifiers(modifiers);
                    case SyntaxKind.OperatorDeclaration:
                        return ((OperatorDeclarationSyntax)member).WithModifiers(modifiers);
                    case SyntaxKind.ConversionOperatorDeclaration:
                        return ((ConversionOperatorDeclarationSyntax)member).WithModifiers(modifiers);
                    case SyntaxKind.MethodDeclaration:
                        return ((MethodDeclarationSyntax)member).WithModifiers(modifiers);
                    case SyntaxKind.GetAccessorDeclaration:
                    case SyntaxKind.SetAccessorDeclaration:
                    case SyntaxKind.AddAccessorDeclaration:
                    case SyntaxKind.RemoveAccessorDeclaration:
                        return ((AccessorDeclarationSyntax)member).WithModifiers(modifiers);
                }
            }

            return null;
        }

        public static TypeDeclarationSyntax WithModifiers(
            this TypeDeclarationSyntax node, SyntaxTokenList modifiers)
        {
            switch (node.Kind())
            {
                case SyntaxKind.ClassDeclaration:
                    return ((ClassDeclarationSyntax)node).WithModifiers(modifiers);
                case SyntaxKind.InterfaceDeclaration:
                    return ((InterfaceDeclarationSyntax)node).WithModifiers(modifiers);
                case SyntaxKind.StructDeclaration:
                    return ((StructDeclarationSyntax)node).WithModifiers(modifiers);
            }

            throw new InvalidOperationException();
        }

        public static bool CheckTopLevel(this SyntaxNode node, TextSpan span)
        {
            var block = node as BlockSyntax;
            if (block != null)
            {
                return block.ContainsInBlockBody(span);
            }

            var field = node as FieldDeclarationSyntax;
            if (field != null)
            {
                foreach (var variable in field.Declaration.Variables)
                {
                    if (variable.Initializer != null && variable.Initializer.Span.Contains(span))
                    {
                        return true;
                    }
                }
            }

            var global = node as GlobalStatementSyntax;
            if (global != null)
            {
                return true;
            }

            var constructorInitializer = node as ConstructorInitializerSyntax;
            if (constructorInitializer != null)
            {
                return constructorInitializer.ContainsInArgument(span);
            }

            return false;
        }

        public static bool ContainsInArgument(this ConstructorInitializerSyntax initializer, TextSpan textSpan)
        {
            if (initializer == null)
            {
                return false;
            }

            return initializer.ArgumentList.Arguments.Any(a => a.Span.Contains(textSpan));
        }

        public static bool ContainsInBlockBody(this BlockSyntax block, TextSpan textSpan)
        {
            if (block == null)
            {
                return false;
            }

            var blockSpan = TextSpan.FromBounds(block.OpenBraceToken.Span.End, block.CloseBraceToken.SpanStart);
            return blockSpan.Contains(textSpan);
        }

        public static bool IsDelegateOrConstructorOrMethodParameterList(this SyntaxNode node)
        {
            if (!node.IsKind(SyntaxKind.ParameterList))
            {
                return false;
            }

            return
                node.IsParentKind(SyntaxKind.MethodDeclaration) ||
                node.IsParentKind(SyntaxKind.ConstructorDeclaration) ||
                node.IsParentKind(SyntaxKind.DelegateDeclaration);
        }

    }
}
