// Provides a helper to override an existing scene with a special shader
// This can be used to render a scene into a texture using a single shader like shadow-mapping or z-pass
//
o3djs.provide( 'patapi.sceneoverride' );

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');

o3djs.require( 'patapi' );
o3djs.require( 'patapi.rendernodeshierarchy' );


////////////////////////////////////////////////////////////////////////////
// Declares a class holding informations for scene override rendering
//
//	_O3DClient, the O3D client
//	_O3DPack, the O3D pack into which create the objects
//	_Name, the name of the override
//	_OverrideMaterial, the material to apply for the override rendering
//	_RenderTarget, the render target into which the scene will render
//	_DepthStencil, the depth stencil target into which the scene will render
//	_optClearColor, the optional clear color (FLOAT[4]) (if undefined, the color buffer won't be cleared)
//	_optClearDepth, the optional clear depth value (FLOAT) (if undefined, the depth buffer won't be cleared)
//
patapi.SceneOverride = function( _O3DClient, _O3DPack, _Name, _OverrideMaterial, _RenderTarget, _DepthStencil, _optClearColor, _optClearDepth )
{
	this.m_Client = _O3DClient;	// Save the client for later use
	this.m_Pack = _O3DPack;		// Save the pack for later use
	this.m_Name = _Name;
	this.m_OverrideMaterial = _OverrideMaterial;
	this.m_RenderTarget = _RenderTarget;
	this.m_DepthStencil = _DepthStencil;


	////////////////////////////////////////////////////////////////////////////
	// Create the rendering pipeline root
	//

	// Create the render surface set under which the standard scene should render
	this.m_RenderSurfaceSet = this.m_Pack.createObject( 'RenderSurfaceSet' );
	this.m_RenderSurfaceSet.name = this.m_Name + " RenderSurfaceSet";
	this.m_RenderSurfaceSet.renderSurface = this.m_RenderTarget.getSurface();
	this.m_RenderSurfaceSet.renderDepthStencilSurface = this.m_DepthStencil;
	// Priority will be set by the BindToScene() method

	// Create our viewport to render in the correct rectangle
	this.m_Viewport = this.m_Pack.createObject( 'Viewport' );
//	this.m_Viewport.viewport = [0.0, 0.0, 1.0, 1.0];
	this.m_Viewport.viewport = this.m_RenderTarget.getViewport();
	this.m_Viewport.parent = this.m_RenderSurfaceSet;
	this.m_Viewport.priority = 0;

 	// Create a ClearBuffer node if needed
 	var	ParentNode = this.m_Viewport;
 	if ( typeof(_optClearColor) != typeof(undefined) || typeof(_optClearDepth) != typeof(undefined) )
 	{
		this.m_ClearBuffer = this.m_Pack.createObject( 'ClearBuffer' );
		this.m_ClearBuffer.name = this.m_Name + " ClearBuffer";
		this.m_ClearBuffer.clearColorFlag = this.m_RenderTarget != null && typeof(_optClearColor) != typeof(undefined);
		this.m_ClearBuffer.clearColor = typeof(_optClearColor) != typeof(undefined) ? _optClearColor : [0,0,0,0];
		this.m_ClearBuffer.clearDepthFlag = this.m_DepthStencil != null && typeof(_optClearDepth) != typeof(undefined);
		this.m_ClearBuffer.clearDepth = typeof(_optClearDepth) != typeof(undefined) ? _optClearDepth : 1.0;
		this.m_ClearBuffer.clearStencilFlag = false;
		this.m_ClearBuffer.parent = this.m_Viewport;
//		this.m_StateSet.parent = this.m_RenderSurfaceSet;
		this.m_ClearBuffer.priority = 0;

		ParentNode = this.m_ClearBuffer;
	}

 	// Create our state-set
	this.m_StateSet = this.m_Pack.createObject( 'StateSet' );
	this.m_StateSet.name = this.m_Name + " StateSet";
	this.m_StateSet.state = this.m_OverrideMaterial.state;
	this.m_StateSet.parent = ParentNode;
	this.m_StateSet.priority = 0;

	// Create our single draw-list
	this.m_DrawList = this.m_Pack.createObject( 'DrawList' );
	this.m_DrawList.name = this.m_Name + " DrawList";

	// Create a single drawpass
	this.m_DrawPass = this.m_Pack.createObject( 'DrawPass' );
	this.m_DrawPass.name = this.m_Name + " DrawPass";
	this.m_DrawPass.drawList = this.m_DrawList;
	this.m_DrawPass.parent = this.m_StateSet;
	this.m_DrawPass.priority = 0;

	// Tie the override material to that draw list
	this.m_OverrideMaterial.drawList = this.m_DrawList;
};

patapi.SceneOverride.prototype = 
{
	// Disposes of used resources
	// Call this when exiting the application
	//
	Dispose : function()
	{
		throw "TODO!"
	},

	// Binds the scene override to the given scene
	//	_SceneViewInfos, the standard ViewInfo structure created by a call to o3djs.rendergraph.createView()
	//
	// NOTE: Remember to call this function only once the scene is loaded!
	// NOTE2: You can also call it before the scene is loaded and then call BindAdditionalTransformsHierarchy() afterward
	//
	BindToScene : function( _SceneViewInfos )
	{
		if ( _SceneViewInfos == null || typeof(_SceneViewInfos) == typeof(undefined) )
			throw "Invalid SceneViewInfo !";

		this.m_SceneViewInfos = _SceneViewInfos;

		////////////////////////////////////////////////////////////////////////////
		// Register our only draw list
		//
		this.m_SceneViewInfos.treeTraversal.registerDrawList( this.m_DrawList, _SceneViewInfos.drawContext, true );


		////////////////////////////////////////////////////////////////////////////
		// Create the draw elements for the scene's shape using our override material
		// The override material is tied to our draw list so everything will render into that draw list
		//
		this.BindAdditionalTransformsHierarchy( this.m_SceneViewInfos.treeRoot );

// alert( patapi.helpers.EnumerateProperties( this.m_SceneViewInfos ) )
// alert( "Scene Transform tree after CreateDrawElements:\n\n" + patapi.helpers.ShowTransformTree( this.m_Client.root ) )


		////////////////////////////////////////////////////////////////////////////
		// Add our render graph hierarchy into the existing scene's render graph
		//
		patapi.RenderNode.InsertAfter( _SceneViewInfos.root, _SceneViewInfos.treeTraversal, this.m_RenderSurfaceSet );


		////////////////////////////////////////////////////////////////////////////
		// Bug patch: O3D fails to restore Viewport render nodes so we insert a dummy one which is a copy of the scene's top viewport node
		//
		this.m_BuggyViewportRestore = this.m_Pack.createObject( 'Viewport' );
		this.m_BuggyViewportRestore.name = "Viewport Restore Bug";
		this.m_BuggyViewportRestore.viewport = this.m_SceneViewInfos.viewport.viewport;

		patapi.RenderNode.InsertAfter( _SceneViewInfos.root, this.m_RenderSurfaceSet, this.m_BuggyViewportRestore );
	},

	// Binds the scene override to an additional hierarchy of transforms
	//	_TransformRoot, the hierarchy of transforms to bind the scene override to
	//
	// NOTE: This function can be called for new scene elements being added later
	//	However, the BindToScene() function must have been called first !
	//	Also note that it's no use to call it for shapes that have already been bound to the scene
	//	(no additional render element will be generated if the material has already been parsed, as
	//	stated by the O3D doc here http://code.google.com/apis/o3d/docs/reference/classo3d_1_1_transform.html#549e91ad72ef95bdff1a010363835f37)
	//
	BindAdditionalTransformsHierarchy : function( _TransformRoot )
	{
		if ( !_TransformRoot )
			throw "BindAdditionalTransformsHierarchy() => Invalid TransformRoot!";

		////////////////////////////////////////////////////////////////////////////
		// Create the draw elements for the scene's shape using our override material
		// The override material is tied to our draw list so everything will render into that draw list
		//
		_TransformRoot.createDrawElements( this.m_Pack, this.m_OverrideMaterial );
	}
};
