' 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.Generic
Imports System.Collections.Immutable
Imports Microsoft.CodeAnalysis
Imports Microsoft.CodeAnalysis.Collections
Imports Microsoft.CodeAnalysis.PooledObjects
Imports Microsoft.CodeAnalysis.Text
Imports Microsoft.CodeAnalysis.VisualBasic.Symbols
Imports Microsoft.CodeAnalysis.VisualBasic.Syntax

Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols

    Partial Friend NotInheritable Class AnonymousTypeManager

        Friend NotInheritable Class AnonymousTypePublicSymbol
            Inherits AnonymousTypeOrDelegatePublicSymbol

            Private ReadOnly _properties As ImmutableArray(Of AnonymousTypePropertyPublicSymbol)
            Private ReadOnly _members As ImmutableArray(Of Symbol)
            Private ReadOnly _interfaces As ImmutableArray(Of NamedTypeSymbol)

            Public Sub New(manager As AnonymousTypeManager, typeDescr As AnonymousTypeDescriptor)
                MyBase.New(manager, typeDescr)

                Dim fieldsCount As Integer = typeDescr.Fields.Length

                Dim methodMembersBuilder = ArrayBuilder(Of Symbol).GetInstance()
                Dim otherMembersBuilder = ArrayBuilder(Of Symbol).GetInstance()

                ' The array storing property symbols to be used in 
                ' generation of constructor and other methods
                Dim propertiesArray = New AnonymousTypePropertyPublicSymbol(fieldsCount - 1) {}

                ' Anonymous types with at least one Key field are being generated slightly different
                Dim hasAtLeastOneKeyField As Boolean = False

                '  Process fields
                For fieldIndex = 0 To fieldsCount - 1

                    Dim field As AnonymousTypeField = typeDescr.Fields(fieldIndex)
                    If field.IsKey Then
                        hasAtLeastOneKeyField = True
                    End If

                    ' Add a property
                    Dim [property] As New AnonymousTypePropertyPublicSymbol(Me, fieldIndex)
                    propertiesArray(fieldIndex) = [property]

                    ' Property related symbols
                    otherMembersBuilder.Add([property])
                    methodMembersBuilder.Add([property].GetMethod)
                    If [property].SetMethod IsNot Nothing Then
                        methodMembersBuilder.Add([property].SetMethod)
                    End If
                Next

                _properties = propertiesArray.AsImmutableOrNull()

                ' Add a constructor
                methodMembersBuilder.Add(CreateConstructorSymbol())
                ' Add 'ToString'
                methodMembersBuilder.Add(CreateToStringMethod())

                ' Add optional members
                If hasAtLeastOneKeyField AndAlso Me.Manager.System_IEquatable_T_Equals IsNot Nothing Then

                    ' Add 'GetHashCode'
                    methodMembersBuilder.Add(CreateGetHashCodeMethod())

                    ' Add optional 'Inherits IEquatable'
                    Dim equatableInterface As NamedTypeSymbol = Me.Manager.System_IEquatable_T.Construct(ImmutableArray.Create(Of TypeSymbol)(Me))
                    Me._interfaces = ImmutableArray.Create(Of NamedTypeSymbol)(equatableInterface)

                    ' Add 'IEquatable.Equals'
                    Dim method As Symbol = DirectCast(equatableInterface, SubstitutedNamedType).GetMemberForDefinition(Me.Manager.System_IEquatable_T_Equals)
                    methodMembersBuilder.Add(CreateIEquatableEqualsMethod(DirectCast(method, MethodSymbol)))

                    ' Add 'Equals'
                    methodMembersBuilder.Add(CreateEqualsMethod())

                Else
                    _interfaces = ImmutableArray(Of NamedTypeSymbol).Empty
                End If

                methodMembersBuilder.AddRange(otherMembersBuilder)
                otherMembersBuilder.Free()
                _members = methodMembersBuilder.ToImmutableAndFree()
            End Sub

            Private Function CreateConstructorSymbol() As MethodSymbol
                Dim constructor As New SynthesizedSimpleConstructorSymbol(Me)

                Dim fieldsCount As Integer = Me._properties.Length
                Dim paramsArr = New ParameterSymbol(fieldsCount - 1) {}
                For index = 0 To fieldsCount - 1
                    Dim [property] As PropertySymbol = Me._properties(index)
                    paramsArr(index) = New SynthesizedParameterSimpleSymbol(constructor, [property].Type, index, [property].Name)
                Next
                constructor.SetParameters(paramsArr.AsImmutableOrNull())

                Return constructor
            End Function

            Private Function CreateEqualsMethod() As MethodSymbol
                Dim method As New SynthesizedSimpleMethodSymbol(Me, WellKnownMemberNames.ObjectEquals, Me.Manager.System_Boolean,
                                                                overriddenMethod:=Me.Manager.System_Object__Equals,
                                                                isOverloads:=True)

                method.SetParameters(ImmutableArray.Create(Of ParameterSymbol)(
                                        New SynthesizedParameterSimpleSymbol(method, Me.Manager.System_Object, 0, "obj")
                                    ))

                Return method
            End Function

            Private Function CreateIEquatableEqualsMethod(iEquatableEquals As MethodSymbol) As MethodSymbol
                Dim method As New SynthesizedSimpleMethodSymbol(Me, WellKnownMemberNames.ObjectEquals, Me.Manager.System_Boolean,
                                                                interfaceMethod:=iEquatableEquals,
                                                                isOverloads:=True)

                method.SetParameters(ImmutableArray.Create(Of ParameterSymbol)(
                                        New SynthesizedParameterSimpleSymbol(method, Me, 0, "val")
                                    ))

                Return method
            End Function

            Private Function CreateGetHashCodeMethod() As MethodSymbol
                Dim method As New SynthesizedSimpleMethodSymbol(Me, WellKnownMemberNames.ObjectGetHashCode, Me.Manager.System_Int32,
                                                                overriddenMethod:=Me.Manager.System_Object__GetHashCode)

                method.SetParameters(ImmutableArray(Of ParameterSymbol).Empty)
                Return method
            End Function

            Private Function CreateToStringMethod() As MethodSymbol
                Dim method As New SynthesizedSimpleMethodSymbol(Me, WellKnownMemberNames.ObjectToString, Me.Manager.System_String,
                                                                overriddenMethod:=Me.Manager.System_Object__ToString)

                method.SetParameters(ImmutableArray(Of ParameterSymbol).Empty)
                Return method
            End Function

            Public Overrides ReadOnly Property TypeKind As TypeKind
                Get
                    Return TypeKind.Class
                End Get
            End Property

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

            Public ReadOnly Property Properties As ImmutableArray(Of AnonymousTypePropertyPublicSymbol)
                Get
                    Return Me._properties
                End Get
            End Property

            Friend Overrides Function InternalSubstituteTypeParameters(substitution As TypeSubstitution) As TypeWithModifiers
                Dim newDescriptor As New AnonymousTypeDescriptor
                If Not Me.TypeDescriptor.SubstituteTypeParametersIfNeeded(substitution, newDescriptor) Then
                    Return New TypeWithModifiers(Me)
                End If

                Return New TypeWithModifiers(Me.Manager.ConstructAnonymousTypeSymbol(newDescriptor, BindingDiagnosticBag.Discarded))
            End Function

            Public Overrides Function GetMembers() As ImmutableArray(Of Symbol)
                Return _members
            End Function

            Friend Overrides Function MakeAcyclicBaseType(diagnostics As BindingDiagnosticBag) As NamedTypeSymbol
                Return Me.Manager.System_Object
            End Function

            Friend Overrides Function MakeAcyclicInterfaces(diagnostics As BindingDiagnosticBag) As ImmutableArray(Of NamedTypeSymbol)
                Return _interfaces
            End Function

            Public Overrides Function MapToImplementationSymbol() As NamedTypeSymbol
                Return Me.Manager.ConstructAnonymousTypeImplementationSymbol(Me)
            End Function

            Public Overrides ReadOnly Property DeclaringSyntaxReferences As ImmutableArray(Of SyntaxReference)
                Get
                    Return GetDeclaringSyntaxReferenceHelper(Of AnonymousObjectCreationExpressionSyntax)(Me.Locations)
                End Get
            End Property
        End Class

    End Class

End Namespace
