// 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.Reflection;
using System.Resources;

namespace System.ComponentModel.Design.Serialization;

public sealed partial class CodeDomComponentSerializationService
{
    private sealed partial class CodeDomSerializationStore
    {
        /// <summary>
        ///  This is a serialization manager that can load assemblies and search for types and provide a resource manager from our serialization store.
        /// </summary>
        private class LocalDesignerSerializationManager : DesignerSerializationManager
        {
            private readonly CodeDomSerializationStore _store;
            private bool? _typeSvcAvailable;

            /// <summary>
            ///  Creates a new serialization manager.
            /// </summary>
            internal LocalDesignerSerializationManager(CodeDomSerializationStore store, IServiceProvider provider) : base(provider)
            {
                _store = store;
            }

            /// <summary>
            ///  We override CreateInstance here to provide a hook to our resource manager.
            /// </summary>
            protected override object CreateInstance(Type type, ICollection? arguments, string? name, bool addToContainer)
            {
                if (typeof(ResourceManager).IsAssignableFrom(type))
                {
                    return _store.Resources;
                }

                return base.CreateInstance(type, arguments, name, addToContainer);
            }

            private bool TypeResolutionAvailable => _typeSvcAvailable ??= GetService(typeof(ITypeResolutionService)) is not null;

            /// <summary>
            ///  Override of GetType.  We favor the base implementation first, which uses the type resolution service if it is available.  If that fails, we will try to load assemblies from the given array of assembly names.
            /// </summary>
            protected override Type? GetType(string? name)
            {
                Type? t = base.GetType(name);
                if (t is not null || TypeResolutionAvailable)
                {
                    return t;
                }

                AssemblyName[] names = _store.AssemblyNames!;
                // First try the assembly names directly.
                foreach (AssemblyName n in names)
                {
                    Assembly a = Assembly.Load(n);
                    t = a?.GetType(name!);
                    if (t is not null)
                    {
                        return t;
                    }
                }

                // Failing that go after their dependencies.
                foreach (AssemblyName n in names)
                {
                    Assembly a = Assembly.Load(n);
                    if (a is not null)
                    {
                        foreach (AssemblyName dep in a.GetReferencedAssemblies())
                        {
                            Assembly aDep = Assembly.Load(dep);
                            t = aDep?.GetType(name!);
                            if (t is not null)
                            {
                                return t;
                            }
                        }
                    }
                }

                return t;
            }
        }
    }
}
