// 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.Collections.Immutable;
using System.Diagnostics.CodeAnalysis;
using System.Reflection.Metadata;
using System.Runtime.InteropServices;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Symbols;
using EmitContext = Microsoft.CodeAnalysis.Emit.EmitContext;

namespace Microsoft.Cci
{
    internal enum PlatformType
    {
        SystemObject = CodeAnalysis.SpecialType.System_Object,
        SystemDecimal = CodeAnalysis.SpecialType.System_Decimal,
        SystemTypedReference = CodeAnalysis.SpecialType.System_TypedReference,
        SystemType = CodeAnalysis.WellKnownType.System_Type,
        SystemInt32 = CodeAnalysis.SpecialType.System_Int32,
        SystemVoid = CodeAnalysis.SpecialType.System_Void,
        SystemString = CodeAnalysis.SpecialType.System_String,
    }

    /// <summary>
    /// This interface models the metadata representation of an array type reference.
    /// </summary>
    internal interface IArrayTypeReference : ITypeReference
    {
        /// <summary>
        /// The type of the elements of this array.
        /// </summary>
        ITypeReference GetElementType(EmitContext context);

        /// <summary>
        /// This type of array is a single dimensional array with zero lower bound for index values.
        /// </summary>
        bool IsSZArray
        {
            get;
            // ^ ensures result ==> Rank == 1;
        }

        /// <summary>
        /// A possibly empty list of lower bounds for dimension indices. When not explicitly specified, a lower bound defaults to zero.
        /// The first lower bound in the list corresponds to the first dimension. Dimensions cannot be skipped.
        /// </summary>
        ImmutableArray<int> LowerBounds
        {
            get;
            // ^ ensures count(result) <= Rank;
        }

        /// <summary>
        /// The number of array dimensions.
        /// </summary>
        int Rank
        {
            get;
            // ^ ensures result > 0;
        }

        /// <summary>
        /// A possible empty list of upper bounds for dimension indices.
        /// The first upper bound in the list corresponds to the first dimension. Dimensions cannot be skipped.
        /// An unspecified upper bound means that instances of this type can have an arbitrary upper bound for that dimension.
        /// </summary>
        ImmutableArray<int> Sizes
        {
            get;
            // ^ ensures count(result) <= Rank;
        }
    }

    /// <summary>
    /// Modifies the set of allowed values for a type, or the semantics of operations allowed on those values. 
    /// Custom modifiers are not associated directly with types, but rather with typed storage locations for values.
    /// </summary>
    internal interface ICustomModifier
    {
        /// <summary>
        /// If true, a language may use the modified storage location without being aware of the meaning of the modification.
        /// </summary>
        bool IsOptional { get; }

        /// <summary>
        /// A type used as a tag that indicates which type of modification applies to the storage location.
        /// </summary>
        ITypeReference GetModifier(EmitContext context);
    }

    /// <summary>
    /// Information that describes a method or property parameter, but does not include all the information in a IParameterDefinition.
    /// </summary>
    internal interface IParameterTypeInformation : IParameterListEntry
    {
        /// <summary>
        /// The list of custom modifiers, if any, associated with the parameter type. 
        /// </summary>
        ImmutableArray<ICustomModifier> CustomModifiers
        {
            get;
        }

        /// <summary>
        /// The list of custom modifiers, if any, associated with the ref modifier. 
        /// </summary>
        ImmutableArray<ICustomModifier> RefCustomModifiers
        {
            get;
        }

        /// <summary>
        /// True if the parameter is passed by reference (using a managed pointer).
        /// </summary>
        bool IsByReference { get; }

        /// <summary>
        /// The type of argument value that corresponds to this parameter.
        /// </summary>
        ITypeReference GetType(EmitContext context);
    }

    /// <summary>
    /// The definition of a type parameter of a generic type or method.
    /// </summary>
    internal interface IGenericParameter : IDefinition, IGenericParameterReference
    {
        /// <summary>
        /// A list of classes or interfaces. All type arguments matching this parameter must be derived from all of the classes and implement all of the interfaces.
        /// </summary>
        IEnumerable<TypeReferenceWithAttributes> GetConstraints(EmitContext context);

        /// <summary>
        /// True if all type arguments matching this parameter are constrained to be reference types.
        /// </summary>
        bool MustBeReferenceType
        {
            get;
            // ^ ensures result ==> !this.MustBeValueType;
        }

        /// <summary>
        /// True if all type arguments matching this parameter are constrained to be value types.
        /// </summary>
        bool MustBeValueType
        {
            get;
            // ^ ensures result ==> !this.MustBeReferenceType;
        }

        bool AllowsRefLikeType
        {
            get;
        }

        /// <summary>
        /// True if all type arguments matching this parameter are constrained to be value types or concrete classes with visible default constructors.
        /// </summary>
        bool MustHaveDefaultConstructor { get; }

        /// <summary>
        /// Indicates if the generic type or method with this type parameter is co-, contra-, or non variant with respect to this type parameter.
        /// </summary>
        TypeParameterVariance Variance { get; }

        IGenericMethodParameter? AsGenericMethodParameter { get; }
        IGenericTypeParameter? AsGenericTypeParameter { get; }
    }

    /// <summary>
    /// A reference to the definition of a type parameter of a generic type or method.
    /// </summary>
    internal interface IGenericParameterReference : ITypeReference, INamedEntity, IParameterListEntry
    {
    }

    /// <summary>
    /// The definition of a type parameter of a generic method.
    /// </summary>
    internal interface IGenericMethodParameter : IGenericParameter, IGenericMethodParameterReference
    {
        /// <summary>
        /// The generic method that defines this type parameter.
        /// </summary>
        new IMethodDefinition DefiningMethod
        {
            get;
            // ^ ensures result.IsGeneric;
        }
    }

    /// <summary>
    /// A reference to a type parameter of a generic method.
    /// </summary>
    internal interface IGenericMethodParameterReference : IGenericParameterReference
    {
        /// <summary>
        /// A reference to the generic method that defines the referenced type parameter.
        /// </summary>
        IMethodReference DefiningMethod { get; }
    }

    /// <summary>
    /// A generic type instantiated with a list of type arguments
    /// </summary>
    internal interface IGenericTypeInstanceReference : ITypeReference
    {
        /// <summary>
        /// The type arguments that were used to instantiate this.GenericType in order to create this type.
        /// </summary>
        ImmutableArray<ITypeReference> GetGenericArguments(EmitContext context);
        // ^ ensures result.GetEnumerator().MoveNext(); // The collection is always non empty.

        /// <summary>
        /// Returns the generic type of which this type is an instance.
        /// Equivalent to Symbol.OriginalDefinition
        /// </summary>
        INamedTypeReference GetGenericType(EmitContext context);
        // ^ ensures result.ResolvedType.IsGeneric;
    }

    /// <summary>
    /// The definition of a type parameter of a generic type.
    /// </summary>
    internal interface IGenericTypeParameter : IGenericParameter, IGenericTypeParameterReference
    {
        /// <summary>
        /// The generic type that defines this type parameter.
        /// </summary>
        new ITypeDefinition DefiningType { get; }
    }

    /// <summary>
    /// A reference to a type parameter of a generic type.
    /// </summary>
    internal interface IGenericTypeParameterReference : IGenericParameterReference
    {
        /// <summary>
        /// A reference to the generic type that defines the referenced type parameter.
        /// </summary>
        ITypeReference DefiningType { get; }
    }

    /// <summary>
    /// A reference to a named type, such as an INamespaceTypeReference or an INestedTypeReference.
    /// </summary>
    internal interface INamedTypeReference : ITypeReference, INamedEntity
    {
        /// <summary>
        /// The number of generic parameters. Zero if the type is not generic.
        /// </summary>
        ushort GenericParameterCount { get; }

        /// <summary>
        /// If true, the persisted type name is mangled by appending "`n" where n is the number of type parameters, if the number of type parameters is greater than 0.
        /// </summary>
        bool MangleName { get; }

        /// <summary>Indicates that the type is scoped to the file it is declared in. Used as a prefix for the metadata name.</summary>
        string? AssociatedFileIdentifier { get; }
    }

    /// <summary>
    /// A named type definition, such as an INamespaceTypeDefinition or an INestedTypeDefinition.
    /// </summary>
    internal interface INamedTypeDefinition : ITypeDefinition, INamedTypeReference
    {
    }

    /// <summary>
    /// A type definition that is a member of a namespace definition.
    /// </summary>
    internal interface INamespaceTypeDefinition : INamedTypeDefinition, INamespaceTypeReference
    {
        /// <summary>
        /// True if the type can be accessed from other assemblies.
        /// </summary>
        bool IsPublic { get; }
    }

    /// <summary>
    /// Represents a namespace.
    /// </summary>
    internal interface INamespace : INamedEntity
    {
        /// <summary>
        /// Containing namespace or null if this namespace is global.
        /// </summary>
        INamespace ContainingNamespace { get; }

        /// <summary>
        /// Returns underlying internal symbol object, if any.
        /// </summary>
        INamespaceSymbolInternal GetInternalSymbol();
    }

    /// <summary>
    /// A reference to a type definition that is a member of a namespace definition.
    /// </summary>
    internal interface INamespaceTypeReference : INamedTypeReference
    {
        /// <summary>
        /// A reference to the unit that defines the referenced type.
        /// </summary>
        IUnitReference GetUnit(EmitContext context);

        /// <summary>
        /// Fully qualified name of the containing namespace.
        /// </summary>
        string NamespaceName { get; }
    }

    /// <summary>
    /// A type definition that is a member of another type definition.
    /// </summary>
    internal interface INestedTypeDefinition : INamedTypeDefinition, ITypeDefinitionMember, INestedTypeReference
    {
    }

    /// <summary>
    /// A type definition that is a member of another type definition.
    /// </summary>
    internal interface INestedTypeReference : INamedTypeReference, ITypeMemberReference
    {
        /// <summary>
        /// When true, the type inherits type parameters from the enclosing type.
        /// Note, <see cref="INamedTypeReference.GenericParameterCount"/> does not include count of the inherited type parameters.
        /// </summary>
        bool InheritsEnclosingTypeTypeParameters { get; }
    }

    /// <summary>
    /// A reference to a type definition that is a specialized nested type.
    /// </summary>
    internal interface ISpecializedNestedTypeReference : INestedTypeReference
    {
        /// <summary>
        /// A reference to the nested type that has been specialized to obtain this nested type reference. When the containing type is an instance of type which is itself a specialized member (i.e. it is a nested
        /// type of a generic type instance), then the unspecialized member refers to a member from the unspecialized containing type. (I.e. the unspecialized member always
        /// corresponds to a definition that is not obtained via specialization.)
        /// </summary>
        [return: NotNull]
        INestedTypeReference GetUnspecializedVersion(EmitContext context);
    }

    /// <summary>
    /// Models an explicit implementation or override of a base class virtual method or an explicit implementation of an interface method.
    /// </summary>
    internal readonly struct MethodImplementation
    {
        /// <summary>
        /// The type that is explicitly implementing or overriding the base class virtual method or explicitly implementing an interface method.
        /// </summary>
        public readonly Cci.IMethodDefinition ImplementingMethod;

        /// <summary>
        /// A reference to the method that provides the implementation.
        /// </summary>
        public readonly Cci.IMethodReference ImplementedMethod;

        public MethodImplementation(Cci.IMethodDefinition ImplementingMethod, Cci.IMethodReference ImplementedMethod)
        {
            this.ImplementingMethod = ImplementingMethod;
            this.ImplementedMethod = ImplementedMethod;
        }

        /// <summary>
        /// The type that is explicitly implementing or overriding the base class virtual method or explicitly implementing an interface method.
        /// </summary>
        public Cci.ITypeDefinition ContainingType
        {
            get { return ImplementingMethod.ContainingTypeDefinition; }
        }
    }

    /// <summary>
    /// A type reference that has custom modifiers associated with it. For example a reference to the target type of a managed pointer to a constant.
    /// </summary>
    internal interface IModifiedTypeReference : ITypeReference
    {
        /// <summary>
        /// Returns the list of custom modifiers associated with the type reference.
        /// </summary>
        ImmutableArray<ICustomModifier> CustomModifiers { get; }

        /// <summary>
        /// An unmodified type reference.
        /// </summary>
        ITypeReference UnmodifiedType { get; }
    }

    /// <summary>
    /// This interface models the metadata representation of a pointer to a location in unmanaged memory.
    /// </summary>
    internal interface IPointerTypeReference : ITypeReference
    {
        /// <summary>
        /// The type of value stored at the target memory location.
        /// </summary>
        ITypeReference GetTargetType(EmitContext context);
    }

    /// <summary>
    /// This interface models the metadata representation of a pointer to a function in unmanaged memory.
    /// </summary>
    internal interface IFunctionPointerTypeReference : ITypeReference
    {
        /// <summary>
        /// The signature of the function located at the target memory address.
        /// </summary>
        ISignature Signature { get; }
    }

    /// <summary>
    /// A type ref with attributes attached directly to the type reference
    /// itself. Unlike <see cref="IReference.GetAttributes(EmitContext)"/> a
    /// <see cref="TypeReferenceWithAttributes"/> will never provide attributes
    /// for the "pointed at" declaration, and all attributes will be emitted
    /// directly on the type ref, rather than the declaration.
    /// </summary>
    // TODO(https://github.com/dotnet/roslyn/issues/12677):
    // Consider: This is basically just a work-around for our overly loose
    // interpretation of IReference and IDefinition. This type would probably
    // be unnecessary if we added a GetAttributes method onto IDefinition and
    // properly segregated attributes that are on type references and attributes
    // that are on underlying type definitions.
    internal readonly struct TypeReferenceWithAttributes
    {
        /// <summary>
        /// The type reference.
        /// </summary>
        public ITypeReference TypeRef { get; }

        /// <summary>
        /// The attributes on the type reference itself.
        /// </summary>
        public ImmutableArray<ICustomAttribute> Attributes { get; }

        public TypeReferenceWithAttributes(
            ITypeReference typeRef,
            ImmutableArray<ICustomAttribute> attributes = default(ImmutableArray<ICustomAttribute>))
        {
            TypeRef = typeRef;
            Attributes = attributes.NullToEmpty();
        }
    }

    /// <summary>
    /// This interface models the metadata representation of a type.
    /// </summary>
    internal interface ITypeDefinition : IDefinition, ITypeReference
    {
        /// <summary>
        /// The byte alignment that values of the given type ought to have. Must be a power of 2. If zero, the alignment is decided at runtime.
        /// </summary>
        ushort Alignment { get; }

        /// <summary>
        /// Returns null for interfaces and System.Object.
        /// </summary>
        ITypeReference? GetBaseClass(EmitContext context);
        // ^ ensures result == null || result.ResolvedType.IsClass;

        /// <summary>
        /// Zero or more events defined by this type.
        /// </summary>
        IEnumerable<IEventDefinition> GetEvents(EmitContext context);

        /// <summary>
        /// Zero or more implementation overrides provided by the class.
        /// </summary>
        IEnumerable<MethodImplementation> GetExplicitImplementationOverrides(EmitContext context);

        /// <summary>
        /// Zero or more fields defined by this type.
        /// </summary>
        IEnumerable<IFieldDefinition> GetFields(EmitContext context);

        /// <summary>
        /// Zero or more parameters that can be used as type annotations.
        /// </summary>
        IEnumerable<IGenericTypeParameter> GenericParameters
        {
            get;
            // ^ requires this.IsGeneric;
        }

        /// <summary>
        /// The number of generic parameters. Zero if the type is not generic.
        /// </summary>
        ushort GenericParameterCount
        { // TODO: remove this
            get;
            // ^ ensures !this.IsGeneric ==> result == 0;
            // ^ ensures this.IsGeneric ==> result > 0;
        }

        /// <summary>
        /// True if this type has a non empty collection of SecurityAttributes or the System.Security.SuppressUnmanagedCodeSecurityAttribute.
        /// </summary>
        bool HasDeclarativeSecurity { get; }

        /// <summary>
        /// Zero or more interfaces implemented by this type.
        /// </summary>
        IEnumerable<TypeReferenceWithAttributes> Interfaces(EmitContext context);

        /// <summary>
        /// True if the type may not be instantiated.
        /// </summary>
        bool IsAbstract { get; }

        /// <summary>
        /// Is type initialized anytime before first access to static field
        /// </summary>
        bool IsBeforeFieldInit { get; }

        /// <summary>
        /// Is this imported from COM type library
        /// </summary>
        bool IsComObject { get; }

        /// <summary>
        /// True if this type is parameterized (this.GenericParameters is a non empty collection).
        /// </summary>
        bool IsGeneric { get; }

        /// <summary>
        /// True if the type is an interface.
        /// </summary>
        bool IsInterface { get; }

        /// <summary>
        /// True if the type is a delegate.
        /// </summary>
        bool IsDelegate { get; }

        /// <summary>
        /// True if this type gets special treatment from the runtime.
        /// </summary>
        bool IsRuntimeSpecial { get; }

        /// <summary>
        /// True if this type is serializable.
        /// </summary>
        bool IsSerializable { get; }

        /// <summary>
        /// True if the type has special name.
        /// </summary>
        bool IsSpecialName { get; }

        /// <summary>
        /// True if the type is a Windows runtime type.
        /// </summary>
        /// <remarks>
        /// A type can me marked as a Windows runtime type in source by applying the WindowsRuntimeImportAttribute.
        /// WindowsRuntimeImportAttribute is a pseudo custom attribute defined as an internal class in System.Runtime.InteropServices.WindowsRuntime namespace.
        /// This is needed to mark Windows runtime types which are redefined in mscorlib.dll and System.Runtime.WindowsRuntime.dll.
        /// These two assemblies are special as they implement the CLR's support for WinRT.
        /// </remarks>
        bool IsWindowsRuntimeImport { get; }

        /// <summary>
        /// True if the type may not be subtyped.
        /// </summary>
        bool IsSealed { get; }

        /// <summary>
        /// Layout of the type.
        /// </summary>
        LayoutKind Layout { get; }

        /// <summary>
        /// Zero or more methods defined by this type.
        /// </summary>
        IEnumerable<IMethodDefinition> GetMethods(EmitContext context);

        /// <summary>
        /// Zero or more nested types defined by this type.
        /// </summary>
        IEnumerable<INestedTypeDefinition> GetNestedTypes(EmitContext context);

        /// <summary>
        /// Zero or more properties defined by this type.
        /// </summary>
        IEnumerable<IPropertyDefinition> GetProperties(EmitContext context);

        /// <summary>
        /// Declarative security actions for this type. Will be empty if this.HasSecurity is false.
        /// </summary>
        IEnumerable<SecurityAttribute> SecurityAttributes { get; }

        /// <summary>
        /// Size of an object of this type. In bytes. If zero, the size is unspecified and will be determined at runtime.
        /// </summary>
        uint SizeOf { get; }

        /// <summary>
        /// Default marshalling of the Strings in this class.
        /// </summary>
        CharSet StringFormat { get; }
    }

    /// <summary>
    /// A reference to a type.
    /// </summary>
    internal interface ITypeReference : IReference
    {
        /// <summary>
        /// True if the type is an enumeration (it extends System.Enum and is sealed). Corresponds to C# enum.
        /// </summary>
        bool IsEnum { get; }

        /// <summary>
        /// True if the type is a value type. 
        /// Value types are sealed and extend System.ValueType or System.Enum.
        /// A type parameter for which MustBeValueType (the struct constraint in C#) is true also returns true for this property.
        /// </summary>
        bool IsValueType { get; }

        /// <summary>
        /// The type definition being referred to.
        /// </summary>
        ITypeDefinition? GetResolvedType(EmitContext context);

        /// <summary>
        /// Unless the value of TypeCode is PrimitiveTypeCode.NotPrimitive, the type corresponds to a "primitive" CLR type (such as System.Int32) and
        /// the type code identifies which of the primitive types it corresponds to.
        /// </summary>
        PrimitiveTypeCode TypeCode { get; }

        /// <summary>
        /// TypeDefs defined in modules linked to the assembly being emitted are listed in the ExportedTypes table.
        /// </summary>
        TypeDefinitionHandle TypeDef { get; }

        IGenericMethodParameterReference? AsGenericMethodParameterReference { get; }
        IGenericTypeInstanceReference? AsGenericTypeInstanceReference { get; }
        IGenericTypeParameterReference? AsGenericTypeParameterReference { get; }
        INamespaceTypeDefinition? AsNamespaceTypeDefinition(EmitContext context);
        INamespaceTypeReference? AsNamespaceTypeReference { get; }
        INestedTypeDefinition? AsNestedTypeDefinition(EmitContext context);
        INestedTypeReference? AsNestedTypeReference { get; }
        ISpecializedNestedTypeReference? AsSpecializedNestedTypeReference { get; }
        ITypeDefinition? AsTypeDefinition(EmitContext context);
    }

    /// <summary>
    /// A enumeration of all of the value types that are built into the Runtime (and thus have specialized IL instructions that manipulate them).
    /// </summary>
    internal enum PrimitiveTypeCode
    {
        /// <summary>
        /// A single bit.
        /// </summary>
        Boolean,

        /// <summary>
        /// An unsigned 16 bit integer representing a Unicode UTF16 code point.
        /// </summary>
        Char,

        /// <summary>
        /// A signed 8 bit integer.
        /// </summary>
        Int8,

        /// <summary>
        /// A 32 bit IEEE floating point number.
        /// </summary>
        Float32,

        /// <summary>
        /// A 64 bit IEEE floating point number.
        /// </summary>
        Float64,

        /// <summary>
        /// A signed 16 bit integer.
        /// </summary>
        Int16,

        /// <summary>
        /// A signed 32 bit integer.
        /// </summary>
        Int32,

        /// <summary>
        /// A signed 64 bit integer.
        /// </summary>
        Int64,

        /// <summary>
        /// A signed 32 bit integer or 64 bit integer, depending on the native word size of the underlying processor.
        /// </summary>
        IntPtr,

        /// <summary>
        /// A pointer to fixed or unmanaged memory.
        /// </summary>
        Pointer,

        /// <summary>
        /// A reference to managed memory.
        /// </summary>
        Reference,

        /// <summary>
        /// A string.
        /// </summary>
        String,

        /// <summary>
        /// An unsigned 8 bit integer.
        /// </summary>
        UInt8,

        /// <summary>
        /// An unsigned 16 bit integer.
        /// </summary>
        UInt16,

        /// <summary>
        /// An unsigned 32 bit integer.
        /// </summary>
        UInt32,

        /// <summary>
        /// An unsigned 64 bit integer.
        /// </summary>
        UInt64,

        /// <summary>
        /// An unsigned 32 bit integer or 64 bit integer, depending on the native word size of the underlying processor.
        /// </summary>
        UIntPtr,

        /// <summary>
        /// A type that denotes the absence of a value.
        /// </summary>
        Void,

        /// <summary>
        /// Not a primitive type.
        /// </summary>
        NotPrimitive,

        /// <summary>
        /// A pointer to a function in fixed or managed memory.
        /// </summary>
        FunctionPointer,

        /// <summary>
        /// Type is a dummy type.
        /// </summary>
        Invalid,
    }

    /// <summary>
    /// Enumerates the different kinds of levels of visibility a type member can have.
    /// </summary>
    internal enum TypeMemberVisibility
    {
        /// <summary>
        /// The member is visible only within its own type.
        /// </summary>
        Private = 1,

        /// <summary>
        /// The member is visible only within the intersection of its family (its own type and any subtypes) and assembly. 
        /// </summary>
        FamilyAndAssembly = 2,

        /// <summary>
        /// The member is visible only within its own assembly.
        /// </summary>
        Assembly = 3,

        /// <summary>
        /// The member is visible only within its own type and any subtypes.
        /// </summary>
        Family = 4,

        /// <summary>
        /// The member is visible only within the union of its family and assembly. 
        /// </summary>
        FamilyOrAssembly = 5,

        /// <summary>
        /// The member is visible everywhere its declaring type is visible.
        /// </summary>
        Public = 6
    }

    /// <summary>
    /// Enumerates the different kinds of variance a generic method or generic type parameter may have.
    /// </summary>
    internal enum TypeParameterVariance
    {
        /// <summary>
        /// Two type or method instances are compatible only if they have exactly the same type argument for this parameter.
        /// </summary>
        NonVariant = 0,

        /// <summary>
        /// A type or method instance will match another instance if it has a type for this parameter that is the same or a subtype of the type the
        /// other instance has for this parameter.
        /// </summary>
        Covariant = 1,

        /// <summary>
        /// A type or method instance will match another instance if it has a type for this parameter that is the same or a supertype of the type the
        /// other instance has for this parameter.
        /// </summary>
        Contravariant = 2,
    }
}
