﻿// 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;
using System.ComponentModel.Composition;
using System.Threading;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Extensions;
using Microsoft.CodeAnalysis.Editor.Shared.Extensions;
using Microsoft.CodeAnalysis.Editor.Shared.Utilities;
using Microsoft.CodeAnalysis.Host.Mef;
using Microsoft.CodeAnalysis.Options;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Microsoft.CodeAnalysis.Text;
using Microsoft.VisualStudio.Commanding;
using Microsoft.VisualStudio.Language.Intellisense.AsyncCompletion;
using Microsoft.VisualStudio.LanguageServices.Implementation.Snippets;
using Microsoft.VisualStudio.Text;
using Microsoft.VisualStudio.Text.Editor;
using Microsoft.VisualStudio.Text.Editor.Commanding.Commands;
using Microsoft.VisualStudio.Text.Editor.Expansion;
using Microsoft.VisualStudio.UI;
using Microsoft.VisualStudio.Utilities;

namespace Microsoft.VisualStudio.LanguageServices.CSharp.Snippets
{
    [Export(typeof(ICommandHandler))]
    [ContentType(CodeAnalysis.Editor.ContentTypeNames.CSharpContentType)]
    [Name("CSharp Snippets")]
    [Order(After = PredefinedCompletionNames.CompletionCommandHandler)]
    [Order(After = CodeAnalysis.Editor.PredefinedCommandHandlerNames.SignatureHelpAfterCompletion)]
    internal sealed class SnippetCommandHandler :
        AbstractSnippetCommandHandler,
        ICommandHandler<SurroundWithCommandArgs>
    {
        [ImportingConstructor]
        [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)]
        public SnippetCommandHandler(
            IThreadingContext threadingContext,
            IExpansionServiceProvider expansionServiceProvider,
            IExpansionManager expansionManager,
            EditorOptionsService editorOptionsService)
            : base(threadingContext, expansionServiceProvider, expansionManager, editorOptionsService)
        {
        }

        public bool ExecuteCommand(SurroundWithCommandArgs args, CommandExecutionContext context)
        {
            ThreadingContext.ThrowIfNotOnUIThread();

            if (!AreSnippetsEnabled(args))
            {
                return false;
            }

            return TryInvokeInsertionUI(args.TextView, args.SubjectBuffer, surroundWith: true);
        }

        public CommandState GetCommandState(SurroundWithCommandArgs args)
        {
            ThreadingContext.ThrowIfNotOnUIThread();

            if (!AreSnippetsEnabled(args))
            {
                return CommandState.Unspecified;
            }

            if (!CodeAnalysis.Workspace.TryGetWorkspace(args.SubjectBuffer.AsTextContainer(), out var workspace))
            {
                return CommandState.Unspecified;
            }

            if (!workspace.CanApplyChange(ApplyChangesKind.ChangeDocument))
            {
                return CommandState.Unspecified;
            }

            return CommandState.Available;
        }

        protected override AbstractSnippetExpansionClient GetSnippetExpansionClient(ITextView textView, ITextBuffer subjectBuffer)
        {
            if (!textView.Properties.TryGetProperty(typeof(AbstractSnippetExpansionClient), out AbstractSnippetExpansionClient expansionClient))
            {
                expansionClient = new SnippetExpansionClient(subjectBuffer.ContentType, textView, subjectBuffer, ExpansionServiceProvider, EditorOptionsService);
                textView.Properties.AddProperty(typeof(AbstractSnippetExpansionClient), expansionClient);
            }

            return expansionClient;
        }

        protected override bool TryInvokeInsertionUI(ITextView textView, ITextBuffer subjectBuffer, bool surroundWith = false)
        {
            ExpansionServiceProvider.GetExpansionService(textView).InvokeInsertionUI(
                GetSnippetExpansionClient(textView, subjectBuffer),
                subjectBuffer.ContentType,
                types: surroundWith ? new[] { "SurroundsWith" } : new[] { "Expansion", "SurroundsWith" },
                includeNullType: true,
                kinds: null,
                includeNullKind: false,
                prefixText: surroundWith ? GettextCatalog.GetString("Surround With") : GettextCatalog.GetString("Insert Snippet"),
                completionChar: null);

            return true;
        }

        protected override bool IsSnippetExpansionContext(Document document, int startPosition, CancellationToken cancellationToken)
        {
            var syntaxTree = document.GetRequiredSyntaxTreeSynchronously(cancellationToken);
            var token = syntaxTree.GetRoot(cancellationToken).FindToken(startPosition);
            var trivia = syntaxTree.GetRoot(cancellationToken).FindTrivia(startPosition);
            return !(trivia.IsKind(SyntaxKind.MultiLineCommentTrivia) || trivia.IsKind(SyntaxKind.SingleLineCommentTrivia) || token.IsKind(SyntaxKind.StringLiteralToken));
        }
    }
}
