// 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.Diagnostics.CodeAnalysis;
using System.Threading;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CodeGeneration;
using Microsoft.VisualStudio.LanguageServices.Implementation.CodeModel.InternalElements;
using Microsoft.VisualStudio.LanguageServices.Implementation.CodeModel.Interop;
using Microsoft.VisualStudio.Text;

namespace Microsoft.VisualStudio.LanguageServices.Implementation.CodeModel;

internal interface ICodeModelService : ICodeModelNavigationPointService
{
    /// <summary>
    /// Retrieves the Option nodes (i.e. VB Option statements) parented
    /// by the given node.
    /// </summary>
    IEnumerable<SyntaxNode> GetOptionNodes(SyntaxNode parent);

    /// <summary>
    /// Retrieves the import nodes (e.g. using/Import directives) parented
    /// by the given node.
    /// </summary>
    IEnumerable<SyntaxNode> GetImportNodes(SyntaxNode parent);

    /// <summary>
    /// Retrieves the attributes parented or owned by the given node.
    /// </summary>
    IEnumerable<SyntaxNode> GetAttributeNodes(SyntaxNode parent);

    /// <summary>
    /// Retrieves the attribute arguments parented by the given node.
    /// </summary>
    IEnumerable<SyntaxNode> GetAttributeArgumentNodes(SyntaxNode parent);

    /// <summary>
    /// Retrieves the Inherits nodes (i.e. VB Inherits statements) parented 
    /// or owned by the given node.
    /// </summary>
    IEnumerable<SyntaxNode> GetInheritsNodes(SyntaxNode parent);

    /// <summary>
    /// Retrieves the Implements nodes (i.e. VB Implements statements) parented 
    /// or owned by the given node.
    /// </summary>
    IEnumerable<SyntaxNode> GetImplementsNodes(SyntaxNode parent);

    /// <summary>
    /// Retrieves the members of a specified <paramref name="container"/> node. The members that are
    /// returned can be controlled by passing various parameters.
    /// </summary>
    /// <param name="container">The <see cref="SyntaxNode"/> from which to retrieve members.</param>
    /// <param name="includeSelf">If true, the container is returned as well.</param>
    /// <param name="recursive">If true, members are recursed to return descendant members as well
    /// as immediate children. For example, a namespace would return the namespaces and types within.
    /// However, if <paramref name="recursive"/> is true, members with the namespaces and types would
    /// also be returned.</param>
    /// <param name="logicalFields">If true, field declarations are broken into their respective declarators.
    /// For example, the field "int x, y" would return two declarators, one for x and one for y in place
    /// of the field.</param>
    /// <param name="onlySupportedNodes">If true, only members supported by Code Model are returned.</param>
    IEnumerable<SyntaxNode> GetMemberNodes(SyntaxNode container, bool includeSelf, bool recursive, bool logicalFields, bool onlySupportedNodes);

    IEnumerable<SyntaxNode> GetLogicalSupportedMemberNodes(SyntaxNode container);

    SyntaxNodeKey GetNodeKey(SyntaxNode node);
    SyntaxNodeKey TryGetNodeKey(SyntaxNode node);
    SyntaxNode LookupNode(SyntaxNodeKey nodeKey, SyntaxTree syntaxTree);
    bool TryLookupNode(SyntaxNodeKey nodeKey, SyntaxTree syntaxTree, [NotNullWhen(true)] out SyntaxNode? node);

    bool MatchesScope(SyntaxNode node, EnvDTE.vsCMElement scope);

    string Language { get; }
    string AssemblyAttributeString { get; }

    /// <summary>
    /// Do not use this method directly! Instead, go through <see cref="FileCodeModel.GetOrCreateCodeElement{T}(SyntaxNode)"/>
    /// </summary>
    EnvDTE.CodeElement CreateInternalCodeElement(CodeModelState state, FileCodeModel fileCodeModel, SyntaxNode node);
    EnvDTE.CodeElement CreateExternalCodeElement(CodeModelState state, ProjectId projectId, ISymbol symbol);
    EnvDTE.CodeElement CreateUnknownCodeElement(CodeModelState state, FileCodeModel fileCodeModel, SyntaxNode node);
    EnvDTE.CodeElement CreateUnknownRootNamespaceCodeElement(CodeModelState state, FileCodeModel fileCodeModel);

    EnvDTE.CodeElement CreateCodeType(CodeModelState state, ProjectId projectId, ITypeSymbol typeSymbol);

    /// <summary>
    /// Used by RootCodeModel.CreateCodeTypeRef to create an EnvDTE.CodeTypeRef.
    /// </summary>
    EnvDTE.CodeTypeRef CreateCodeTypeRef(CodeModelState state, ProjectId projectId, object type);

    EnvDTE.vsCMTypeRef GetTypeKindForCodeTypeRef(ITypeSymbol typeSymbol);
    string GetAsFullNameForCodeTypeRef(ITypeSymbol typeSymbol);
    string GetAsStringForCodeTypeRef(ITypeSymbol typeSymbol);

    bool IsParameterNode(SyntaxNode node);
    bool IsAttributeNode(SyntaxNode node);
    bool IsAttributeArgumentNode(SyntaxNode node);
    bool IsOptionNode(SyntaxNode node);
    bool IsImportNode(SyntaxNode node);

    ISymbol? ResolveSymbol(Microsoft.CodeAnalysis.Workspace workspace, ProjectId projectId, SymbolKey symbolId);

    string GetUnescapedName(string name);

    /// <summary>
    /// Retrieves the value to be returned from the EnvDTE.CodeElement.Name property.
    /// </summary>
    string GetName(SyntaxNode node);
    SyntaxNode GetNodeWithName(SyntaxNode node);
    SyntaxNode SetName(SyntaxNode node, string name);

    /// <summary>
    /// Retrieves the value to be returned from the EnvDTE.CodeElement.FullName property.
    /// </summary>
    string GetFullName(SyntaxNode node, SemanticModel semanticModel);

    /// <summary>
    /// Given a name, attempts to convert it to a fully qualified name.
    /// </summary>
    string GetFullyQualifiedName(string name, int position, SemanticModel semanticModel);

    void Rename(ISymbol symbol, string newName, Workspace workspace, ProjectCodeModelFactory projectCodeModelFactory);

    /// <summary>
    /// Returns true if the given <paramref name="symbol"/> can be used to create an external code element; otherwise, false.
    /// </summary>
    bool IsValidExternalSymbol(ISymbol symbol);

    /// <summary>
    /// Returns the value to be returned from <see cref="EnvDTE.CodeElement.Name"/> for external code elements.
    /// </summary>
    string GetExternalSymbolName(ISymbol symbol);

    /// <summary>
    /// Retrieves the value to be returned from <see cref="EnvDTE.CodeElement.FullName"/> for external code elements.
    /// </summary>
    string GetExternalSymbolFullName(ISymbol symbol);

#nullable disable
    SyntaxNode GetNodeWithModifiers(SyntaxNode node);
    SyntaxNode GetNodeWithType(SyntaxNode node);
#nullable restore
    SyntaxNode GetNodeWithInitializer(SyntaxNode node);

    EnvDTE.vsCMAccess GetAccess(ISymbol symbol);
    EnvDTE.vsCMAccess GetAccess(SyntaxNode node);
    SyntaxNode SetAccess(SyntaxNode node, EnvDTE.vsCMAccess access);
    EnvDTE.vsCMElement GetElementKind(SyntaxNode node);

    bool IsExpressionBodiedProperty(SyntaxNode node);
    bool IsAccessorNode(SyntaxNode node);
    MethodKind GetAccessorKind(SyntaxNode node);

    bool TryGetAccessorNode(SyntaxNode parentNode, MethodKind kind, [NotNullWhen(true)] out SyntaxNode? accessorNode);
    bool TryGetAutoPropertyExpressionBody(SyntaxNode parentNode, [NotNullWhen(true)] out SyntaxNode? expressionBody);
    bool TryGetParameterNode(SyntaxNode parentNode, string name, [NotNullWhen(true)] out SyntaxNode? parameterNode);
    bool TryGetImportNode(SyntaxNode parentNode, string dottedName, [NotNullWhen(true)] out SyntaxNode? importNode);
    bool TryGetOptionNode(SyntaxNode parentNode, string name, int ordinal, [NotNullWhen(true)] out SyntaxNode? optionNode);
    bool TryGetInheritsNode(SyntaxNode parentNode, string name, int ordinal, [NotNullWhen(true)] out SyntaxNode? inheritsNode);
    bool TryGetImplementsNode(SyntaxNode parentNode, string name, int ordinal, [NotNullWhen(true)] out SyntaxNode? implementsNode);
    bool TryGetAttributeNode(SyntaxNode parentNode, string name, int ordinal, [NotNullWhen(true)] out SyntaxNode? attributeNode);
    bool TryGetAttributeArgumentNode(SyntaxNode attributeNode, int index, [NotNullWhen(true)] out SyntaxNode? attributeArgumentNode);

    void GetOptionNameAndOrdinal(SyntaxNode parentNode, SyntaxNode optionNode, out string name, out int ordinal);
    void GetInheritsNamespaceAndOrdinal(SyntaxNode parentNode, SyntaxNode inheritsNode, out string namespaceName, out int ordinal);
    void GetImplementsNamespaceAndOrdinal(SyntaxNode parentNode, SyntaxNode implementsNode, out string namespaceName, out int ordinal);

    void GetAttributeArgumentParentAndIndex(SyntaxNode attributeArgumentNode, out SyntaxNode attributeNode, out int index);

    void GetAttributeNameAndOrdinal(SyntaxNode parentNode, SyntaxNode attributeNode, out string name, out int ordinal);
    SyntaxNode GetAttributeTargetNode(SyntaxNode attributeNode);
    string GetAttributeTarget(SyntaxNode attributeNode);
    string GetAttributeValue(SyntaxNode attributeNode);
    SyntaxNode SetAttributeTarget(SyntaxNode attributeNode, string value);
    SyntaxNode SetAttributeValue(SyntaxNode attributeNode, string value);

    /// <summary>
    /// Given a node, finds the related node that holds on to the attribute information.
    /// Generally, this will be an ancestor node. For example, given a C# VariableDeclarator,
    /// looks up the syntax tree to find the FieldDeclaration.
    /// </summary>
    SyntaxNode GetNodeWithAttributes(SyntaxNode node);

    /// <summary>
    /// Given node for an attribute, returns a node that can represent the parent.
    /// For example, an attribute on a C# field cannot use the FieldDeclaration (as it is
    /// not keyed) but instead must use one of the FieldDeclaration's VariableDeclarators.
    /// </summary>
    SyntaxNode GetEffectiveParentForAttribute(SyntaxNode node);

    SyntaxNode CreateAttributeNode(string name, string value, string? target = null);
    SyntaxNode CreateAttributeArgumentNode(string name, string value);
    SyntaxNode CreateImportNode(string name, string? alias = null);
    SyntaxNode CreateParameterNode(string name, string type);

    string GetAttributeArgumentValue(SyntaxNode attributeArgumentNode);

    string GetImportAlias(SyntaxNode node);
    string GetImportNamespaceOrType(SyntaxNode node);
    string GetParameterName(SyntaxNode node);
    string GetParameterFullName(SyntaxNode node);
    EnvDTE80.vsCMParameterKind GetParameterKind(SyntaxNode node);
    SyntaxNode SetParameterKind(SyntaxNode node, EnvDTE80.vsCMParameterKind kind);
    IEnumerable<SyntaxNode> GetParameterNodes(SyntaxNode parent);
    EnvDTE80.vsCMParameterKind UpdateParameterKind(EnvDTE80.vsCMParameterKind parameterKind, PARAMETER_PASSING_MODE passingMode);

    EnvDTE.vsCMFunction ValidateFunctionKind(SyntaxNode containerNode, EnvDTE.vsCMFunction kind, string name);

    bool SupportsEventThrower { get; }

    bool GetCanOverride(SyntaxNode memberNode);
    SyntaxNode SetCanOverride(SyntaxNode memberNode, bool value);

    EnvDTE80.vsCMClassKind GetClassKind(SyntaxNode typeNode, INamedTypeSymbol typeSymbol);
    SyntaxNode SetClassKind(SyntaxNode typeNode, EnvDTE80.vsCMClassKind kind);

    string GetComment(SyntaxNode node);
    SyntaxNode SetComment(SyntaxNode node, string value);

    EnvDTE80.vsCMConstKind GetConstKind(SyntaxNode variableNode);
    SyntaxNode SetConstKind(SyntaxNode variableNode, EnvDTE80.vsCMConstKind kind);

    EnvDTE80.vsCMDataTypeKind GetDataTypeKind(SyntaxNode typeNode, INamedTypeSymbol symbol);
    SyntaxNode SetDataTypeKind(SyntaxNode typeNode, EnvDTE80.vsCMDataTypeKind kind);

    string GetDocComment(SyntaxNode node);
    SyntaxNode SetDocComment(SyntaxNode node, string value);

    EnvDTE.vsCMFunction GetFunctionKind(IMethodSymbol symbol);

    EnvDTE80.vsCMInheritanceKind GetInheritanceKind(SyntaxNode typeNode, INamedTypeSymbol typeSymbol);
    SyntaxNode SetInheritanceKind(SyntaxNode node, EnvDTE80.vsCMInheritanceKind kind);

    bool GetIsAbstract(SyntaxNode memberNode, ISymbol symbol);
    SyntaxNode SetIsAbstract(SyntaxNode memberNode, bool value);

    bool GetIsConstant(SyntaxNode memberNode);
    SyntaxNode SetIsConstant(SyntaxNode memberNode, bool value);

    bool GetIsDefault(SyntaxNode propertyNode);
    SyntaxNode SetIsDefault(SyntaxNode propertyNode, bool value);

    bool GetIsGeneric(SyntaxNode memberNode);

    bool GetIsPropertyStyleEvent(SyntaxNode eventNode);

    bool GetIsShared(SyntaxNode memberNode, ISymbol symbol);
    SyntaxNode SetIsShared(SyntaxNode memberNode, bool value);

    bool GetMustImplement(SyntaxNode memberNode);
    SyntaxNode SetMustImplement(SyntaxNode memberNode, bool value);

    EnvDTE80.vsCMOverrideKind GetOverrideKind(SyntaxNode memberNode);
    SyntaxNode SetOverrideKind(SyntaxNode memberNode, EnvDTE80.vsCMOverrideKind kind);

    EnvDTE80.vsCMPropertyKind GetReadWrite(SyntaxNode memberNode);

    SyntaxNode SetType(SyntaxNode node, ITypeSymbol? typeSymbol);

    Document Delete(Document document, SyntaxNode node);

    string GetMethodXml(SyntaxNode node, SemanticModel semanticModel);

    string? GetInitExpression(SyntaxNode node);
    SyntaxNode AddInitExpression(SyntaxNode node, string value);

    CodeGenerationDestination GetDestination(SyntaxNode containerNode);

    /// <summary>
    /// Retrieves the Accessibility for an EnvDTE.vsCMAccess. If the specified value is
    /// EnvDTE.vsCMAccess.vsCMAccessDefault, then the SymbolKind and CodeGenerationDestination hints
    /// will be used to retrieve the correct Accessibility for the current language.
    /// </summary>
    Accessibility GetAccessibility(EnvDTE.vsCMAccess access, SymbolKind targetSymbolKind, CodeGenerationDestination destination = CodeGenerationDestination.Unspecified);
    bool GetWithEvents(EnvDTE.vsCMAccess access);

    /// <summary>
    /// Given an "type" argument received from a CodeModel client, converts it to an ITypeSymbol. Note that
    /// this parameter is a VARIANT and could be an EnvDTE.vsCMTypeRef, a string representing a fully-qualified
    /// type name, or an EnvDTE.CodeTypeRef.
    /// </summary>
    ITypeSymbol GetTypeSymbol(object type, SemanticModel semanticModel, int position);

    ITypeSymbol? GetTypeSymbolFromFullName(string fullName, Compilation compilation);

    SyntaxNode CreateReturnDefaultValueStatement(ITypeSymbol type);

    int PositionVariantToAttributeInsertionIndex(object position, SyntaxNode containerNode, FileCodeModel fileCodeModel);
    int PositionVariantToMemberInsertionIndex(object position, SyntaxNode containerNode, FileCodeModel fileCodeModel);
    int PositionVariantToAttributeArgumentInsertionIndex(object position, SyntaxNode containerNode, FileCodeModel fileCodeModel);
    int PositionVariantToImportInsertionIndex(object position, SyntaxNode containerNode, FileCodeModel fileCodeModel);
    int PositionVariantToParameterInsertionIndex(object position, SyntaxNode containerNode, FileCodeModel fileCodeModel);

    SyntaxNode InsertAttribute(
        Document document,
        bool batchMode,
        int insertionIndex,
        SyntaxNode containerNode,
        SyntaxNode attributeNode,
        CancellationToken cancellationToken,
        out Document newDocument);

    SyntaxNode InsertAttributeArgument(
        Document document,
        bool batchMode,
        int insertionIndex,
        SyntaxNode containerNode,
        SyntaxNode attributeArgumentNode,
        CancellationToken cancellationToken,
        out Document newDocument);

    SyntaxNode InsertImport(
        Document document,
        bool batchMode,
        int insertionIndex,
        SyntaxNode containerNode,
        SyntaxNode importNode,
        CancellationToken cancellationToken,
        out Document newDocument);

    SyntaxNode InsertMember(
        Document document,
        bool batchMode,
        int insertionIndex,
        SyntaxNode containerNode,
        SyntaxNode newMemberNode,
        CancellationToken cancellationToken,
        out Document newDocument);

    SyntaxNode InsertParameter(
        Document document,
        bool batchMode,
        int insertionIndex,
        SyntaxNode containerNode,
        SyntaxNode parameterNode,
        CancellationToken cancellationToken,
        out Document newDocument);

    Document UpdateNode(
        Document document,
        SyntaxNode node,
        SyntaxNode newNode,
        CancellationToken cancellationToken);

    Queue<CodeModelEvent> CollectCodeModelEvents(SyntaxTree oldTree, SyntaxTree newTree);

    bool IsNamespace(SyntaxNode node);
    bool IsType(SyntaxNode node);

    IList<string> GetHandledEventNames(SyntaxNode method, SemanticModel semanticModel);
    bool HandlesEvent(string eventName, SyntaxNode method, SemanticModel semanticModel);
    Document AddHandlesClause(Document document, string eventName, SyntaxNode method, CancellationToken cancellationToken);
    Document RemoveHandlesClause(Document document, string eventName, SyntaxNode method, CancellationToken cancellationToken);

    string[] GetFunctionExtenderNames();
    object GetFunctionExtender(string name, SyntaxNode node, ISymbol symbol);
    string[] GetPropertyExtenderNames();
    object GetPropertyExtender(string name, SyntaxNode node, ISymbol symbol);
    string[] GetExternalTypeExtenderNames();
    object GetExternalTypeExtender(string name, string externalLocation);
    string[] GetTypeExtenderNames();
    object GetTypeExtender(string name, AbstractCodeType codeType);

    bool IsValidBaseType(SyntaxNode node, ITypeSymbol typeSymbol);
    SyntaxNode AddBase(SyntaxNode node, ITypeSymbol typeSymbol, SemanticModel semanticModel, int? position);
    SyntaxNode RemoveBase(SyntaxNode node, ITypeSymbol typeSymbol, SemanticModel semanticModel);

    bool IsValidInterfaceType(SyntaxNode node, ITypeSymbol typeSymbol);
    SyntaxNode AddImplementedInterface(SyntaxNode node, ITypeSymbol typeSymbol, SemanticModel semanticModel, int? position);
    SyntaxNode RemoveImplementedInterface(SyntaxNode node, ITypeSymbol typeSymbol, SemanticModel semanticModel);

    string GetPrototype(SyntaxNode node, ISymbol symbol, PrototypeFlags flags);

    void AttachFormatTrackingToBuffer(ITextBuffer buffer);
    void DetachFormatTrackingToBuffer(ITextBuffer buffer);
    void EnsureBufferFormatted(ITextBuffer buffer);
}
