DefaultGenericHandler
using Castle.Core;
using Castle.Core.Internal;
using Castle.DynamicProxy;
using Castle.MicroKernel.ComponentActivator;
using Castle.MicroKernel.Context;
using Castle.MicroKernel.ModelBuilder;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
namespace Castle.MicroKernel.Handlers
{
    [Serializable]
    public class DefaultGenericHandler : AbstractHandler
    {
        private readonly IGenericImplementationMatchingStrategy implementationMatchingStrategy;
        private readonly IGenericServiceStrategy serviceStrategy;
        private readonly SimpleThreadSafeDictionary<Type, IHandler> type2SubHandler = new SimpleThreadSafeDictionary<Type, IHandler>();
        public IGenericImplementationMatchingStrategy ImplementationMatchingStrategy => implementationMatchingStrategy;
        public IGenericServiceStrategy ServiceStrategy => serviceStrategy;
        public DefaultGenericHandler(ComponentModel model, IGenericImplementationMatchingStrategy implementationMatchingStrategy, IGenericServiceStrategy serviceStrategy)
            : base(model)
        {
            this.implementationMatchingStrategy = implementationMatchingStrategy;
            this.serviceStrategy = serviceStrategy;
        }
        public override void Dispose()
        {
            IHandler[] array = type2SubHandler.EjectAllValues();
            for (int i = 0; i < array.Length; i++) {
                (array[i] as IDisposable)?.Dispose();
            }
        }
        public override bool ReleaseCore(Burden burden)
        {
            Type unproxiedType = ProxyUtil.GetUnproxiedType(burden.Instance);
            return type2SubHandler.GetOrThrow(unproxiedType).Release(burden);
        }
        public override bool Supports(Type service)
        {
            if (base.Supports(service))
                return true;
            if (type2SubHandler.Contains(service))
                return true;
            if (service.GetTypeInfo().get_IsGenericType() && !service.GetTypeInfo().get_IsGenericTypeDefinition()) {
                Type genericTypeDefinition = service.GetGenericTypeDefinition();
                if (!base.Supports(genericTypeDefinition))
                    return false;
                if (serviceStrategy != null)
                    return serviceStrategy.Supports(service, base.ComponentModel);
                return true;
            }
            return false;
        }
        public override bool SupportsAssignable(Type service)
        {
            if (base.SupportsAssignable(service))
                return true;
            if (!service.GetTypeInfo().get_IsGenericType() || service.GetTypeInfo().get_IsGenericTypeDefinition())
                return false;
            Type[] serviceArguments = TypeExtensions.GetGenericArguments(service);
            return base.ComponentModel.Services.Any((Type s) => SupportsAssignable(service, s, serviceArguments));
        }
        protected virtual Type[] AdaptServices(Type closedImplementationType, Type requestedType)
        {
            Type[] array = base.ComponentModel.Services.ToArray();
            if (array.Length == 1 && requestedType.GetTypeInfo().get_IsGenericType() && (object)array[0] == requestedType.GetGenericTypeDefinition())
                return new Type[1] {
                    requestedType
                };
            List<Type> list = new List<Type>(array.Length);
            int num = AdaptClassServices(closedImplementationType, list, array);
            if (num == array.Length - 1 && list.Count > 0)
                return list.ToArray();
            AdaptInterfaceServices(closedImplementationType, list, array, num);
            if (list.Count == 0)
                return new Type[1] {
                    requestedType
                };
            return list.ToArray();
        }
        protected virtual IHandler BuildSubHandler(Type closedImplementationType, Type requestedType)
        {
            ComponentModel componentModel = base.Kernel.ComponentModelBuilder.BuildModel(base.ComponentModel.ComponentName, AdaptServices(closedImplementationType, requestedType), closedImplementationType, GetExtendedProperties());
            CloneParentProperties(componentModel);
            return base.Kernel.CreateHandler(componentModel);
        }
        protected IHandler GetSubHandler(Type genericType, Type requestedType)
        {
            bool added = false;
            IHandler orAdd = type2SubHandler.GetOrAdd(genericType, delegate(Type t) {
                added = true;
                return BuildSubHandler(t, requestedType);
            });
            if (added)
                base.Kernel.RaiseEventsOnHandlerCreated(orAdd);
            return orAdd;
        }
        protected override void InitDependencies()
        {
            IDependencyAwareActivator dependencyAwareActivator = base.Kernel.CreateComponentActivator(base.ComponentModel) as IDependencyAwareActivator;
            if (dependencyAwareActivator != null && dependencyAwareActivator.CanProvideRequiredDependencies(base.ComponentModel)) {
                foreach (DependencyModel dependency in base.ComponentModel.Dependencies) {
                    dependency.Init(base.ComponentModel.ParametersInternal);
                }
            } else
                base.InitDependencies();
        }
        protected override object Resolve(CreationContext context, bool instanceRequired)
        {
            Type closedImplementationType = GetClosedImplementationType(context, instanceRequired);
            if ((object)closedImplementationType == null)
                return null;
            IHandler subHandler = GetSubHandler(closedImplementationType, context.RequestedType);
            using (context.EnterResolutionContext(this, false, false))
                try {
                    return subHandler.Resolve(context);
                } catch (GenericHandlerTypeMismatchException innerException) {
                    throw new HandlerException($"""{base.ComponentModel.Name}""", base.ComponentModel.ComponentName, innerException);
                }
        }
        protected bool SupportsAssignable(Type service, Type modelService, Type[] serviceArguments)
        {
            if (!modelService.GetTypeInfo().get_IsGenericTypeDefinition() || TypeExtensions.GetGenericArguments(modelService).Length != serviceArguments.Length)
                return false;
            Type type = modelService.TryMakeGenericType(serviceArguments);
            if ((object)type == null)
                return false;
            if (!TypeExtensions.IsAssignableFrom(service, type))
                return false;
            if (ServiceStrategy != null && !ServiceStrategy.Supports(type, base.ComponentModel))
                return false;
            return true;
        }
        private void CloneParentProperties(ComponentModel newModel)
        {
            newModel.LifestyleType = base.ComponentModel.LifestyleType;
            foreach (InterceptorReference interceptor in base.ComponentModel.Interceptors) {
                newModel.Interceptors.AddIfNotInCollection(interceptor);
            }
            if (base.ComponentModel.HasCustomDependencies) {
                Arguments customDependencies = newModel.CustomDependencies;
                foreach (KeyValuePair<object, object> customDependency in base.ComponentModel.CustomDependencies) {
                    customDependencies.Add(customDependency.Key, customDependency.Value);
                }
            }
            ICollection<IMetaComponentModelDescriptor> metaDescriptors = base.ComponentModel.GetMetaDescriptors(false);
            if (metaDescriptors != null) {
                foreach (IMetaComponentModelDescriptor item in metaDescriptors) {
                    item.ConfigureComponentModel(base.Kernel, newModel);
                }
            }
        }
        public IHandler ConvertToClosedGenericHandler(Type service, CreationContext openGenericContext)
        {
            Type closedImplementationType = GetClosedImplementationType(openGenericContext, false);
            return GetSubHandler(closedImplementationType, service);
        }
        private Type GetClosedImplementationType(CreationContext context, bool instanceRequired)
        {
            if ((object)base.ComponentModel.Implementation != typeof(LateBoundComponent)) {
                Type[] genericArguments = GetGenericArguments(context);
                try {
                    return base.ComponentModel.Implementation.MakeGenericType(genericArguments);
                } catch (ArgumentNullException) {
                    if (implementationMatchingStrategy == null)
                        throw;
                    throw new HandlerException(string.Format("Custom {0} ({1}) didn't select any generic parameters for implementation type of component '{2}'. This usually signifies bug in the {0}.", typeof(IGenericImplementationMatchingStrategy).get_Name(), implementationMatchingStrategy, base.ComponentModel.Name), base.ComponentModel.ComponentName);
                } catch (ArgumentException innerException) {
                    if (instanceRequired) {
                        Type[] genericArguments2 = TypeExtensions.GetGenericArguments(base.ComponentModel.Implementation);
                        if (genericArguments2.Length > genericArguments.Length) {
                            string str = $"""{context.RequestedType}""{context.GenericArguments.Length}""{base.ComponentModel.Implementation}""{genericArguments2.Length}""{Environment.NewLine}""";
                            str = ((implementationMatchingStrategy != null) ? (str + string.Format("{0}This is most likely a bug in the {1} implementation this component uses ({2}).{0}Please consult the documentation for examples of how to implement it properly.", Environment.NewLine, typeof(IGenericImplementationMatchingStrategy).get_Name(), implementationMatchingStrategy)) : (str + string.Format("{0}You can instruct Windsor which types it should use to close this generic component by supplying an implementation of {1}.{0}Please consult the documentation for examples of how to do that.", Environment.NewLine, typeof(IGenericImplementationMatchingStrategy).get_Name())));
                            throw new HandlerException(str, base.ComponentModel.ComponentName, innerException);
                        }
                        string[] array = (from t in genericArguments.Where(delegate(Type a) {
                            if (!a.IsPointer && !a.IsByRef)
                                return (object)a == typeof(void);
                            return true;
                        })
                        select t.FullName).ToArray();
                        if (array.Length != 0) {
                            string str = string.Format("The following types provided as generic parameters are not legal: {0}. This is most likely a bug in your code.", string.Join(", ", array));
                            throw new HandlerException(str, base.ComponentModel.ComponentName, innerException);
                        }
                        throw new GenericHandlerTypeMismatchException(genericArguments, base.ComponentModel, this);
                    }
                    return null;
                }
            }
            return context.RequestedType;
        }
        private Arguments GetExtendedProperties()
        {
            Arguments arguments = base.ComponentModel.ExtendedProperties;
            if (arguments != null && arguments.Count > 0)
                arguments = new Arguments(arguments);
            return arguments;
        }
        private Type[] GetGenericArguments(CreationContext context)
        {
            if (implementationMatchingStrategy == null)
                return context.GenericArguments;
            return implementationMatchingStrategy.GetGenericArguments(base.ComponentModel, context) ?? context.GenericArguments;
        }
        private static int AdaptClassServices(Type closedImplementationType, List<Type> closedServices, Type[] openServices)
        {
            int i = 0;
            IDictionary<Type, Type> genericDefinitionToClass = null;
            for (; i < openServices.Length && openServices[i].GetTypeInfo().get_IsClass(); i++) {
                Type type = openServices[i];
                if (type.GetTypeInfo().get_IsGenericTypeDefinition()) {
                    EnsureClassMappingInitialized(closedImplementationType, ref genericDefinitionToClass);
                    if (genericDefinitionToClass.TryGetValue(type, out Type value))
                        closedServices.Add(value);
                } else
                    closedServices.Add(type);
            }
            return i;
        }
        private static void AdaptInterfaceServices(Type closedImplementationType, List<Type> closedServices, Type[] openServices, int index)
        {
            IDictionary<Type, Type> genericDefinitionToInterface = null;
            while (index < openServices.Length) {
                Type type = openServices[index];
                if (type.GetTypeInfo().get_IsGenericTypeDefinition()) {
                    EnsureInterfaceMappingInitialized(closedImplementationType, ref genericDefinitionToInterface);
                    if (genericDefinitionToInterface.TryGetValue(type, out Type value))
                        closedServices.Add(value);
                } else
                    closedServices.Add(type);
                index++;
            }
        }
        private static void EnsureClassMappingInitialized(Type closedImplementationType, ref IDictionary<Type, Type> genericDefinitionToClass)
        {
            if (genericDefinitionToClass == null) {
                genericDefinitionToClass = new Dictionary<Type, Type>();
                Type type = closedImplementationType;
                while ((object)type != typeof(object)) {
                    if (type.GetTypeInfo().get_IsGenericType())
                        genericDefinitionToClass.Add(type.GetGenericTypeDefinition(), type);
                    type = type.GetTypeInfo().get_BaseType();
                }
            }
        }
        private static void EnsureInterfaceMappingInitialized(Type closedImplementationType, ref IDictionary<Type, Type> genericDefinitionToInterface)
        {
            if (genericDefinitionToInterface == null)
                genericDefinitionToInterface = (from i in TypeExtensions.GetInterfaces(closedImplementationType)
                where i.GetTypeInfo().get_IsGenericType()
                select i).ToDictionary((Type i) => i.GetGenericTypeDefinition());
        }
    }
}