ArnoldMeshLight::ArnoldMeshLight( const std::string &name )
	:	GafferScene::FilteredSceneProcessor( name, IECore::PathMatcher::NoMatch )
{

	// ArnoldAttributesNode. This hides the objects from the majority
	// of ray types, since we don't want to add the poor sampling of the
	// object on top of the nice sampling of the light. The only visibility
	// option we don't turn off is camera visibility - instead we promote
	// so the user can decide whether or not the mesh should be visible in
	// the render.

	ArnoldAttributesPtr attributes = new ArnoldAttributes( "__attributes" );
	attributes->inPlug()->setInput( inPlug() );
	attributes->filterPlug()->setInput( filterPlug() );
	for( CompoundDataPlug::MemberPlugIterator it( attributes->attributesPlug() ); !it.done(); ++it )
	{
		if( boost::ends_with( (*it)->getName().string(), "Visibility" ) && (*it)->getName() != "cameraVisibility" )
		{
			(*it)->enabledPlug()->setValue( true );
			(*it)->valuePlug<BoolPlug>()->setValue( false );
		}
	}

	addChild( attributes );

	Plug *internalCameraVisibilityPlug = attributes->attributesPlug()->getChild<Plug>( "cameraVisibility" );
	PlugPtr cameraVisibilityPlug = internalCameraVisibilityPlug->createCounterpart( "cameraVisibility", Plug::In );
	addChild( cameraVisibilityPlug );
	internalCameraVisibilityPlug->setInput( cameraVisibilityPlug );

	// Shader node. This loads the Arnold mesh_light shader.

	ArnoldShaderPtr shader = new ArnoldShader( "__shader" );
	shader->loadShader( "mesh_light" );
	addChild( shader );

	PlugPtr parametersPlug = shader->parametersPlug()->createCounterpart( "parameters", Plug::In );
	addChild( parametersPlug );
	for( PlugIterator srcIt( parametersPlug.get() ), dstIt( shader->parametersPlug() ); !srcIt.done(); ++srcIt, ++dstIt )
	{
		(*dstIt)->setInput( *srcIt );
		// We don't need the parameters to be dynamic, because we create the
		// plugs in our constructor when calling `loadShader()`.
		(*srcIt)->setFlags( Plug::Dynamic, false );
	}

	// ShaderAssignment node. This assigns the mesh_light shader
	// to the objects chosen by the filter.

	ShaderAssignmentPtr shaderAssignment = new ShaderAssignment( "__shaderAssignment" );
	shaderAssignment->inPlug()->setInput( attributes->outPlug() );
	shaderAssignment->filterPlug()->setInput( filterPlug() );
	shaderAssignment->shaderPlug()->setInput( shader->outPlug() );
	addChild( shaderAssignment );

	// Set node. This adds the objects into the __lights set,
	// so they will be output correctly to the renderer.

	SetPtr set = new Set( "__set" );
	set->inPlug()->setInput( shaderAssignment->outPlug() );
	set->filterPlug()->setInput( filterPlug() );
	set->namePlug()->setValue( "__lights" );
	set->modePlug()->setValue( Set::Add );
	addChild( set );

	// Default lights Set node.

	BoolPlugPtr defaultLightPlug = new BoolPlug( "defaultLight", Plug::In, true );
	addChild( defaultLightPlug );

	SetPtr defaultLightsSet = new Set( "__defaultLightsSet" );
	defaultLightsSet->inPlug()->setInput( set->outPlug() );
	defaultLightsSet->filterPlug()->setInput( filterPlug() );
	defaultLightsSet->enabledPlug()->setInput( defaultLightPlug.get() );
	defaultLightsSet->namePlug()->setValue( "defaultLights" );
	defaultLightsSet->modePlug()->setValue( Set::Add );
	addChild( defaultLightsSet );

	// Switch for enabling/disabling

	SwitchPtr enabledSwitch = new Switch( "__switch" );
	enabledSwitch->setup( inPlug() );
	enabledSwitch->inPlugs()->getChild<ScenePlug>( 0 )->setInput( inPlug() );
	enabledSwitch->inPlugs()->getChild<ScenePlug>( 1 )->setInput( defaultLightsSet->outPlug() );
	enabledSwitch->indexPlug()->setValue( 1 );
	enabledSwitch->enabledPlug()->setInput( enabledPlug() );
	addChild( enabledSwitch );

	outPlug()->setInput( enabledSwitch->outPlug() );
	// We don't need to serialise the connection because we make
	// it upon construction.
	/// \todo Can we just do this in the SceneProcessor base class?
	outPlug()->setFlags( Plug::Serialisable, false );
}