// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Linq;
using Microsoft.AspNetCore.Razor.Language.Syntax;

namespace Microsoft.AspNetCore.Razor.Language.Legacy
{
    internal class WhitespaceRewriter : SyntaxRewriter
    {
        public override SyntaxNode Visit(SyntaxNode node)
        {
            if (node == null)
            {
                return base.Visit(node);
            }

            var children = node.ChildNodes();
            for (var i = 0; i < children.Count; i++)
            {
                var child = children[i];
                if (child is CSharpCodeBlockSyntax codeBlock &&
                    TryRewriteWhitespace(codeBlock, out var rewritten, out var whitespaceLiteral))
                {
                    // Replace the existing code block with the whitespace literal
                    // followed by the rewritten code block (with the code whitespace removed).
                    node = node.ReplaceNode(codeBlock, new SyntaxNode[] { whitespaceLiteral, rewritten });

                    // Since we replaced node, its children are different. Update our collection.
                    children = node.ChildNodes();
                }
            }

            return base.Visit(node);
        }

        private bool TryRewriteWhitespace(CSharpCodeBlockSyntax codeBlock, out CSharpCodeBlockSyntax rewritten, out SyntaxNode whitespaceLiteral)
        {
            // Rewrite any whitespace represented as code at the start of a line preceding an expression block.
            // We want it to be rendered as Markup.

            rewritten = null;
            whitespaceLiteral = null;
            var children = codeBlock.ChildNodes();
            if (children.Count < 2)
            {
                return false;
            }

            if (children[0] is CSharpStatementLiteralSyntax literal &&
                (children[1] is CSharpExplicitExpressionSyntax || children[1] is CSharpImplicitExpressionSyntax))
            {
                var containsNonWhitespace = literal.DescendantNodes()
                    .Where(n => n.IsToken)
                    .Cast<SyntaxToken>()
                    .Any(t => !string.IsNullOrWhiteSpace(t.Content));

                if (!containsNonWhitespace)
                {
                    // Literal node is all whitespace. Can rewrite.
                    whitespaceLiteral = SyntaxFactory.MarkupTextLiteral(literal.LiteralTokens);
                    rewritten = codeBlock.ReplaceNode(literal, newNode: null);
                    return true;
                }
            }

            return false;
        }
    }
}
