' 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.

Imports System.Collections.Immutable
Imports Microsoft.CodeAnalysis.CodeGen
Imports Microsoft.CodeAnalysis.PooledObjects
Imports Microsoft.CodeAnalysis.Symbols
Imports Microsoft.CodeAnalysis.VisualBasic.Symbols

Namespace Microsoft.CodeAnalysis.VisualBasic
    Friend MustInherit Class StateMachineTypeSymbol
        Inherits SynthesizedContainer
        Implements ISynthesizedMethodBodyImplementationSymbol

        Private _attributes As ImmutableArray(Of VisualBasicAttributeData)
        Public ReadOnly KickoffMethod As MethodSymbol

        Public Sub New(slotAllocatorOpt As VariableSlotAllocator,
                       compilationState As TypeCompilationState,
                       kickoffMethod As MethodSymbol,
                       kickoffMethodOrdinal As Integer,
                       baseType As NamedTypeSymbol,
                       originalInterfaces As ImmutableArray(Of NamedTypeSymbol))
            MyBase.New(kickoffMethod, MakeName(slotAllocatorOpt, compilationState, kickoffMethod, kickoffMethodOrdinal), baseType, originalInterfaces)

            Debug.Assert(kickoffMethod IsNot Nothing)

            ' If the async method is partial the kickoff method should be the method with body (implementation)
            Debug.Assert(kickoffMethod.IsDefinition)
            Debug.Assert(Not kickoffMethod.IsPartial OrElse kickoffMethod.PartialImplementationPart Is Nothing)

            Me.KickoffMethod = kickoffMethod
        End Sub

        Private Shared Function MakeName(slotAllocatorOpt As VariableSlotAllocator, compilationState As TypeCompilationState, kickoffMethod As MethodSymbol, kickoffMethodOrdinal As Integer) As String
            Return If(slotAllocatorOpt?.PreviousStateMachineTypeName,
                      GeneratedNames.MakeStateMachineTypeName(kickoffMethod.Name,
                                                              kickoffMethodOrdinal,
                                                              If(compilationState.ModuleBuilderOpt?.CurrentGenerationOrdinal, 0))) ' Note: module builder is not available only when testing emit diagnostics
        End Function

        Public ReadOnly Property HasMethodBodyDependency As Boolean Implements ISynthesizedMethodBodyImplementationSymbol.HasMethodBodyDependency
            Get
                ' This method contains user code from the async method
                Return True
            End Get
        End Property

        Public ReadOnly Property Method As IMethodSymbolInternal Implements ISynthesizedMethodBodyImplementationSymbol.Method
            Get
                Return KickoffMethod
            End Get
        End Property

        Friend NotOverridable Overrides ReadOnly Property IsInterface As Boolean
            Get
                Return False
            End Get
        End Property

        Public NotOverridable Overrides Function GetAttributes() As ImmutableArray(Of VisualBasicAttributeData)
            If _attributes.IsDefault Then
                Debug.Assert(MyBase.GetAttributes().Length = 0)

                Dim builder As ArrayBuilder(Of VisualBasicAttributeData) = Nothing

                ' Inherit some attributes from the container of the kickoff method
                Dim kickoffType = KickoffMethod.ContainingType
                For Each attribute In kickoffType.GetAttributes()
                    If attribute.IsTargetAttribute(AttributeDescription.DebuggerNonUserCodeAttribute) OrElse
                       attribute.IsTargetAttribute(AttributeDescription.DebuggerStepThroughAttribute) Then
                        If builder Is Nothing Then
                            builder = ArrayBuilder(Of VisualBasicAttributeData).GetInstance(2) ' only 2 different attributes are inherited at the moment
                        End If

                        builder.Add(attribute)
                    End If
                Next

                ImmutableInterlocked.InterlockedCompareExchange(_attributes,
                                                                If(builder Is Nothing, ImmutableArray(Of VisualBasicAttributeData).Empty, builder.ToImmutableAndFree()),
                                                                Nothing)
            End If

            Return _attributes
        End Function
    End Class
End Namespace
