/// <summary>
        /// Constructor.  Creates a new TypeRegistrationProvider instance.
        /// </summary>
        /// <param name="container">SimpleInjector Container instance</param>
        /// <param name="options">Registration options to use to auto-register types.</param>
        public TypeRegistrationProvider(Container container, IAutoRegistrationOptions options)
        {
            this.container = container;
            this.options = options;

            this.container.Options.AllowOverridingRegistrations = true;
        }
        /// <summary>
        /// Discovers any Func dependencies of the specified type and attempts to register them.
        /// </summary>
        /// <param name="concreteType">Concrete type for which the dependencies should be discovered
        /// and registered</param>
        /// <param name="container">SimpleInjector container in which the dependencies should be registered.</param>
        /// <param name="options">Options to apply to the discovery and registration process.</param>
        public void RegisterDependencies(Type concreteType, Container container, IAutoRegistrationOptions options)
        {
            foreach (var ctor in concreteType.GetConstructors().Where((x) => x.IsPublic))
            {
                foreach (var param in ctor.GetParameters().Where((x) => x.ParameterType.Name.StartsWith("Func`")))
                {
                    var funcParamType = param.ParameterType;
                    var genericArgType = funcParamType.GenericTypeArguments.FirstOrDefault();

                    if (genericArgType != null 
                     && genericArgType.IsInterface 
                     && options.AutoRegistrationEnabledProvider.IsAutoRegistrationEnabled(genericArgType))
                    {
                        //Since Funcs<> do not retain any state and the wrapped Func<> accounts for the
                        //lifestyle of the object, it can always be made singleton, while the wrapped
                        //type may be transient.
                        container.RegisterSingle(funcParamType, () =>
                        {
                            //Create a Func<> with the container in context to create an instance of the type
                            var method = typeof(ContextualFuncCreationHelper).GetMethod("GetFunc").MakeGenericMethod(genericArgType);
                            var funcInstance = new ContextualFuncCreationHelper(container);

                            return Delegate.CreateDelegate(funcParamType, funcInstance, method);
                        });
                    }
                }
            }
        }
        /// <summary>
        /// Discovers any Lazy dependencies of the specified type and attempts to register them.
        /// </summary>
        /// <param name="concreteType">Concrete type for which the dependencies should be discovered
        /// and registered</param>
        /// <param name="container">SimpleInjector container in which the dependencies should be registered.</param>
        /// <param name="options">Options to apply to the discovery and registration process.</param>
        public void RegisterDependencies(Type concreteType, Container container, IAutoRegistrationOptions options)
        {
            foreach (var ctor in concreteType.GetConstructors().Where((x) => x.IsPublic))
            {
                foreach (var param in ctor.GetParameters().Where((x) => x.ParameterType.Name.StartsWith("Lazy`")))
                {
                    var funcParamType = param.ParameterType;
                    var genericArgType = funcParamType.GenericTypeArguments.FirstOrDefault();

                    if (genericArgType != null 
                     && genericArgType.IsInterface 
                     && options.AutoRegistrationEnabledProvider.IsAutoRegistrationEnabled(genericArgType))
                    {
                        Lifestyle lifestyle = options.LifestyleResolver.GetLifestyle(genericArgType);

                        if (lifestyle == null)
                        {
                            throw new Exception(string.Format("Lifestyle not defined for wrapped type of Lazy<{0}>", genericArgType.Name));
                        }

                        Func<object> creationDelegate = () =>
                        {
                            //First create a Func<> with the container in context to create an instance of the type
                            var method = typeof(ContextualFuncCreationHelper).GetMethod("GetFunc").MakeGenericMethod(genericArgType);
                            var funcType = typeof(Func<>).MakeGenericType(genericArgType);
                            var funcInstance = new ContextualFuncCreationHelper(container);
                            
                            var dgt = Delegate.CreateDelegate(funcType, funcInstance, method);

                            //Create lazy with the Func delegate as a parameter
                            return Activator.CreateInstance(funcParamType, dgt);
                        };

                        //Since the Lazy<> acts as a singleton and retains state, we need to determine if the Lazy<>
                        //should be transient or singleton
                        if (lifestyle == Lifestyle.Singleton)
                        {
                            container.RegisterSingle(funcParamType, creationDelegate);
                        }
                        else
                        {
                            container.Register(funcParamType, creationDelegate);
                        }
                    }
                }
            }
        }
        /// <summary>
        /// Discovers any IEnumerable dependencies of the specified type and attempts to register them.
        /// </summary>
        /// <param name="concreteType">Concrete type for which the dependencies should be discovered
        /// and registered</param>
        /// <param name="container">SimpleInjector container in which the dependencies should be registered.</param>
        /// <param name="options">Options to apply to the discovery and registration process.</param>
        public void RegisterDependencies(Type concreteType, Container container, IAutoRegistrationOptions options)
        {
            foreach (var ctor in concreteType.GetConstructors().Where((x) => x.IsPublic))
            {
                foreach (var param in ctor.GetParameters().Where((x) => typeof(System.Collections.IEnumerable).IsAssignableFrom(x.ParameterType)))
                {
                    var genericArgType = param.ParameterType.GenericTypeArguments.FirstOrDefault();

                    if (genericArgType != null 
                     && genericArgType.IsInterface 
                     && options.AutoRegistrationEnabledProvider.IsAutoRegistrationEnabled(genericArgType))
                    {
                        container.RegisterAll(genericArgType, options.ImplementationProvider.GetConcreteImplementionsOf(genericArgType));
                    }
                }
            }
        }
        /// <summary>
        /// Discovers and registers all types within the root namespace of the application.  To extend the behavior or specify 
        /// details, including the root namespace, please specify the options object.
        /// </summary>
        /// <param name="container">SimpleInjector container in which these types should be registered.</param>
        /// <param name="options">Optional: options to use while registering types.  This can also be used or override or
        /// extend the behavior of the auto-registration process.</param>
        public static void AutoRegister(this Container container, IAutoRegistrationOptions options = null)
        {
            if (options == null)
            {
                string ns = null;

                //Must be in constructor to get actual calling assembly
                var workingAssembly = Assembly.GetEntryAssembly()
                                   ?? Assembly.GetCallingAssembly();

                if (workingAssembly != null)
                {
                    var workingNamespace = workingAssembly.FullName;
                    var rootNamespaceDotIndex = workingNamespace.IndexOf(".");

                    ns = rootNamespaceDotIndex > 0 ? workingNamespace.Substring(0, rootNamespaceDotIndex) : workingNamespace;
                }

                options = new AutoRegistrationOptions(ns);
            }            
                      
            var typeRegProvider = new TypeRegistrationProvider(container, options);
            typeRegProvider.RegisterTypes();
        }