// 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 Microsoft.CodeAnalysis.Host;
using Microsoft.CodeAnalysis.LanguageService;
using Microsoft.CodeAnalysis.Operations;

namespace Microsoft.CodeAnalysis.Editing
{
    /// <summary>
    /// Internal extensions to <see cref="SyntaxGenerator"/>.
    /// 
    /// This interface is available in the shared CodeStyle and Workspaces layer to allow
    /// sharing internal generator methods between them. Once the methods are ready to be
    /// made public APIs, they can be moved to <see cref="SyntaxGenerator"/>.
    /// </summary>
    internal abstract class SyntaxGeneratorInternal : ILanguageService
    {
        public abstract ISyntaxFacts SyntaxFacts { get; }

        public abstract SyntaxTrivia EndOfLine(string text);

        /// <summary>
        /// Creates a statement that declares a single local variable with an optional initializer.
        /// </summary>
        public abstract SyntaxNode LocalDeclarationStatement(
            SyntaxNode? type, SyntaxToken identifier, SyntaxNode? initializer = null, bool isConst = false);

        /// <summary>
        /// Creates a statement that declares a single local variable.
        /// </summary>
        public SyntaxNode LocalDeclarationStatement(SyntaxToken name, SyntaxNode initializer)
            => LocalDeclarationStatement(null, name, initializer);

        public abstract SyntaxNode WithInitializer(SyntaxNode variableDeclarator, SyntaxNode initializer);

        public abstract SyntaxNode EqualsValueClause(SyntaxToken operatorToken, SyntaxNode value);

        public abstract SyntaxToken Identifier(string identifier);

        public abstract SyntaxNode ConditionalAccessExpression(SyntaxNode expression, SyntaxNode whenNotNull);

        public abstract SyntaxNode MemberBindingExpression(SyntaxNode name);

        public abstract SyntaxNode RefExpression(SyntaxNode expression);

        /// <summary>
        /// Wraps with parens.
        /// </summary>
        public abstract SyntaxNode AddParentheses(SyntaxNode expression, bool includeElasticTrivia = true, bool addSimplifierAnnotation = true);

        /// <summary>
        /// Creates a statement that can be used to yield a value from an iterator method.
        /// </summary>
        /// <param name="expression">An expression that can be yielded.</param>
        public abstract SyntaxNode YieldReturnStatement(SyntaxNode expression);

        /// <summary>
        /// <see langword="true"/> if the language requires a "TypeExpression"
        /// (including <see langword="var"/>) to be stated when making a 
        /// <see cref="LocalDeclarationStatement(SyntaxNode, SyntaxToken, SyntaxNode, bool)"/>.
        /// <see langword="false"/> if the language allows the type node to be entirely elided.
        /// </summary>
        public abstract bool RequiresLocalDeclarationType();

        public abstract SyntaxToken InterpolatedStringTextToken(string content, string value);
        public abstract SyntaxNode InterpolatedStringText(SyntaxToken textToken);
        public abstract SyntaxNode Interpolation(SyntaxNode syntaxNode);
        public abstract SyntaxNode InterpolatedStringExpression(SyntaxToken startToken, IEnumerable<SyntaxNode> content, SyntaxToken endToken);
        public abstract SyntaxNode InterpolationAlignmentClause(SyntaxNode alignment);
        public abstract SyntaxNode InterpolationFormatClause(string format);
        public abstract SyntaxNode TypeParameterList(IEnumerable<string> typeParameterNames);

        /// <summary>
        /// Produces an appropriate TypeSyntax for the given <see cref="ITypeSymbol"/>.  The <paramref name="typeContext"/>
        /// flag controls how this should be created depending on if this node is intended for use in a type-only
        /// context, or an expression-level context.  In the former case, both C# and VB will create QualifiedNameSyntax
        /// nodes for dotted type names, whereas in the latter case both languages will create MemberAccessExpressionSyntax
        /// nodes.  The final stringified result will be the same in both cases.  However, the structure of the trees
        /// will be substantively different, which can impact how the compilation layers analyze the tree and how
        /// transformational passes affect it.
        /// </summary>
        /// <remarks>
        /// Passing in the right value for <paramref name="typeContext"/> is necessary for correctness and for use
        /// of compilation (and other) layers in a supported fashion.  For example, if a QualifiedTypeSyntax is
        /// sed in a place the compiler would have parsed out a MemberAccessExpression, then it is undefined behavior
        /// what will happen if that tree is passed to any other components.
        /// </remarks>
        public abstract SyntaxNode Type(ITypeSymbol typeSymbol, bool typeContext);

        public abstract SyntaxNode NegateEquality(SyntaxGenerator generator, SyntaxNode binaryExpression, SyntaxNode left, BinaryOperatorKind negatedKind, SyntaxNode right);

        public abstract SyntaxNode IsNotTypeExpression(SyntaxNode expression, SyntaxNode type);

        #region Patterns

        public abstract bool SupportsPatterns(ParseOptions options);
        public abstract SyntaxNode IsPatternExpression(SyntaxNode expression, SyntaxToken isToken, SyntaxNode pattern);

        public abstract SyntaxNode AndPattern(SyntaxNode left, SyntaxNode right);
        public abstract SyntaxNode ConstantPattern(SyntaxNode expression);
        public abstract SyntaxNode DeclarationPattern(INamedTypeSymbol type, string name);
        public abstract SyntaxNode GreaterThanRelationalPattern(SyntaxNode expression);
        public abstract SyntaxNode GreaterThanEqualsRelationalPattern(SyntaxNode expression);
        public abstract SyntaxNode LessThanRelationalPattern(SyntaxNode expression);
        public abstract SyntaxNode LessThanEqualsRelationalPattern(SyntaxNode expression);
        public abstract SyntaxNode NotPattern(SyntaxNode pattern);
        public abstract SyntaxNode OrPattern(SyntaxNode left, SyntaxNode right);
        public abstract SyntaxNode ParenthesizedPattern(SyntaxNode pattern);
        public abstract SyntaxNode TypePattern(SyntaxNode type);
        public abstract SyntaxNode UnaryPattern(SyntaxToken operatorToken, SyntaxNode pattern);

        #endregion
    }
}
