﻿// 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.

using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Editing;
using Microsoft.CodeAnalysis.Formatting;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Microsoft.CodeAnalysis.Simplification;
using Roslyn.Utilities;

namespace Microsoft.CodeAnalysis.ConvertIfToSwitch
{
    internal abstract partial class AbstractConvertIfToSwitchCodeRefactoringProvider<
        TIfStatementSyntax, TExpressionSyntax, TIsExpressionSyntax, TPatternSyntax>
    {
        public abstract SyntaxNode CreateSwitchExpressionStatement(SyntaxNode target, ImmutableArray<AnalyzedSwitchSection> sections, Feature feature);
        public abstract SyntaxNode CreateSwitchStatement(TIfStatementSyntax ifStatement, SyntaxNode target, IEnumerable<SyntaxNode> sectionList);
        public abstract IEnumerable<SyntaxNode> AsSwitchSectionStatements(IOperation operation);
        public abstract SyntaxNode AsSwitchLabelSyntax(AnalyzedSwitchLabel label, Feature feature);
        protected abstract SyntaxTriviaList GetLeadingTriviaToTransfer(SyntaxNode syntaxToRemove);

        private async Task<Document> UpdateDocumentAsync(
            Document document,
            SyntaxNode target,
            TIfStatementSyntax ifStatement,
            ImmutableArray<AnalyzedSwitchSection> sections,
            Feature feature,
            bool convertToSwitchExpression,
            CancellationToken cancellationToken)
        {
            var root = await document.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false);
            var generator = SyntaxGenerator.GetGenerator(document);
            var ifSpan = ifStatement.Span;
            var options = root.SyntaxTree.Options;

            var @switch = convertToSwitchExpression
                ? CreateSwitchExpressionStatement(target, sections, feature)
                : CreateSwitchStatement(ifStatement, target, sections.Select(section => AsSwitchSectionSyntax(section, generator, feature)));

            var lastNode = sections.Last().SyntaxToRemove;
            @switch = @switch
                .WithLeadingTrivia(ifStatement.GetLeadingTrivia())
                .WithTrailingTrivia(lastNode.GetTrailingTrivia())
                .WithAdditionalAnnotations(Formatter.Annotation)
                .WithAdditionalAnnotations(Simplifier.Annotation);

            var nodesToRemove = sections.Skip(1).Select(s => s.SyntaxToRemove).Where(s => s.Parent == ifStatement.Parent);
            root = root.RemoveNodes(nodesToRemove, SyntaxRemoveOptions.KeepNoTrivia);
            Debug.Assert(root is object); // we didn't remove the root
            root = root.ReplaceNode(root.FindNode(ifSpan, getInnermostNodeForTie: true), @switch);
            return document.WithSyntaxRoot(root);
        }

        private SyntaxNode AsSwitchSectionSyntax(AnalyzedSwitchSection section, SyntaxGenerator generator, Feature feature)
        {
            var statements = AsSwitchSectionStatements(section.Body);
            var sectionNode = section.Labels.IsDefault
                ? generator.DefaultSwitchSection(statements)
                : generator.SwitchSectionFromLabels(section.Labels.Select(label => AsSwitchLabelSyntax(label, feature)), statements);

            sectionNode = sectionNode.WithPrependedLeadingTrivia(GetLeadingTriviaToTransfer(section.SyntaxToRemove));

            return sectionNode;
        }
    }
}
