DefaultNamingSubSystem
using Castle.Core.Internal;
using Castle.MicroKernel.Util;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
namespace Castle.MicroKernel.SubSystems.Naming
{
    [Serializable]
    public class DefaultNamingSubSystem : AbstractSubSystem, INamingSubSystem, ISubSystem
    {
        protected struct HandlerWithPriority
        {
            private readonly IHandler handler;
            private readonly int priority;
            public IHandler Handler => handler;
            public HandlerWithPriority(int priority, IHandler handler)
            {
                this.priority = priority;
                this.handler = handler;
            }
            public bool Triumphs(HandlerWithPriority other)
            {
                if (priority > other.priority)
                    return true;
                if (priority == other.priority && priority > 0)
                    return true;
                return false;
            }
        }
        protected readonly Lock lock = Lock.Create();
        protected readonly Dictionary<string, IHandler> name2Handler = new Dictionary<string, IHandler>(StringComparer.OrdinalIgnoreCase);
        protected readonly Dictionary<Type, HandlerWithPriority> service2Handler = new Dictionary<Type, HandlerWithPriority>(SimpleTypeEqualityComparer.Instance);
        protected IList<IHandlersFilter> filters;
        protected IList<IHandlerSelector> selectors;
        private readonly IDictionary<Type, IHandler[]> assignableHandlerListsByTypeCache = new Dictionary<Type, IHandler[]>(SimpleTypeEqualityComparer.Instance);
        protected readonly IDictionary<Type, IHandler[]> handlerListsByTypeCache = new Dictionary<Type, IHandler[]>(SimpleTypeEqualityComparer.Instance);
        private Dictionary<string, IHandler> handlerByNameCache;
        private Dictionary<Type, IHandler> handlerByServiceCache;
        public virtual int ComponentCount => HandlerByNameCache.Count;
        protected IDictionary<string, IHandler> HandlerByNameCache {
            get {
                Dictionary<string, IHandler> dictionary = handlerByNameCache;
                if (dictionary == null) {
                    ILockHolder val = lock.ForWriting();
                    try {
                        return handlerByNameCache = new Dictionary<string, IHandler>(name2Handler, name2Handler.Comparer);
                    } finally {
                        ((IDisposable)val)?.Dispose();
                    }
                }
                return dictionary;
            }
        }
        protected IDictionary<Type, IHandler> HandlerByServiceCache {
            get {
                Dictionary<Type, IHandler> dictionary = handlerByServiceCache;
                if (dictionary == null) {
                    ILockHolder val = lock.ForWriting();
                    try {
                        dictionary = new Dictionary<Type, IHandler>(service2Handler.Count, service2Handler.Comparer);
                        foreach (KeyValuePair<Type, HandlerWithPriority> item in service2Handler) {
                            dictionary.Add(item.Key, item.Value.Handler);
                        }
                        handlerByServiceCache = dictionary;
                        return dictionary;
                    } finally {
                        ((IDisposable)val)?.Dispose();
                    }
                }
                return dictionary;
            }
        }
        public void AddHandlerSelector(IHandlerSelector selector)
        {
            if (selectors == null)
                selectors = new List<IHandlerSelector>();
            selectors.Add(selector);
        }
        public void AddHandlersFilter(IHandlersFilter filter)
        {
            if (filters == null)
                filters = new List<IHandlersFilter>();
            filters.Add(filter);
        }
        public virtual bool Contains(string name)
        {
            return HandlerByNameCache.ContainsKey(name);
        }
        public virtual bool Contains(Type service)
        {
            return GetHandler(service) != null;
        }
        public virtual IHandler[] GetAllHandlers()
        {
            IDictionary<string, IHandler> dictionary = HandlerByNameCache;
            IHandler[] array = new IHandler[dictionary.Values.Count];
            dictionary.Values.CopyTo(array, 0);
            return array;
        }
        public virtual IHandler[] GetAssignableHandlers(Type service)
        {
            if ((object)service == null)
                throw new ArgumentNullException("service");
            if ((object)service == typeof(object))
                return GetAllHandlers();
            return GetAssignableHandlersNoFiltering(service);
        }
        public virtual IHandler GetHandler(string name)
        {
            if (name == null)
                throw new ArgumentNullException("name");
            if (selectors != null) {
                IHandler selectorsOpinion = GetSelectorsOpinion(name, null);
                if (selectorsOpinion != null)
                    return selectorsOpinion;
            }
            HandlerByNameCache.TryGetValue(name, out IHandler value);
            return value;
        }
        public virtual IHandler GetHandler(Type service)
        {
            if ((object)service == null)
                throw new ArgumentNullException("service");
            if (selectors != null) {
                IHandler selectorsOpinion = GetSelectorsOpinion(null, service);
                if (selectorsOpinion != null)
                    return selectorsOpinion;
            }
            if (HandlerByServiceCache.TryGetValue(service, out IHandler value))
                return value;
            if (service.GetTypeInfo().get_IsGenericType() && !service.GetTypeInfo().get_IsGenericTypeDefinition()) {
                Type genericTypeDefinition = service.GetGenericTypeDefinition();
                if (HandlerByServiceCache.TryGetValue(genericTypeDefinition, out value) && value.Supports(service))
                    return value;
                IHandler[] handlers = GetHandlers(genericTypeDefinition);
                foreach (IHandler handler in handlers) {
                    if (handler.Supports(service))
                        return handler;
                }
            }
            return null;
        }
        public virtual IHandler[] GetHandlers(Type service)
        {
            if ((object)service != null) {
                if (filters != null) {
                    IHandler[] filtersOpinion = GetFiltersOpinion(service);
                    if (filtersOpinion != null)
                        return filtersOpinion;
                }
                IUpgradeableLockHolder val = lock.ForReadingUpgradeable();
                try {
                    if (!handlerListsByTypeCache.TryGetValue(service, out IHandler[] value)) {
                        value = GetHandlersNoLock(service);
                        val.Upgrade();
                        handlerListsByTypeCache[service] = value;
                        return value;
                    }
                    return value;
                } finally {
                    ((IDisposable)val)?.Dispose();
                }
            }
            throw new ArgumentNullException("service");
        }
        public virtual void Register(IHandler handler)
        {
            string name = handler.ComponentModel.Name;
            ILockHolder val = lock.ForWriting();
            try {
                try {
                    name2Handler.Add(name, handler);
                } catch (ArgumentException) {
                    throw new ComponentRegistrationException($"""{name}""");
                }
                Func<Type, HandlerWithPriority> serviceSelector = GetServiceSelector(handler);
                foreach (Type service in handler.ComponentModel.Services) {
                    HandlerWithPriority value = serviceSelector(service);
                    if (!service2Handler.TryGetValue(service, out HandlerWithPriority value2) || value.Triumphs(value2))
                        service2Handler[service] = value;
                }
                InvalidateCache();
            } finally {
                ((IDisposable)val)?.Dispose();
            }
        }
        protected IHandler[] GetAssignableHandlersNoFiltering(Type service)
        {
            IUpgradeableLockHolder val = lock.ForReadingUpgradeable();
            try {
                if (!assignableHandlerListsByTypeCache.TryGetValue(service, out IHandler[] value)) {
                    val.Upgrade();
                    if (!assignableHandlerListsByTypeCache.TryGetValue(service, out value)) {
                        value = (from h in name2Handler.Values
                        where h.SupportsAssignable(service)
                        select h).ToArray();
                        assignableHandlerListsByTypeCache[service] = value;
                        return value;
                    }
                    return value;
                }
                return value;
            } finally {
                ((IDisposable)val)?.Dispose();
            }
        }
        protected virtual IHandler[] GetFiltersOpinion(Type service)
        {
            if (filters == null)
                return null;
            IHandler[] array = null;
            foreach (IHandlersFilter filter in filters) {
                if (filter.HasOpinionAbout(service)) {
                    if (array == null)
                        array = GetAssignableHandlersNoFiltering(service);
                    array = filter.SelectHandlers(service, array);
                    if (array != null)
                        return array;
                }
            }
            return null;
        }
        protected virtual IHandler GetSelectorsOpinion(string name, Type type)
        {
            if (selectors == null)
                return null;
            type = (type ?? typeof(object));
            IHandler[] array = null;
            foreach (IHandlerSelector selector in selectors) {
                if (selector.HasOpinionAbout(name, type)) {
                    if (array == null)
                        array = GetAssignableHandlersNoFiltering(type);
                    IHandler handler = selector.SelectHandler(name, type, array);
                    if (handler != null)
                        return handler;
                }
            }
            return null;
        }
        private void InvalidateCache()
        {
            handlerListsByTypeCache.Clear();
            assignableHandlerListsByTypeCache.Clear();
            handlerByNameCache = null;
            handlerByServiceCache = null;
        }
        private IHandler[] GetHandlersNoLock(Type service)
        {
            SegmentedList<IHandler> segmentedList = new SegmentedList<IHandler>(3);
            foreach (IHandler value in name2Handler.Values) {
                if (value.Supports(service)) {
                    if (IsDefault(value, service))
                        segmentedList.AddFirst(0, value);
                    else if (IsFallback(value, service)) {
                        segmentedList.AddLast(2, value);
                    } else {
                        segmentedList.AddLast(1, value);
                    }
                }
            }
            return segmentedList.ToArray();
        }
        private Func<Type, HandlerWithPriority> GetServiceSelector(IHandler handler)
        {
            Predicate<Type> defaultsFilter = handler.ComponentModel.GetDefaultComponentForServiceFilter();
            Predicate<Type> fallbackFilter = handler.ComponentModel.GetFallbackComponentForServiceFilter();
            if (defaultsFilter == null) {
                if (fallbackFilter == null)
                    return (Type service) => new HandlerWithPriority(0, handler);
                return (Type service) => new HandlerWithPriority(fallbackFilter(service) ? (-1) : 0, handler);
            }
            if (fallbackFilter == null)
                return (Type service) => new HandlerWithPriority(defaultsFilter(service) ? 1 : 0, handler);
            return (Type service) => new HandlerWithPriority(defaultsFilter(service) ? 1 : (fallbackFilter(service) ? (-1) : 0), handler);
        }
        private bool IsDefault(IHandler handler, Type service)
        {
            return handler.ComponentModel.GetDefaultComponentForServiceFilter()?.Invoke(service) ?? false;
        }
        private bool IsFallback(IHandler handler, Type service)
        {
            return handler.ComponentModel.GetFallbackComponentForServiceFilter()?.Invoke(service) ?? false;
        }
    }
}