// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Collections.Generic;
using System.Reflection.Runtime.TypeInfos;

namespace System.Reflection.Runtime.BindingFlagSupport
{
    //==========================================================================================================================
    // Policies for nested types.
    //
    // Nested types enumerate a little differently than other members:
    //
    //    Base classes are never searched, regardless of BindingFlags.DeclaredOnly value.
    //
    //    Public|NonPublic|IgnoreCase are the only relevant BindingFlags. The apis ignore any other bits.
    //
    //    There is no such thing as a "static" or "instanced" nested type. For enumeration purposes,
    //    we'll arbitrarily denote all nested types as "static."
    //
    //==========================================================================================================================
    internal sealed class NestedTypePolicies : MemberPolicies<Type>
    {
        public static readonly NestedTypePolicies Instance = new NestedTypePolicies();

        public NestedTypePolicies() : base(MemberTypeIndex.NestedType) { }

        [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2070:UnrecognizedReflectionPattern",
            Justification = "Reflection implementation")]
        public sealed override IEnumerable<Type> GetDeclaredMembers(Type type)
        {
            return type.GetNestedTypes(DeclaredOnlyLookup);
        }

        public sealed override IEnumerable<Type> CoreGetDeclaredMembers(RuntimeTypeInfo type, NameFilter? optionalNameFilter, RuntimeTypeInfo reflectedType)
        {
            Debug.Assert(reflectedType.Equals(type));  // NestedType queries are always performed as if BindingFlags.DeclaredOnly are set so the reflectedType should always be the declaring type.
            return type.CoreGetDeclaredNestedTypes(optionalNameFilter);
        }

        public sealed override bool AlwaysTreatAsDeclaredOnly => true;

        public sealed override void GetMemberAttributes(Type member, out MethodAttributes visibility, out bool isStatic, out bool isVirtual, out bool isNewSlot)
        {
            isStatic = true;
            isVirtual = false;
            isNewSlot = false;

            // Since we never search base types for nested types, we don't need to map every visibility value one to one.
            // We just need to distinguish between "public" and "everything else."
            visibility = member.IsNestedPublic ? MethodAttributes.Public : MethodAttributes.Private;
        }

        public sealed override bool ImplicitlyOverrides(Type baseMember, Type derivedMember) => false;

        public sealed override bool IsSuppressedByMoreDerivedMember(Type member, Type[] priorMembers, int startIndex, int endIndex)
        {
            return false;
        }

        public sealed override BindingFlags ModifyBindingFlags(BindingFlags bindingFlags)
        {
            bindingFlags &= BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.IgnoreCase;
            bindingFlags |= BindingFlags.Static | BindingFlags.DeclaredOnly;
            return bindingFlags;
        }

        public sealed override bool OkToIgnoreAmbiguity(Type m1, Type m2)
        {
            return false;
        }
    }
}
