' 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 Microsoft.CodeAnalysis.VisualBasic.Symbols
Imports Microsoft.CodeAnalysis.VisualBasic.Syntax
Imports Microsoft.CodeAnalysis.Test.Utilities
Imports Roslyn.Test.Utilities
Imports System.Xml.Linq
Imports Xunit

Namespace Microsoft.CodeAnalysis.VisualBasic.UnitTests
    Public Class CodeGenWinMdDelegates
        Inherits BasicTestBase

        ''' <summary>
        ''' When the output type is .winmdobj, delegate types shouldn't output Begin/End invoke 
        ''' members.
        ''' </summary>
        <Fact()>
        Public Sub SimpleDelegateMembersTest()
            Dim libSrc =
                <compilation>
                    <file name="c.vb">
                        <![CDATA[
Namespace Test
  Public Delegate Sub SubDelegate()
End Namespace]]>
                    </file>
                </compilation>

            Dim getValidator =
                Function(expectedMembers As String())
                    Return Sub(m As ModuleSymbol)
                               Dim actualMembers = m.GlobalNamespace.GetMember(Of NamespaceSymbol)("Test").
                                   GetMember(Of NamedTypeSymbol)("SubDelegate").GetMembers().ToArray()

                               AssertEx.SetEqual((From s In actualMembers Select s.Name), expectedMembers)
                           End Sub
                End Function


            Dim verify =
                Sub(winmd As Boolean, expected As String())
                    Dim validator = getValidator(expected)

                    ' We should see the same members from both source and metadata
                    Dim verifier = CompileAndVerify(
                        libSrc,
                        sourceSymbolValidator:=validator,
                        symbolValidator:=validator,
                        options:=If(winmd, TestOptions.ReleaseWinMD, TestOptions.ReleaseDll))
                    verifier.VerifyDiagnostics()
                End Sub

            ' Test winmd
            verify(True, New String() {
                WellKnownMemberNames.InstanceConstructorName,
                WellKnownMemberNames.DelegateInvokeName})

            ' Test normal
            verify(False, New String() {
                WellKnownMemberNames.InstanceConstructorName,
                WellKnownMemberNames.DelegateInvokeName,
                WellKnownMemberNames.DelegateBeginInvokeName,
                WellKnownMemberNames.DelegateEndInvokeName})
        End Sub

        <Fact()>
        Public Sub AnonymousDelegate()
            Dim src =
                <compilation>
                    <file name="c.vb">
                        <![CDATA[
Class C
    Public Sub S()
        Dim x = Function(y As Integer) y
    End Sub

    Public F = Function(x As Integer) x
End Class]]>
                    </file>
                </compilation>

            Dim srcValidator =
                Sub(m As ModuleSymbol)
                    Dim comp = m.DeclaringCompilation
                    Dim tree = comp.SyntaxTrees.Single()
                    Dim model = comp.GetSemanticModel(tree)
                    Dim node = tree.GetRoot().DescendantNodes().OfType(Of ModifiedIdentifierSyntax).First()
                    Dim nodeSymbol = DirectCast(model.GetDeclaredSymbol(node), LocalSymbol).Type

                    Assert.True(nodeSymbol.IsAnonymousType)
                    AssertEx.SetEqual((From member In nodeSymbol.GetMembers() Select member.Name),
                                      {WellKnownMemberNames.InstanceConstructorName,
                                       "Invoke"})
                End Sub

            Dim mdValidator =
                Sub(m As ModuleSymbol)
                    Dim members = m.GlobalNamespace
                End Sub

            Dim verifier = CompileAndVerify(
                src,
                sourceSymbolValidator:=srcValidator,
                symbolValidator:=mdValidator,
                options:=TestOptions.ReleaseWinMD)
        End Sub

        <Fact()>
        Public Sub TestAllDelegates()
            Dim winRtDelegateLibrarySrc =
                <compilation>
                    <file name="WinRTDelegateLibrary.vb"><![CDATA[
Namespace WinRTDelegateLibrary
    Public Structure S1
    End Structure  

    Public Enum E1
        alpha = 1
        bravo
        charlie
        delta
    End Enum

    Public Class C1
    End Class

    Public Interface I1
    End Interface

    ''' 
    ''' These are the interesting types
    ''' 

    Public Delegate Sub SubSubDelegate()

    Public Delegate Function intintDelegate(a As Integer) As Integer
           
    Public Delegate Function structDelegate(s As S1) As S1
           
    Public Delegate Function enumDelegate(e As E1) As E1
           
    Public Delegate Function classDelegate(c As C1) As C1
           
    Public Delegate Function stringDelegate(s As string) As string
           
    Public Delegate Function decimalDelegate(d As Decimal) As Decimal
           
    Public Delegate Function WinRTDelegate(d As SubSubDelegate) as SubSubDelegate
           
    Public Delegate Function nullableDelegate(a As Integer?) As Integer?
           
    Public Delegate Function genericDelegate(Of T)(t As T) As T
    Public Delegate Function genericDelegate2(Of T As New)(t As T) As T
    Public Delegate Function genericDelegate3(Of T As Class)(t As T) As T
    Public Delegate Function genericDelegate4(Of T As Structure)(t As T) As T
    Public Delegate Function genericDelegate5(Of T As I1)(t As T) As T
           
    Public Delegate Function arrayDelegate(arr as Integer()) As Integer()
           
    Public Delegate Function interfaceDelegate(i As I1) As I1
End Namespace
]]>

                    </file>
                </compilation>

            ' We need the 4.5 refs here
            Dim coreRefs45 = {
            MscorlibRef_v4_0_30316_17626,
            SystemCoreRef_v4_0_30319_17929}

            Dim winRtDelegateLibrary = CompilationUtils.CreateEmptyCompilationWithReferences(
                winRtDelegateLibrarySrc,
                references:=coreRefs45,
                options:=TestOptions.ReleaseWinMD).EmitToImageReference()

            Dim fileElement = winRtDelegateLibrarySrc.<file>.Single()
            fileElement.ReplaceAttributes(New XAttribute("name", "NonWinRTDelegateLibrary.vb"))
            fileElement.SetValue(fileElement.Value.Replace("WinRTDelegateLibrary", "NonWinRTDelegateLibrary"))

            Dim nonWinRtDelegateLibrary = CompilationUtils.CreateEmptyCompilationWithReferences(
                winRtDelegateLibrarySrc,
                references:=coreRefs45,
                options:=TestOptions.ReleaseDll).EmitToImageReference()

            Dim allDelegates =
                <compilation>
                    <file name="c.vb">
                        <![CDATA[
Imports WinRT = WinRTDelegateLibrary
Imports NonWinRT = NonWinRTDelegateLibrary

Class Test
    Public d001 As WinRT.SubSubDelegate
    Public d101 As NonWinRT.SubSubDelegate

    Public d002 As WinRT.intintDelegate
    Public d102 As NonWinRT.intintDelegate

    Public d003 As WinRT.structDelegate
    Public d103 As NonWinRT.structDelegate

    Public d004 As WinRT.enumDelegate
    Public d104 As NonWinRT.enumDelegate

    Public d005 As WinRT.classDelegate
    Public d105 As NonWinRT.classDelegate

    Public d006 As WinRT.stringDelegate
    Public d106 As NonWinRT.stringDelegate

    Public d007 As WinRT.decimalDelegate
    Public d107 As NonWinRT.decimalDelegate

    Public d008 As WinRT.WinRTDelegate
    Public d108 As NonWinRT.WinRTDelegate

    Public d009 As WinRT.nullableDelegate
    Public d109 As NonWinRT.nullableDelegate

    Public d010 As WinRT.genericDelegate(Of Single)
    Public d110 As NonWinRT.genericDelegate(Of Single)

    Public d011 As WinRT.genericDelegate2(Of Object)
    Public d111 As NonWinRT.genericDelegate2(Of Object)

    Public d012 As WinRT.genericDelegate3(Of WinRT.C1)
    Public d112 As NonWinRT.genericDelegate3(Of NonWinRT.C1)

    Public d013 As WinRT.genericDelegate4(Of WinRT.S1)
    Public d113 As NonWinRT.genericDelegate4(Of NonWinRT.S1)

    Public d014 As WinRT.genericDelegate5(Of WinRT.I1)
    Public d114 As NonWinRT.genericDelegate5(Of NonWinRT.I1)

    Public d015 As WinRT.arrayDelegate
    Public d115 As NonWinRT.arrayDelegate

    Public d016 As WinRT.interfaceDelegate
    Public d116 As NonWinRT.interfaceDelegate
End Class
]]>
                    </file>
                </compilation>

            Dim isWinRt = Function(field As FieldSymbol)
                              Dim fieldType = field.Type
                              If DirectCast(fieldType, Object) Is Nothing Then
                                  Return False
                              End If

                              If Not fieldType.IsDelegateType() Then
                                  Return False
                              End If

                              For Each member In fieldType.GetMembers()
                                  Select Case member.Name
                                      Case WellKnownMemberNames.DelegateBeginInvokeName
                                      Case WellKnownMemberNames.DelegateEndInvokeName
                                          Return False
                                  End Select
                              Next

                              Return True
                          End Function

            Dim validator As Action(Of ModuleSymbol) =
                Sub(m As ModuleSymbol)
                    Dim type = m.GlobalNamespace.GetMember(Of NamedTypeSymbol)("Test")
                    Dim fields = type.GetMembers()

                    For Each field In fields
                        Dim fieldSymbol = TryCast(field, FieldSymbol)
                        If DirectCast(fieldSymbol, Object) IsNot Nothing Then
                            If fieldSymbol.Name.Contains("d1") Then
                                Assert.False(isWinRt(fieldSymbol))
                            Else
                                Assert.True(isWinRt(fieldSymbol))
                            End If
                        End If
                    Next
                End Sub

            Dim verifier = CompileAndVerify(
                allDelegates,
                references:={
                    winRtDelegateLibrary,
                    nonWinRtDelegateLibrary},
                symbolValidator:=validator)

            verifier.VerifyDiagnostics()
        End Sub
    End Class
End Namespace
