/*****************************************************************************

File: ClipmapPatchManager.h
Desc: The collection of the 6 sides of a cubemap

Date: 2005/01/01

Author: Wim Lemkens

*****************************************************************************/

#ifndef CLIPMAPPATCHMANAGER_H
#define CLIPMAPPATCHMANAGER_H

#include <OgreSimpleRenderable.h>
#include <OgreRenderOperation.h>
#include <OgreCamera.h>
#include <OgreSceneNode.h>
#include "OgreHardwareBufferManager.h"
#include <OgreMaterialManager.h>

#include <OgreSceneNode.h>
#include <OgreTextureUnitState.h>
#include <OgreSceneManager.h>

#include "ClipmapRegion.h"
#include "ClipmapPatch.h"
#include "ClipmapConfig.h"
#include "Vector2i.h"
#include "SeperateCubemappedPlanetReader.h"

#include <stdlib.h>
#include <vector>
#include <queue>

namespace Ogre
{

	class ClipmapPatchManager : public Renderable, public MovableObject {
	public:

		/**
		* The constructor of the patchmanager
		* @post	The patchmanager is marked as not initialised
		* @post	The patchamanger is marked as not needing refinement
		* @post	A name is assigned to the patchamanger
		*/
		ClipmapPatchManager();

		/**
		* The destructor of the patchmanager
		* @post	the Hardware Index- and VertexBuffers are set to null
		* @post	the clipmappatchen in clipmapPatch[] are destroyed
		*/
		~ClipmapPatchManager();

		/**
		* Update the renderqueue
		* Gets called by Ogre
		* @param	RenderQueue *queue
		* 			A pointer to the renderqueue, given by Ogre
		* @post	If the camera has moved the geometry is updated
		* @post	The patchmanager is added to the renderqueue.
		*/
		void _updateRenderQueue(RenderQueue *queue)
		{
			if ((pcampos!=campos)||(needRefinement)){
				generateRenderQueue();
			}
			queue->addRenderable(this);
		}

		/**
		* Initialises the patchmanager
		* @param	SceneNode *sceneRoot
		* 			The root of the scene where the geometry has to be attached	to
		* @param	SeperateCubemappedPlanetReader* geometryReader
		* 			The geometryreader containing the geometry of the planet
		*/
		bool initialise(SceneNode *sceneRoot, SeperateCubemappedPlanetReader* geometryReader);

		/**
		* Calculate and update the indices for the indexbuffer
		* @param	force
		* 			true : force the vertexdata to also be rewritten
		*/
		void updateIndices(bool force=false);

		/**
		* A method needed by Ogre. Returns the world transformations
		*/
		virtual void getWorldTransforms(Matrix4 *xform) const
		{
			*xform = mParentNode->_getFullTransform();
		}

		/**
		* A method needed by Ogre. Returns the world orientation
		*/
		virtual const Quaternion& getWorldOrientation(void) const
		{
			return mParentNode->_getDerivedOrientation();
		}

		/**
		* A method needed by Ogre. Returns the world position
		*/
		virtual const Vector3& getWorldPosition(void) const
		{
			return mParentNode->_getDerivedPosition();
		}

		/** @copydoc Renderable::getLights */
		const LightList& getLights(void) const { return getParentSceneNode()->findLights(getBoundingRadius()); }

		/// Returns the name of instance
		virtual const String &getName(void) const
		{ 
			return mName;
		}

		/// Returns the movable type
		virtual const String& getMovableType(void) const
		{
			static String myType = "ClipmapPatch";
			return myType;
		}

		/**
		* Gets called when the camera is moved
		* @post	new.pcampos = old.campos
		* @post	new.campos = new camera position
		*/
		void _notifyCurrentCamera(Camera *cam);

		Real getBoundingRadius(void) const { return 0; /* not needed */ }

		/**
		* Sets the renderoperation up
		*/
		void getRenderOperation(RenderOperation& op);

		// Needed for Ogre, not important, not implemented
		Real getSquaredViewDepth(const Camera *cam) const
		{
			return 0;
		}

		/// Returns the bounding box
		const AxisAlignedBox &getBoundingBox() const
		{
			return mBounds;
		}

		/// Returns the material
		virtual const MaterialPtr& getMaterial(void) const
		{
			return mMaterial;
		}


		/// Sets the material
		void setMaterial(MaterialPtr &material)
		{
			mMaterial = material;
		}

		/**
		* Sets the material for this renderable
		*/
		bool setMaterial(String materialName, SceneNode* mSceneRoot);

	private:
		// The position of the camera and the previous position
		Vector3 campos,pcampos;

		// Are there patches that need refinement?
		bool needRefinement;

		/**
		* Notifies al patches that they should check for changes and update if 
		* needed.
		*/
		void generateRenderQueue();

		// The 6 clipmappatches
		ClipmapPatch **clipmapPatch;


		/// Unique name for each instance
		String mName;
		static unsigned int mGenNameCount;

		/// Position in world coordinates
		Vector3	mWorld;
		/// Position relative to zone
		Vector3	mZone;
		/// Scaling info
		Vector3	mScale;

		/// Material used for rendering
		MaterialPtr mMaterial;

		/// Bounding box
		AxisAlignedBox mBounds;

		/// Is it already initialised?
		bool mInit;

		/// The vertexdata
		VertexData* mVertexData;
		/// The indexdata
		IndexData* mIndexData;

		/// The hardwareVertexBuffer
		HardwareVertexBufferSharedPtr posVertexBuffer ;
		/// The HardwareIndexBuffer
		HardwareIndexBufferSharedPtr indexBuffer ;

		/// The geometryreader to query the heightdata
		SeperateCubemappedPlanetReader *mGeometryReader;

		/// The root of the scene to which the planet will be attached
		SceneNode *mSceneRoot;

		/// The size of the clipRegion		
		Real clipRegion;

		/**
		* Returns the depth of the real (not synthesized) geometry
		*/
		int getDepth();

		/// Stores the number of levels of real (not synthesized) geometry for
		/// efficiency
		int nbOfLevels;

		/**
		* Initialises the hardwareVertexBuffer to the right size
		* @post	posVertexBuffer points to a new buffer
		*/ 		
		void createLandscape();

		/**
		* Returns the size in quads of the given level
		* @param	short level
		* 			The level to return the number of quads from
		* @return	int
		* 			The size of the level
		*/
		int getLevelSize(short level);
	};
} // namespace Ogre

#endif // CLIPMAPPATCHMANAGER_H
