﻿// 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.Immutable;
using System.Diagnostics.CodeAnalysis;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Host;
using Microsoft.CodeAnalysis.LanguageService;
using Microsoft.CodeAnalysis.Text;

namespace Microsoft.CodeAnalysis.CodeRefactorings
{
    /// <summary>
    /// Contains helpers related to asking intuitive semantic questions about a users intent
    /// based on the position of their caret or span of their selection.
    /// </summary>
    internal interface IRefactoringHelpersService : IHeaderFactsService, ILanguageService
    {
        /// <summary>
        /// True if the user is on a blank line where a member could go inside a type declaration.
        /// This will be between members and not ever inside a member.
        /// </summary>
        bool IsBetweenTypeMembers(SourceText sourceText, SyntaxNode root, int position, [NotNullWhen(true)] out SyntaxNode? typeDeclaration);

        /// <summary>
        /// <para>
        /// Returns an array of <typeparamref name="TSyntaxNode"/> instances for refactoring given specified selection
        /// in document. <paramref name="allowEmptyNodes"/> determines if the returned nodes will can have empty spans
        /// or not.
        /// </para>
        /// <para>
        /// A <typeparamref name="TSyntaxNode"/> instance is returned if: - Selection is zero-width and inside/touching
        /// a Token with direct parent of type <typeparamref name="TSyntaxNode"/>. - Selection is zero-width and
        /// touching a Token whose ancestor of type <typeparamref name="TSyntaxNode"/> ends/starts precisely on current
        /// selection. - Selection is zero-width and in whitespace that corresponds to a Token whose direct ancestor is
        /// of type of type <typeparamref name="TSyntaxNode"/>. - Selection is zero-width and in a header (defined by
        /// ISyntaxFacts helpers) of an node of type of type <typeparamref name="TSyntaxNode"/>. - Token whose direct
        /// parent of type <typeparamref name="TSyntaxNode"/> is selected. - Selection is zero-width and wanted node is
        /// an expression / argument with selection within such syntax node (arbitrarily deep) on its first line. -
        /// Whole node of a type <typeparamref name="TSyntaxNode"/> is selected.
        /// </para>
        /// <para>
        /// Attempts extracting a Node of type <typeparamref name="TSyntaxNode"/> for each Node it considers (see
        /// above). E.g. extracts initializer expressions from declarations and assignments, Property declaration from
        /// any header node, etc.
        /// </para>
        /// <para>
        /// Note: this function trims all whitespace from both the beginning and the end of given <paramref
        /// name="selection"/>. The trimmed version is then used to determine relevant <see cref="SyntaxNode"/>. It also
        /// handles incomplete selections of tokens gracefully. Over-selection containing leading comments is also
        /// handled correctly. 
        /// </para>
        /// </summary>
        Task<ImmutableArray<TSyntaxNode>> GetRelevantNodesAsync<TSyntaxNode>(Document document, TextSpan selection, bool allowEmptyNodes, CancellationToken cancellationToken) where TSyntaxNode : SyntaxNode;
    }

    internal static class IRefactoringHelpersServiceExtensions
    {
        public static Task<ImmutableArray<TSyntaxNode>> GetRelevantNodesAsync<TSyntaxNode>(
            this IRefactoringHelpersService service, Document document, TextSpan selection, CancellationToken cancellationToken) where TSyntaxNode : SyntaxNode
        {
            return service.GetRelevantNodesAsync<TSyntaxNode>(document, selection, allowEmptyNodes: false, cancellationToken);
        }
    }
}
