/// This file allows to bind a sub-scene to use as environment rendering.
/// Typically, you attach a shape rendering in camera space (i.e. moving with the camera)
///	 that will render below the setup render state
/// 
///
o3djs.provide( 'patapi.environment' );

o3djs.require( 'patapi' );

o3djs.require('o3djs.util');
o3djs.require('o3djs.camera');
o3djs.require('o3djs.math');
o3djs.require('o3djs.rendergraph');
o3djs.require('o3djs.pack');
o3djs.require('o3djs.scene');
o3djs.require('o3djs.effect');
o3djs.require('o3djs.primitives');

var Math3D = o3djs.math;	// O3D Math


////////////////////////////////////////////////////////////////////////////
// Declares a class holding informations on the environment
//
//	_O3DClient, the O3D client
//	_O3DPack, the O3D pack into which create the objects
//	_optEnvironmentShape, the optional shape to use as environment
//	_optUpdateCallback, the optional callback that will be called on every update (prototype is "Callback( _Environment )")
//
patapi.Environment = function( _O3DClient, _O3DPack, _optEnvironmentShape, _optUpdateCallback )
{
	this.m_Client = _O3DClient;	// Save the client for later use
	this.m_Pack = _O3DPack;		// Save the pack for later use

	this.m_UpdateCallback = _optUpdateCallback ? _optUpdateCallback : null;

	// Create the environment transform
	this.m_EnvironmentTransform = this.m_Pack.createObject( 'Transform' );		// Create a Root Transform for the environment scene
	this.m_EnvironmentTransform.name = "Environment Root";
	if ( _optEnvironmentShape )
		this.m_EnvironmentTransform.addShape( _optEnvironmentShape );			// Attach the shape to the scene.
	this.m_EnvironmentTransform.cull = false;


	////////////////////////////////////////////////////////////////////////////
	// Create the render nodes
	//

	// Create our single state-set
	// The state-set will be the root of every drawpasses, one drawpass per post-process
	var State = this.m_Pack.createObject( 'State' );
  		State.getStateParam( 'o3d.ZEnable' ).value = true;
		State.getStateParam( 'o3d.ZComparisonFunction' ).value = o3djs.base.o3d.State.CMP_ALWAYS;	// Always authorize writing (so the shader can be used to initialize the ZBuffer)
  		State.getStateParam( 'o3d.ZWriteEnable' ).value = true;
  		State.getStateParam( 'o3d.CullMode' ).value = o3djs.base.o3d.State.CULL_NONE;				// No culling...


	this.m_StateSet = this.m_Pack.createObject( 'StateSet' );
	this.m_StateSet.name = "Environment StateSet";
	this.m_StateSet.state = State;

	// Create our single draw-list
	this.m_DrawList = this.m_Pack.createObject( 'DrawList' );
	this.m_DrawList.name = "Environment DrawList";

// 	this.m_ClearBuffer = this.m_Pack.createObject( 'ClearBuffer' );
// 	this.m_ClearBuffer.clearColorFlag = false;
// 	this.m_ClearBuffer.clearColor = [0.1, 0.2, 0.3, 1.0];
// 	this.m_ClearBuffer.clearDepthFlag = true;
// 	this.m_ClearBuffer.clearDepth = 1.0;
// 	this.m_ClearBuffer.clearStencilFlag = false;
// 	this.m_ClearBuffer.priority = 0;
// 	this.m_ClearBuffer.parent = this.m_StateSet;

	// Create a tree-traversal render node bound to our single object
	this.m_TreeTraversal = this.m_Pack.createObject( 'TreeTraversal' );
	this.m_TreeTraversal.name = "Environment TreeTraversal";
	this.m_TreeTraversal.transform = this.m_EnvironmentTransform;								// Tie to the transform as it's the only object we're going to render
	this.m_TreeTraversal.parent = this.m_StateSet;
	this.m_TreeTraversal.priority = 1;

	// Create a single drawpass for that post-process
	this.m_DrawPass = this.m_Pack.createObject( 'DrawPass' );
	this.m_DrawPass.name = "Environment DrawPass";
	this.m_DrawPass.drawList = this.m_DrawList;
	this.m_DrawPass.parent = this.m_StateSet;
	this.m_DrawPass.priority = 2;


	////////////////////////////////////////////////////////////////////////////
	// Perform a single dummy update to initialize parameters
	//
	this.Update();
};

patapi.Environment.prototype = 
{
	// Gets the environment root transform
	getRootTransform : function()			{ return this.m_EnvironmentTransform; },

	// Gets the environment draw list
	getDrawList : function()				{ return this.m_DrawList; },

	// Gets or sets the callback that will be called on every update
	getUpdateCallback : function()			{ return this.m_UpdateCallback; },
	setUpdateCallback : function( value )	{ this.m_UpdateCallback = value; },

	// Disposes of used resources
	// Call this when exiting the application
	//
	Dispose : function()
	{
		throw "TODO!"
	},

	// Binds the environment to the scene
	//	_SceneViewInfos, the standard ViewInfo structure created by a call to o3djs.rendergraph.createView()
	//
	BindToScene : function(	_SceneViewInfos )
	{
		if ( !_SceneViewInfos )
			throw "Invalid SceneViewInfo !";

		this.m_SceneViewInfos = _SceneViewInfos;

		// We're assuming the created render graph is something like:
		//
		//        [Viewport]
		//            |
		//     +------+--------+------------------+---------------------+
		//     |               |                  |                     |
		// [ClearBuffer] [TreeTraversal] [Performance StateSet] [ZOrdered StateSet]
		//                                        |                     |
		//                               [Performance DrawPass] [ZOrdered DrawPass]
		//
		//
		// Modify it a bit so it doesn't call the "ClearBuffer" render node but renders our environment instead
		//

		this.m_StateSet.parent = this.m_SceneViewInfos.root;
		this.m_StateSet.priority = this.m_SceneViewInfos.clearBuffer.priority;
		this.m_TreeTraversal.registerDrawList( this.m_DrawList, this.m_SceneViewInfos.drawContext, true );	// Tie to our only draw list

		// Detach the clear buffer node
		this.m_SceneViewInfos.clearBuffer.parent = null;

		// We should now have something more like:
		//
		//                   [Viewport]
		//                       |
		//                +------+--------+------------------+---------------------+
		//                |               |                  |                     |
		//          [EnvStateSet]   [TreeTraversal] [Performance StateSet] [ZOrdered StateSet]
		//                |                                  |                     |
		//        +-------+-------+                 [Performance DrawPass] [ZOrdered DrawPass]
		//        |               |
		//  [TreeTraversal]   [DrawPass]
	},

	// Attaches a new shape to the environment
	//
	AddEnvironmentShape : function( _Shape )
	{
		this.m_EnvironmentTransform.addShape( _Shape );
	},

	// Updates the environment
	//
	Update : function( _optCameraPosition )
	{
		// Update the camera position
		if ( _optCameraPosition )
			this.UpdateCameraPosition( _optCameraPosition );
	
		////////////////////////////////////////////////////////////////////////////
		// Notify if the callback is valid
		if ( this.m_UpdateCallback != null )
			this.m_UpdateCallback( this );
	},

	// Updates the position of the environment given the current camera position
	//	_CameraPosition, the camera position in WORLD space (a [X,Y,Z] vector)
	//
	UpdateCameraPosition : function( _CameraPosition )
	{
		var	CurrentTransform = this.m_EnvironmentTransform.localMatrix;
		if ( !CurrentTransform )
			return;

		CurrentTransform[3] = [_CameraPosition[0], _CameraPosition[1], _CameraPosition[2], 1];

		this.m_EnvironmentTransform.localMatrix = CurrentTransform;
	}
};
