// Provides the Effects Manager object and a wrapping for O3D Effects
//
// The main advantage of passing through the Effects Manager rather than using plain O3D effects is
//	the possiblity to write #include "SomePath\MyOtherShaderCode.shader" in your shader. The include
//	will be resolved by the manager automatically.
//
// The second HUUUGE advantage, a little more difficult to grasp, is the notion of "shader interfaces".
// Let's take a look at the simple example of the Lambert lighting model:
//
// It needs a material color, a normal direction, an ambient light color, a light color and direction
// It's also computed quite simply by doing : Color = MatColor * (AmbientColor + LightColor * saturate( dot( Normal, LightDirection ) ))
//
// Then you can arbitrarily decide the convention for the "Lambert Shader Interface" is :
//
//	================= LAMBERT INTERFACE =================
//
//	// A set of 3 parameters
//	float3	AmbientColor;
//	float3	LightColor;
//	float3	LightDirection;
//
//	// A single function that uses these parameters and some other arguments that are not part of the interface
//	float3	ComputeLambert( float3 _MatColor, float3 _Normal )
//	{
//		return	_MatColor * (AmbientColor + LightColor * saturate( dot( _Normal, LightDirection ) ))
//	}
//
//	=====================================================
//
//
// The effects manager lets you do exactly that! You can write lines like that :
//
//		// #interface "Lambert"
//
//  (note the prefixing // so you can use the shader even without the manager)
//
// By writing such lines in your shaders, or by including shaders that have that kind of lines, you specify
//	that your shader (and all the materials using it) COMPLY with the "Lambert" interface.
//
// The second part of the mechanism consists in publishing and registering callbacks that are able to
//	provide the data needed by the interfaces.
// You do that by calling the "CreateInterfaceDataProvider()" method in the EffectsManager, telling the
//	name of the interface you can provide for and a callback function that will be called each time a new
//	material is created using the effect.
//
// From the shader programmer's perspective, as soon as you publish an interface, you can assume the data
//	are valid and you can use the associated functions in your code (in our example, you can immediately call
//	ComputeLambert() that will provide the material color and normal, assuming the lambert data are valid).
//
//
// So ? What do you think about that ? Kind of neat, isn't it ?   ^_^
//
//
o3djs.provide( 'patapi.effectsmanager' );

o3djs.require( 'patapi' );

o3djs.require('o3djs.util');
o3djs.require('o3djs.math');
o3djs.require('o3djs.pack');
o3djs.require('o3djs.effect');
o3djs.require('o3djs.dump');

patapi.EffectsManager = patapi.EffectsManager || {};

// An enumerate to give informations on provider type
//
patapi.EFFECTS_MANAGER_PROVIDER_TYPE = {
	MANUAL	: 0,	// Provider will provide data manually (e.g. on an internal change event)
	INIT	: 1,	// Provider can provide data on init (i.e. when an object is first bound to an effect)
	RENDER	: 2		// Provider can provide data at each Render event (i.e. real-time update)
};


////////////////////////////////////////////////////////////////////////////
// Declares a class for the effects manager
//
//	_O3DClient, the O3D client
//	_O3DPack, the O3D pack into which create the objects
//	_optBaseShaderURL, the optional base URL for shaders (will be "./" if not specified)
//
patapi.EffectsManager = function( _O3DClient, _O3DPack, _optBaseShaderURL )
{
	this.m_Client = _O3DClient;	// Save the client for later use
	this.m_Pack = _O3DPack;		// Save the pack for later use

	if ( !!_optBaseShaderURL )
	{	// Save the URL
		this.m_BaseShaderURL = _optBaseShaderURL.replace( /\\/g, '/' );	// Replace backslashes by forward ones
		if ( this.m_BaseShaderURL.lastIndexOf( '/' ) != this.m_BaseShaderURL.length-1 )
			this.m_BaseShaderURL = this.m_BaseShaderURL.concat( '/' );	// and append a trailing '/' if it's missing
	}
	else
		this.m_BaseShaderURL = "";

	this.m_RegisteredProviders = {};	// The table of registered data providers (KEYS=Interface Names (string), VALUES=Providers)
	this.m_RegisteredEffects = {};		// The table of registered interfaces (KEYS=Interface Names (string), VALUES=Registered Effects (Array))
};

patapi.EffectsManager.prototype = 
{
	// Gets the base URL for shaders
	getBaseShaderURL : function()	{ return this.m_BaseShaderURL; },

	// Disposes of used resources
	// Call this when exiting the application
	//
	Dispose : function()
	{
		throw "TODO!"
	},

	// Creates a wrapped effect
	//	_Name, the name of the effect to create (DEBUG PURPOSE)
	//	_ShaderURL, the URL of the shader to create, relative to the Manager's base URL
	//	returns: the created wrapped effect
	//
	CreateEffect : function( _Name, _ShaderURL )
	{
		return new patapi.Effect( this, _Name, _ShaderURL );
	},

	// Creates a new material directly bound to a new wrapped effect
	//	_Name, the name of the material to create (DEBUG PURPOSE)
	//	_ShaderURL, the URL of the shader to create, relative to the Manager's base URL
	//	returns: the created material
	//
	// NOTE: This is useful to create single materials like post-process materials that are used with only one material
	//
	CreateMaterial : function( _Name, _ShaderURL )
	{
//window.alert( "Creating Material " + _Name + " URL = " + _ShaderURL )

		// Load the FX
		var Effect = this.CreateEffect( _Name + " Wrapped Effect", _ShaderURL ).Load();

		// Create the material using that FX
		var Material = this.m_Pack.createObject( 'Material' );
			Material.name = _Name;

		Effect.BindObject( Material );

		return	Material;
	},

	// Creates a data provider
	//	_ProviderName, the name of the provider to create
	//	_SupportedInterfaceName, the name of the interface we can provide (warning: case sensitive!)
	//	_ProviderType, a PROVIDER_TYPE info telling how and when the provider is able to provide data
	//	_ProviderCallback, the callback function that will be called when requested to provide interface data to an object
	//	returns: the created provider
	//
	// NOTE: Throws if a provider already exists for the same interface : there can be only one provider at a time for a given interface!
	//
	CreateInterfaceDataProvider : function( _ProviderName, _SupportedInterfaceName, _ProviderType, _ProviderCallback )
	{
		if ( this.m_RegisteredProviders[_SupportedInterfaceName] )
			throw "There already exists a registered data provider named \"" + this.m_RegisteredProviders[_SupportedInterfaceName].getName() + "\" for interface \"" + _SupportedInterfaceName + "\" ! Can't register new provider \"" + _ProviderName + "\" !";

//window.alert( "Creating provider " + _ProviderName + " of type " + _ProviderType )

		return	this.m_RegisteredProviders[_SupportedInterfaceName] = new patapi.EffectInterfaceDataProvider( this, _ProviderName, _SupportedInterfaceName, _ProviderCallback, _ProviderType );
	},

	// Removes a data provider
	//	_SupportedInterfaceName, the name of the interface we want to remove
	//
	RemoveInterfaceDataProvider : function( _SupportedInterfaceName )
	{
		if ( !this.m_RegisteredProviders[_SupportedInterfaceName] )
			throw "There is no registered data provider for interface \"" + _SupportedInterfaceName + "\" !";

		delete this.m_RegisteredProviders[_SupportedInterfaceName];
	},

	// Function to call on every render
	// It will browse for every registered data provider with the "RENDER" type and call it for every object bound to the effects publishing the provider's interface
	//
	OnRender : function()
	{
		for ( var Key in this.m_RegisteredProviders )
		{
			var	Provider = this.m_RegisteredProviders[Key];
			if ( (Provider.getType() & patapi.EFFECTS_MANAGER_PROVIDER_TYPE.RENDER) == 0 )
				continue;	// Not of the RENDER type

			// Retrieve the array of effects registered with the provider's supported interface
			var	RegisteredEffects = this.m_RegisteredEffects[Provider.getInterface()]
			if ( !RegisteredEffects )
				continue;	// None exists...

			for ( var EffectIndex=0; EffectIndex < RegisteredEffects.length; EffectIndex++ )
			{
				var	Effect = RegisteredEffects[EffectIndex];

// window.alert( "Provider " + Provider.getName() + " providing data to the " + patapi.helpers.EnumerateProperties( Effect.getBoundObjects() ) + " objects bound to effect " + Effect.Name )

				// Retrieve the objects bound to the effect and provide data for each of them
				var	BoundObjects = Effect.getBoundObjects();
				for ( var BoundObjectKey in BoundObjects )
					Provider.ProvideData( BoundObjects[BoundObjectKey] );
			}
		}
	},

	// Immediately provides data needed for interfaces published by a wrapped effect to the given object using the effect
	//	_Effect, the effect publishing interfaces
	//	_Object, the object needing interfaces data (must have been bound using the effect's BindObject() or createUniformParameters() methods)
	//
	// NOTE: Throws if some data couldn't be bound (i.e. interfaces are not supported). You should catch such exceptions
	//	to avoid using objects that don't have all the necessary data to function properly.
	//
	ProvideInitialInterfacesData : function( _Effect, _Object )
	{
		for ( var InterfaceIndex=0; InterfaceIndex < _Effect.getInterfaces().length; InterfaceIndex++ )
		{
//window.alert( "Requesting Provider for interface \"" + _Effect.getInterfaces()[InterfaceIndex] + "\"" )

			var	Provider = this.FindInterfaceDataProvider( _Effect.getInterfaces()[InterfaceIndex] );
			if ( !Provider )
				throw "No registered data provider for interface \"" + _Effect.getInterfaces()[InterfaceIndex] + "\" ! The object cannot function properly without all its data and you shouldn't use it !"

//window.alert( "Found provider " + Provider.getName() + " of type " + Provider.getType() )
			if ( (Provider.getType() & patapi.EFFECTS_MANAGER_PROVIDER_TYPE.INIT) != 0 )
				Provider.ProvideData( _Object );
		}
	},

	// Finds an interface data provider given the name of the interface
	//	_SupportedInterfaceName, the name of the interface to retrieve the data provider for
	//	return the requested data provider or "undefined" if none could be found
	//
	FindInterfaceDataProvider : function( _SupportedInterfaceName )
	{
		return	this.m_RegisteredProviders[_SupportedInterfaceName];
	},

	// Registers a wrapped effect publishing the specified interface
	//	_InterfaceName, the name of the interface published by the effect
	//	_WrappedEffect, the effect to register
	//
	RegisterEffectWithInterface : function( _InterfaceName, _WrappedEffect )
	{
		if ( !this.m_RegisteredEffects[_InterfaceName] )
			this.m_RegisteredEffects[_InterfaceName] = new Array();	// No effect registered yet for this interface, create the array

		this.m_RegisteredEffects[_InterfaceName].push( _WrappedEffect );
	}
};


////////////////////////////////////////////////////////////////////////////
// Declares a class wrapping the O3D effects
//
//	_Manager, the effect manager
//	_Name, the name to give to the shader
//	_URL, the shader URL (relative to the manager's base URL)
//
patapi.Effect = function( _Manager, _Name, _URL )
{
// window.alert( "New Effect " + _Name + " URL = " + _URL )

	this.m_Manager = _Manager;
	this.m_Name = _Name;
	this.m_URL = this.ResolveURL( _URL );
	this.m_BasePath = this.m_URL.substring( 0, this.m_URL.lastIndexOf( '/' ) + 1 );

	// The list of interfaces published by this shader (will be filled once the shader is loaded)
	this.m_PublishedInterfaces = [];

	// The list of objects bound to this shader
	this.m_BoundObjects = [];

	this.m_Source = null;

	// Create the wrapped effect
	this.m_Effect = this.m_Manager.m_Pack.createObject( 'Effect' );
	this.m_Effect.name = _Name;
};

patapi.Effect.prototype =
{
	// Gets the effect name
	getName : function()			{ return this.m_Name; },

	// Gets the effect's absolute URL (directly loadable)
	getURL : function()				{ return this.m_URL; },

	// Gets the base path of the shader URL (e.g.  "A\B\C\" if the shader's URL is "A\B\C\D.shader")
	getBasePath : function()		{ return this.m_BasePath; },

	// Gets the O3D effect wrapped by this PatAPI effect
	getO3DEffect : function()		{ return this.m_Effect; },

	// Gets the list of interfaces published by this effect
	getInterfaces : function()		{ return this.m_PublishedInterfaces; },

	// Gets the array of objects bound to this effect
	getBoundObjects : function()	{ return this.m_BoundObjects; },

	// Gets the shader source code
	getSource : function()			{ return this.m_Source; },

	// Loads the effect
	//	_optCallback, an optional callback. If defined, the loading is asynchronous and the call back is called once the
	//					 effect has finished loading, otherwise the loading is synchronous
	//
	Load : function( _optCallback )
	{
		var async = _optCallback ? true : false;
		var	that = this;

		var callback = function( fxString )
		{
			// Resolve includes in this shader code
			fxString = that.ResolveIncludes( that.getBasePath(), fxString );

			// Resolve interfaces published by this shader
			that.ResolveInterfaces( fxString );

			// Actual load of the final resolved shader
			if ( !that.m_Effect.loadFromFXString( fxString ) )
			{	// The effect failed to compile!
				var	RegExp = /^.*\((\d*,\d*)\): (.*)/g;

				var	Match = RegExp.exec( that.m_Manager.m_Client.lastError );
				if ( Match == null )
					throw "Failed to load effect \"" + that.m_Name + "\" !\n" + that.m_Manager.m_Client.lastError;	// Unrecognized error...

				// Help the user by providing error context...
				//
				var	ErrorCoordinates = Match[1].split( ',' );
				var	ErrorLineIndex = parseInt( ErrorCoordinates[0] ) - 1;	// As we start counting from 0

				var	ShaderLines = fxString.split( '\n' );
					ShaderLines[ErrorLineIndex] += "	<=== " + Match[2];
					ShaderLines = ShaderLines.slice( Math.max( 0, ErrorLineIndex-3 ), Math.min( ShaderLines.length, ErrorLineIndex+4 ) );

				var	ErrorContext = ShaderLines.join( '\n' );

				throw "Failed to load effect \"" + that.m_Name + "\" !\n\n" + ErrorContext;
			}

			if ( async )
				_optCallback();
		};

		if ( async )
		{	// Asynchronous loading
			o3djs.io.loadTextFileAsynchronous( this.m_URL, callback );
		}
		else
		{	// Synchronous loading
			var fxString = o3djs.io.loadTextFileSynchronous( this.m_URL );
			callback( fxString );

			return	this;
		}

		return	null;
	},

	// Binds the effect to an object, usually a material
	//	_Object, the object to bind the effect to. This function has the same effect as the o3djs.effect.createUniformParameters() function
	//
	// NOTE: If the provided object is a material, the effect is automatically assigned
	//
	BindObject : function( _Object )
	{
		if ( this.FindObjectIndex( _Object ) != -1 )
			return;	// Already bound!

//o3djs.dump.dump( "BindingObject \"" + _Object.name + "\" to Effect \"" + this.m_Name + "\"\n" );
		
		// Create the uniform parameters requested by the Effect on this object
		this.m_Effect.createUniformParameters( _Object );
		this.m_BoundObjects.push( _Object );

//window.alert( "Adding Object Dependency " + this.m_Name )
		// Add a dependency on the object
		var	that = this;
		patapi.helpers.AddObjectDependency( _Object, "Effect " + this.m_Name, function( _DisposedObject ) { that.UnBindObject( _DisposedObject ); } );

		// Automatically assign the effect to the material
		if ( _Object.className == 'o3d.Material' )
			_Object.effect = this.m_Effect;

		// Provide the interface data
		this.m_Manager.ProvideInitialInterfacesData( this, _Object );

// window.alert( "Bound objects count for \"" + this.m_Name + "\" (current Binding Object = \"" + _Object.name + "\") = " + this.m_BoundObjects.length )
// window.alert( "Bound objects for \"" + this.m_Name + "\" = \r\n" + patapi.helpers.EnumerateProperties( this.m_BoundObjects ) )
	},

	// Unbinds the effect from an object
	//	_Object, the object to unbind the effect from
	//
	UnBindObject : function( _Object )
	{
//o3djs.dump.dump( "UnBindingObject \"" + _Object.name + "\" from Effect \"" + this.m_Name + "\" PrevCount = " + this.m_BoundObjects.length + " - " );

		var	ObjectIndex = this.FindObjectIndex( _Object );
		if ( ObjectIndex != -1 )
			this.m_BoundObjects.splice( ObjectIndex, 1 );	// Remove it

//o3djs.dump.dump( "NewCount = " + this.m_BoundObjects.length + "\n" );
	},

	// Finds the index of a bound object inside the array
	//	_Object, the object to find in the array
	// returns the index of the specified index or -1 if not found
	//
	FindObjectIndex : function( _Object )
	{
		for ( var ObjectIndex=0; ObjectIndex < this.m_BoundObjects.length; ObjectIndex++ )
			if ( this.m_BoundObjects[ObjectIndex] == _Object )
				return	ObjectIndex;	// Found it!

		return	-1;
	},

	////////////////////////////////////////////////////////////////////////////
	// Internal methods

	// Resolves an URL relative to the base shader URL
	//	_RelativeURL, the URL to resolve (can contain "../" parts to go up the hierarchy)
	//	returns: the absolute URL the IO library can resolve to load an actual file from the server
	//
	ResolveURL : function( _RelativeURL )
	{
		// Replace backslashes by forward ones	
		_RelativeURL = _RelativeURL.replace( /\\/g, '/' );

		// Remove any '/' prefix
		while ( _RelativeURL.indexOf( '/' ) == 0 )
			_RelativeURL = _RelativeURL.substring( 1 );

		// Concatenate into an absolute URL
		var AbsoluteURL = this.m_Manager.getBaseShaderURL() + _RelativeURL;

		// Split by '/'
		var	Directories = AbsoluteURL.split( '/' );
		var	TargetDirectories = [];
		for ( var DirectoryIndex=0; DirectoryIndex < Directories.length; DirectoryIndex++ )
		{
			var	Directory = Directories[DirectoryIndex];
			if ( Directory == ".." )
				TargetDirectories.pop();				// Remove the previously pushed element
			else if ( Directory != "." )
				TargetDirectories.push( Directory );	// Push a new element
		}

		// Join the array again in the new absolute URL
		AbsoluteURL = TargetDirectories.join( '/' );

//window.alert( "Relative URL = " + _RelativeURL + " - Absolute URL = " + AbsoluteURL )

		return	AbsoluteURL;
	},

	// Resolves the include parts of a shader
	//	_BaseURL, the base URL of the shader code file
	//	_ShaderCode, the shader code whose includes should be resolved
	//
	ResolveIncludes : function( _BaseURL, _ShaderCode )
	{
		var	TargetCode = _ShaderCode;

		// An include line syntax is in the form: #include "<some path>"
 		var	RX = new RegExp( /^\s*#include\s*"(.*)"/gim );
  		var IncludeMatch;
		while ( IncludeMatch = RX.exec( _ShaderCode ) )
		{
			// Resolve the include's URL
			var	IncludeURL = "";
			try
			{
				IncludeURL = this.ResolveURL( _BaseURL + IncludeMatch[1] );
			}
			catch ( e )
			{
				throw "Failed to resolve shader include URL \"" + IncludeMatch[1] + "\"! " + e;
			}

			// Attempt to load the file
			var	IncludedFile = "";
			try
			{
				IncludedFile = o3djs.io.loadTextFileSynchronous( IncludeURL );
			}
			catch ( e )
			{
				throw "Failed to load included shader file \"" + IncludeURL + "\"! " + e;
			}

			// Recursively call ourselves to resolve includes in the included file
			var	IncludeBasePath = IncludeURL.substring( 0, IncludeURL.lastIndexOf( '/' ) + 1 );
//window.alert( "Included File (URL = " + IncludeURL + " BasePath = " + IncludeBasePath + ") = \n\n" + IncludedFile + "\n\n=====================================\n RECURSING !" )
			IncludedFile = this.ResolveIncludes( IncludeBasePath, IncludedFile );

			// Replace the match with the included file
			TargetCode = TargetCode.replace( IncludeMatch[0], IncludedFile );
		}

//o3djs.dump.dump( "Target code for shader " + _BaseURL + " = \n\n" + TargetCode )

		return	TargetCode;
	},

	// Resolves the interfaces published by a shader
	//	_ShaderCode, the shader code whose interfaces must be resolved
	//
	ResolveInterfaces : function( _ShaderCode )
	{
		// An interface line syntax is in the form: #interface "<some name>"
		//
 		var	RX = new RegExp( /#interface\s*"(.*)"/gim );

		this.m_PublishedInterfaces = [];

  		var InterfaceMatch;
		while ( InterfaceMatch = RX.exec( _ShaderCode ) )
		{
			var	InterfaceName = InterfaceMatch[1];

			this.m_PublishedInterfaces.push( InterfaceName );

			// Also register the effect as publishing this interface
			this.m_Manager.RegisterEffectWithInterface( InterfaceName, this );
		}

// window.alert( "Interfaces published by " + this.m_Name + " =\n\n" + patapi.helpers.EnumerateProperties( this.getInterfaces() ) )
	},

	////////////////////////////////////////////////////////////////////////////
	// Wrapped Effect Members

	// Properties
//     get matrixLoadOrder()	{ return this.m_Effect.matrixLoadOrder; },
//     get	source()			{ return this.m_Effect.source; },
//     get params()			{ return this.m_Effect.params; },
//     get name()				{ return this.m_Effect.name; },
//     set name( value )		{ this.m_Effect.name = value; },
//     get clientId()			{ return this.m_Effect.clientId; },
//     get className()			{ return this.m_Effect.className; },

	// Methods
    loadFromFXString : function( effect )					{ return this.m_Effect.loadFromFXString( effect ); },
    createUniformParameters : function( param_object )		{ this.BindObject( param_object ); },
    createSASParameters : function( param_object )			{ this.m_Effect.createSASParameters( param_object ); },
    getParameterInfo : function()							{ return this.m_Effect.getParameterInfo(); },
    getStreamInfo : function()								{ return this.m_Effect.getStreamInfo(); },
    createParam : function( param_name, param_type_name )	{ return this.m_Effect.createParam( param_name, param_type_name ); },
    getParam : function( param_name )						{ return this.m_Effect.getParam( param_name ); },
    removeParam : function( param )							{ return this.m_Effect.removeParam( param ); },
    copyParams : function( source_param_object )			{ this.m_Effect.copyParams( source_param_object ); },
    isAClassName : function( class_name )					{ return this.m_Effect.isAClassName( class_name ); }
};



////////////////////////////////////////////////////////////////////////////
// Declares a class for interface data providers
//
//	_Manager, the effect manager
//	_Name, the name of the provider
//	_SupportedInterfaceName, the name of the interface we can support (warning: case sensitive!)
//	_Type, a PROVIDER_TYPE info telling how and when the provider is able to provide data
//	_Callback, the callback function that will be called when requested to provide interface data to an object (the callback prototype is "function MyCallback( SomeObject )" where SomeObject is the object to setup data for)
//
// NOTE: If specifying an INIT provider type, the provider will be called only once, when the material is bound to an effect
//		 If specifying a RENDER provider type, the provider will be called at each render to provide data (that's the case for dynamic providers that have volatile, changing data like the skydome)
//
patapi.EffectInterfaceDataProvider = function( _Manager, _Name, _SupportedInterfaceName, _Callback, _Type )
{
	this.m_Manager = _Manager;
	this.m_Name = _Name;
	this.m_Interface = _SupportedInterfaceName;
	this.m_Callback = _Callback;
	this.m_Type = _Type;
};

patapi.EffectInterfaceDataProvider.prototype =
{
	// Gets the name of the provider
	getName : function()		{ return this.m_Name; },

	// Gets the name of the interface it supports
	getInterface : function()	{ return this.m_Interface; },

	// Gets the type of providing it supports
	getType : function()		{ return this.m_Type; },


	// Provide the interface data to the specified object
	//	_Object, the object to setup interface data for (must have been bound to an effect publishing the interface)
	//
	ProvideData : function( _Object )
	{
//window.alert( "Provider \"" + this.m_Name + "\" providing data for object " + _Object )

		this.m_Callback( _Object );
	},

	// Provides the interface data to ALL objects that need them
	// This method is used by providers that are of MANUAL type so they can refresh shader parameters on demand (e.g. when their internal parameters changed)
	//
	ProvideDataAll : function()
	{
		// Retrieve the array of effects registered with the our interface
		var	RegisteredEffects = this.m_Manager.m_RegisteredEffects[this.m_Interface]
		if ( !RegisteredEffects )
			return;	// None exists...

		for ( var EffectIndex=0; EffectIndex < RegisteredEffects.length; EffectIndex++ )
		{
			var	Effect = RegisteredEffects[EffectIndex];

			// Retrieve the objects bound to the effect and provide data for each of them
			var	BoundObjects = Effect.getBoundObjects();
			for ( var BoundObjectKey in BoundObjects )
				this.ProvideData( BoundObjects[BoundObjectKey] );
		}
	}
};

