/*-------------------------------------------------------------------------------------
Copyright (c) 2006 John Judnich

This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software.
Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
-------------------------------------------------------------------------------------*/

/// \file BatchPageHw.h
/// \brief BatchPageHw is an extension to PagedGeometry which displays entities as hardware instanced geometry.
//-------------------------------------------------------------------------------------
#ifndef __BatchPageHw_H__
#define __BatchPageHw_H__

#include "PagedGeometry.h"
#include "BatchedGeometryHw.h"

#include <memory>
#include <OgrePrerequisites.h>
#include <OgreStringConverter.h>

namespace Forests
{

	/**
	\brief The BatchPageHw class renders entities as hardware instanced geometry.

	This is one of the geometry page types included in the PagedGeometry engine. These
	page types should be added to a PagedGeometry object with PagedGeometry::addDetailLevel()
	so the PagedGeometry will know how you want your geometry displayed.

	To use this page type, use (the last parameter is optional):
	\code
	PagedGeometry::addDetailLevel<BatchPageHw>(farRange, transitionLength, Ogre::Any(LODLevel));
	\endcode

	Hardware instanced geometry is generally much faster than plain entities, since video card state
	changes and transform calculations can be minimized. Instanced geometry can be anywhere
	from 2 to 20 times faster than plain entities.

	"LODLevel" can be used to specify a certain LOD level to use from the added entities.
	This would be useful, for example, if you wanted to add high-res batched trees near the camera,
	and low-res batched trees farther away.
	*/
    class BatchPageHw: public GeometryPage
    {

		typedef std::vector<Ogre::MaterialPtr> TMaterials;
   
	public:
      
		BatchPageHw();
		
		void init(PagedGeometry *geom, const Ogre::Any &data);

		void addEntity(Ogre::Entity *ent, const Ogre::Vector3 &position, const Ogre::Quaternion &rotation,
			const Ogre::Vector3 &scale, const Ogre::ColourValue &color);
		
		void removeEntities();

        virtual void setCastShadows(bool castShadows) {}
        virtual void setVisibilityFlags(Ogre::uint32 visibilityFlags) {}
		void build();

	    void setVisible(bool visible);
		void setFade(bool enabled, Ogre::Real visibleDist, Ogre::Real invisibleDist);
	        
		const Ogre::AxisAlignedBox &getBoundingBox() { return m_pBatchGeom->getBoundingBox(); }

		struct Params
		{
			unsigned lod;
			bool shadows;
		};

        static void setCustomShadersNames(const Ogre::String & vsName,const Ogre::String & psName);
	protected:
		
		virtual void _updateShaders();
		
		Ogre::String getShaderLanguage();
		Ogre::String getMaterialSignature (const Ogre::MaterialPtr material, bool fadeEnabled);

		Ogre::String createVertexProgName (bool lightingEnabled, bool fadeEnabled, BatchedGeometryHw::SubBatchHw& subBatch);
		Ogre::String createVertexCasterProgName (const Ogre::String& vertexProgName);
		void createVertexProgSource (BatchedGeometryHw::SubBatchHw& subBatch, bool lightingEnabled, Ogre::String& progSource);
		void createVertexCasterProgSource (BatchedGeometryHw::SubBatchHw& subBatch, Ogre::String& progSource);
		void createTexCoordLocals (BatchedGeometryHw::SubBatchHw &subBatch, Ogre::String& progSource);
		void createTexcoordParams (Forests::BatchedGeometryHw::SubBatchHw& subBatch, Ogre::String& progSource);
		Ogre::MaterialPtr createInstanceMaterial (const Ogre::MaterialPtr material, const Ogre::String& materialSignature, 
			const Ogre::String& vertexProgName, const Ogre::String& vertexCasterProgName, 
			bool lightingEnabled, bool fadeEnabled, const Ogre::String& shaderLanguage);

		void setVertexShaderConfig (const Ogre::String& shaderLanguage, Ogre::HighLevelGpuProgramPtr& vertexShader);
		void setVertexShaderParams (bool lightingEnabled, bool fadeEnabled, Ogre::GpuProgramParametersSharedPtr params, const Ogre::String& shaderLanguage, Ogre::Pass* pass);
		void setVertexCasterShaderParams (Ogre::GpuProgramParametersSharedPtr params);
				
		Ogre::HighLevelGpuProgramPtr loadShader (const Ogre::String& progName, const Ogre::String& language, const Ogre::String& source);

		static Ogre::String getUniqueID(const Ogre::String &prefix)
		{
			 return prefix + Ogre::StringConverter::toString(++s_nGUID);
		}

		PagedGeometry*						m_pPagedGeom;
		Ogre::SceneManager*					m_pSceneMgr;
		std::auto_ptr<BatchedGeometryHw>	m_pBatchGeom;
		size_t								m_nLODLevel;
		bool								m_bFadeEnabled;
		bool								m_bShadersSupported;
		Ogre::Real							m_fVisibleDist;
		Ogre::Real							m_fInvisibleDist;
		TMaterials							m_vecUnfadedMaterials;
      
		static unsigned long s_nRefCount;
		static unsigned long s_nGUID;

        static Ogre::String  s_CustomVSName;
        static Ogre::String  s_CustomPSName;
   };

}

#endif
