﻿// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

#nullable disable

using System.Collections.Generic;
using System.Threading;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.DocumentationComments;
using Microsoft.CodeAnalysis.MetadataAsSource;
using Microsoft.CodeAnalysis.Shared.Utilities;

namespace Microsoft.CodeAnalysis.CSharp.DocumentationComments
{
    internal class DocCommentConverter : CSharpSyntaxRewriter
    {
        private readonly IDocumentationCommentFormattingService _formattingService;
        private readonly CancellationToken _cancellationToken;

        public static SyntaxNode ConvertToRegularComments(SyntaxNode node, IDocumentationCommentFormattingService formattingService, CancellationToken cancellationToken)
        {
            var converter = new DocCommentConverter(formattingService, cancellationToken);

            return converter.Visit(node);
        }

        private DocCommentConverter(IDocumentationCommentFormattingService formattingService, CancellationToken cancellationToken)
            : base(visitIntoStructuredTrivia: false)
        {
            _formattingService = formattingService;
            _cancellationToken = cancellationToken;
        }

        public override SyntaxNode Visit(SyntaxNode node)
        {
            _cancellationToken.ThrowIfCancellationRequested();

            if (node == null)
            {
                return node;
            }

            // Process children first
            node = base.Visit(node);

            // Check the leading trivia for doc comments.
            if (node.GetLeadingTrivia().Any(SyntaxKind.SingleLineDocumentationCommentTrivia))
            {
                var newLeadingTrivia = new List<SyntaxTrivia>();

                foreach (var trivia in node.GetLeadingTrivia())
                {
                    if (trivia.Kind() == SyntaxKind.SingleLineDocumentationCommentTrivia)
                    {
                        var structuredTrivia = (DocumentationCommentTriviaSyntax)trivia.GetStructure();
                        var commentLines = ConvertDocCommentToRegularComment(structuredTrivia).ToSyntaxTriviaList();

                        if (commentLines.Count > 0)
                        {
                            newLeadingTrivia.Add(SyntaxFactory.Comment("//"));
                            newLeadingTrivia.Add(SyntaxFactory.ElasticCarriageReturnLineFeed);

                            newLeadingTrivia.AddRange(commentLines);
                        }
                    }
                    else
                    {
                        newLeadingTrivia.Add(trivia);
                    }
                }

                node = node.WithLeadingTrivia(newLeadingTrivia);
            }

            return node;
        }

        private IEnumerable<SyntaxTrivia> ConvertDocCommentToRegularComment(DocumentationCommentTriviaSyntax structuredTrivia)
        {
            var xmlFragment = DocumentationCommentUtilities.ExtractXMLFragment(structuredTrivia.ToFullString(), "///");

            var docComment = DocumentationComment.FromXmlFragment(xmlFragment);

            var commentLines = AbstractMetadataAsSourceService.DocCommentFormatter.Format(_formattingService, docComment);

            foreach (var line in commentLines)
            {
                if (!string.IsNullOrWhiteSpace(line))
                {
                    yield return SyntaxFactory.Comment("// " + line);
                }
                else
                {
                    yield return SyntaxFactory.Comment("//");
                }

                yield return SyntaxFactory.ElasticCarriageReturnLineFeed;
            }
        }
    }
}
