/// <summary>
        ///     Creates a new instance of the <see cref="Engine" /> class.
        /// </summary>
        /// <param name="createInfo">
        ///     The parameters to use for creating a new runtime instance.
        /// </param>
        /// <returns>
        ///     The created runtime environment.
        /// </returns>
        /// <exception cref="ArgumentNullException">
        ///     <paramref name="createInfo"/> is <c>null</c>.
        /// </exception>
        /// <exception cref="EngineException">
        ///     An error occurred while creating the runtime.
        /// </exception>
        public static Engine Create(
            EngineCreateInfo createInfo)
        {
            Guard.NotNull(createInfo, nameof(createInfo));

            try
            {
                return(CreateCore(createInfo));
            }
            catch (Exception e)
            {
                throw new EngineException(
                          "Unable to create the runtime. See the inner exception for details.",
                          e);
            }
        }
        private static Engine CreateCore(
            EngineCreateInfo createInfo)
        {
            Debug.Assert(createInfo != null);

            var extensionDirectory = createInfo.ExtensionDirectory;

            Debug.Assert(extensionDirectory != null);

            var assemblyLoader = createInfo.AssemblyLoader ?? new AssemblyLoader();

            var assemblyDiscovery = new AssemblyExtensionDiscovery(assemblyLoader);

            var catalog = new ReflectionPlugInCatalog(assemblyDiscovery);

            var factory     = new DataExtensionFactory();
            var repoBuilder = factory.CreateDataExtensionRepository();
            var reflector   = new DataExtensionReflector(
                assemblyDiscovery,
                repoBuilder);

            assemblyDiscovery.ProcessAssemblies(extensionDirectory);

            repoBuilder.FinalizeDataExtensions();

            var repoTuple = Tuple.Create(factory, repoBuilder);

            Debug.Assert(repoTuple != null);

            var instance = new EngineImpl();

            instance.ExtensionDirectory = extensionDirectory;
            instance.customDataSourceReferences.AddRange(catalog.PlugIns);
            instance.sourceDataCookers.AddRange(repoTuple.Item2.SourceDataCookers);
            instance.compositeDataCookers.AddRange(repoTuple.Item2.CompositeDataCookers);

            var allTables = new HashSet <Guid>();

            foreach (var tableId in repoTuple.Item2.TablesById)
            {
                allTables.Add(tableId.Key);
            }

            foreach (var descriptor in catalog.PlugIns.SelectMany(x => x.AvailableTables))
            {
                allTables.Add(descriptor.Guid);
                instance.tableGuidToDescriptor[descriptor.Guid] = descriptor;
            }

            instance.allTables.AddRange(allTables);

            instance.dataProcessors.AddRange(repoTuple.Item2.DataProcessors);

            instance.repository = repoTuple.Item2;
            instance.factory    = repoTuple.Item1;

            instance.applicationEnvironment = new ApplicationEnvironment(
                applicationName: string.Empty,
                runtimeName: "Microsoft.Performance.Toolkit.Engine",
                new RuntimeTableSynchronizer(),
                new TableConfigurationsSerializer(),
                instance.repository,
                instance.factory.CreateSourceSessionFactory(),
                new RuntimeMessageBox());

            instance.loader = assemblyLoader;

            foreach (var cds in instance.customDataSourceReferences)
            {
                try
                {
                    cds.Instance.SetApplicationEnvironment(instance.applicationEnvironment);

                    // todo: CreateLogger func should be passed in from the EngineCreateInfo
                    cds.Instance.SetLogger(Logger.Create(cds.Instance.GetType()));
                }
                catch (Exception e)
                {
                    //
                    // todo: log
                    //

                    Console.Error.WriteLine(e);
                }
            }

            instance.IsProcessed = false;

            return(instance);
        }