IModule* ModuleManager::load( const std::string& moduleName )
{
	SystemState systemState = getSystem()->getState();

	if( systemState < SystemState_Initializing )
		throw IllegalStateException( "cannot load modules before the system is set up" );

	if( systemState > SystemState_Running )
		throw IllegalStateException( "cannot load modules while the system is being torn down" );

	if( _loaders.empty() )
		throw ModuleLoadException( "there are no installed module loaders" );

	// check if the module was already loaded
	IModule* alreadyLoaded = findModule( moduleName );
	if( alreadyLoaded )
		return alreadyLoaded;

	/*
		Load and initialize ModuleParts. Notice that once we initialize a part,
		it may register a new ModulePartLoader that must also be considered.
	 */

	// the IModule is created on demand
	Module* module = NULL;

	// for error handling: whether a part was being loaded (true) or initialized (false)
	bool wasLoading = true;

	try
	{
		size_t numLoaders = _loaders.size();
		for( size_t i = 0; i < numLoaders; ++i )
		{
			IModulePartLoader* loader = _loaders[i].get();
			if( !loader->canLoadModulePart( moduleName ) )
				continue;

			// load the module part
			wasLoading = true;

			RefPtr<IModulePart> part( loader->loadModulePart( moduleName ) );
			if( !part.isValid() )
				throw ModuleLoadException( "loader returned a null IModulePart" );

			if( !module )
				module = createModule( moduleName );
			module->addPart( part.get() );

			// initialize the module part
			wasLoading = false;
			part->initialize( module );

			// this module part may have added a new IModulePartLoader
			size_t newNumLoaders = _loaders.size();
			assert( newNumLoaders >= numLoaders );
			numLoaders = newNumLoaders;
		}
	}
	catch( std::exception& e )
	{
		// any error while loading or initializing a part aborts the whole module
		if( module )
			module->abort();

		std::stringstream ss;
		if( wasLoading )
			ss << "error loading module '" << moduleName << "': ";
		else
			ss << "exception raised by module '" << moduleName << "' during initialization: ";
		ss << e.what();

		throw ModuleLoadException( ss.str() );
	}

	if( !module )
	{
		CORAL_THROW( ModuleLoadException, "no module loader recognized '" << moduleName <<
			"' as a module (perhaps it was not compiled in " CORAL_BUILD_MODE " mode?)" );
	}

	module->initialize();
	syncModuleWithSystemState( module );

	return module;
}