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

using System.Collections;
using System.Resources;

namespace System.ComponentModel.Design.Serialization;

/// <summary>
///  This is the serialization provider for all code dom serializers.
/// </summary>
internal sealed class CodeDomSerializationProvider : IDesignerSerializationProvider
{
    /// <summary>
    ///  This will be called by the serialization manager when it
    ///  is trying to locate a serializer for an object type.
    ///  If this serialization provider can provide a serializer
    ///  that is of the correct type, it should return it.
    ///  Otherwise, it should return null.
    /// </summary>
    object? IDesignerSerializationProvider.GetSerializer(IDesignerSerializationManager manager, object? currentSerializer, Type? objectType, Type serializerType)
    {
        // Don't provide our serializer if someone else already had one
        if (currentSerializer is not null)
        {
            return null;
        }

        if (serializerType == typeof(CodeDomSerializer))
        {
            return GetCodeDomSerializer(objectType);
        }
        else if (serializerType == typeof(MemberCodeDomSerializer))
        {
            return GetMemberCodeDomSerializer(objectType);
        }
        else if (serializerType == typeof(TypeCodeDomSerializer))
        {
            return GetTypeCodeDomSerializer(objectType);
        }

        return null; // don't understand this type of serializer.
    }

    /// <summary>
    ///  Returns a code dom serializer
    /// </summary>
    private static object? GetCodeDomSerializer(Type? objectType)
    {
        // Null is a valid value that can be passed into GetSerializer. It indicates
        // that the value we need to serialize is null, in which case we handle it
        // through the PrimitiveCodeDomSerializer.
        //
        if (objectType is null)
        {
            return PrimitiveCodeDomSerializer.Default;
        }

        // Support for components.
        if (typeof(IComponent).IsAssignableFrom(objectType))
        {
            return ComponentCodeDomSerializer.Default;
        }

        // We special case enums. They do have instance descriptors, but we want
        // better looking code than the instance descriptor can provide for flags,
        // so we do it ourselves.
        if (typeof(Enum).IsAssignableFrom(objectType))
        {
            return EnumCodeDomSerializer.Default;
        }

        // We will provide a serializer for any intrinsic types.
        if (objectType.IsPrimitive || objectType.IsEnum || objectType == typeof(string))
        {
            return PrimitiveCodeDomSerializer.Default;
        }

        // And one for collections.
        if (typeof(ICollection).IsAssignableFrom(objectType))
        {
            return CollectionCodeDomSerializer.Default;
        }

        // And one for IContainer
        if (typeof(IContainer).IsAssignableFrom(objectType))
        {
            return ContainerCodeDomSerializer.Default;
        }

        // And one for resources
        if (typeof(ResourceManager).IsAssignableFrom(objectType))
        {
            return ResourceCodeDomSerializer.Default;
        }

        // The default serializer can do any object including those with instance descriptors.
        return CodeDomSerializer.Default;
    }

    /// <summary>
    ///  Returns a code dom serializer for members
    /// </summary>
    private static object? GetMemberCodeDomSerializer(Type? objectType)
    {
        if (typeof(PropertyDescriptor).IsAssignableFrom(objectType))
        {
            return PropertyMemberCodeDomSerializer.Default;
        }

        if (typeof(EventDescriptor).IsAssignableFrom(objectType))
        {
            return EventMemberCodeDomSerializer.Default;
        }

        return null;
    }

    /// <summary>
    ///  Returns a code dom serializer for types
    /// </summary>
    private static object? GetTypeCodeDomSerializer(Type? objectType)
    {
        if (typeof(IComponent).IsAssignableFrom(objectType))
        {
            return ComponentTypeCodeDomSerializer.Default;
        }

        return TypeCodeDomSerializer.Default;
    }
}
