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

File: ClipmapPatch.h
Desc: A class containing the information and functionality of one side of the
		"cubemapped" sphere
		
Date: 2005/01/01

Author: Wim Lemkens

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

#ifndef __CLIPMAPPATCH_H
#define __CLIPMAPPATCH_H

#include <OgreCamera.h>
#include "OgreHardwareBufferManager.h"
#include <OgreMaterialManager.h>


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

#include <stdlib.h>
#include <vector>
#include <queue>
using namespace std;


namespace Ogre
{

    //----------------------------------------------------------------------------

    /**
     * template for converting numbers into strings
     */
    template<typename T> inline std::string toString(T t)
    {
        std::ostringstream s;
        s << t;
        return s.str();
    }

    //----------------------------------------------------------------------------

    class ClipmapPatch
    {
	
    public:

		// Does this patch needs refinement?
		bool needRefinement;
		
		// A parameter for the distance/size ratio a new lod will kick in
		float clipRegion;

		// The width and the height of the heightmap
        int heightMapWidth;
        int heightMapHeight;

        // The material for the patch
        Material *pMaterial;

		/**
		 * The destructor of the patch
		 * @post	The clipRegions of this patch are deleted
		 */
        ~ClipmapPatch()
        {
        	while (!clipRegions.empty()){
        		delete clipRegions.back();
        		clipRegions.pop_back();
        	}
        }

		/**
		 * The constructor of this patch
		 * @post	Camera is set to a default non-zero location
		 * @post	Patch is marked as not initialised
		 * @post	Patch is marked as not in need of rendering
		 */        
        ClipmapPatch()
        {
			mInit = false;
            mNeedRendering = false;
            campos.x=400;
            campos.y=550;
            campos.z=400;

			clipRegions.clear();
        }

		/**
		 * Initialises the patch
		 * @param	SceneNode *sceneRoot
		 * 			The rootnode of the scene to attach the landscape to
		 * @param	SeperateCubemappedPlanetReader *geometryReader
		 * 			The datasource for height, texture, etc... coordinates
		 * @param	int map
		 * 			Wich of the 6 patches this patch is
		 */
        bool initialise(SceneNode *sceneRoot, SeperateCubemappedPlanetReader *geometryReader, int map);

		/**
		 * Sets the levels of detail to write to the correct address of the 
		 * vertex-buffer and on the correct offset
		 * @param	HardwareVertexBufferSharedPtr posVertexBuffer
		 * 			The vertex-buffer
		 * @param	int patchOffset
		 * 			The offset in the vertexbuffer for that lod
		 */
		void updateVertexBuffers(HardwareVertexBufferSharedPtr posVertexBuffer, int patchOffset);
		
		/**
		 * Create a new region and update the vertex-buffer
		 * @param	HardwareVertexBufferSharedPtr posVertexBuffer
		 * 			The vertex-buffer
		 * @param	int patchOffset
		 * 			The offset in the vertexbuffer for the new lod
		 */
		void refinePatch(HardwareVertexBufferSharedPtr posVertexBuffer, int patchOffset);
		
		/**
		 * Update the patch (and so the levels of detail) to the new 
		 * camera-position
		 * @param	Vector3 &campos
		 * 			The new position of the camera
		 */
		bool updatePositions(Vector3 &campos);
		
		/**
		 * Forces the regions to update their modified sections and send them to
		 * the graphics card
		 */
		void updateVertices();
		
		/**
		 * Returns the maximum level of detail to be displayed
		 * @return	the maximum level of detail to be displayed
		 */
		int getMaxLevel() const {
			return maxlevel;
		}

		/**
		 * Returns the minimum level of detail to be displayed
		 * @return	the minimum level of detail to be displayed
		 */
		int getMinLevel() const {
			return minlevel;
		}
		
		/**
		 * A vector containing pointers to the level of details (regions) of 
		 * the patch
		 */
		std::vector<ClipmapRegion*> clipRegions;

		/**
		 * Create the necessary regions for this patch
		 * @param	HardwareVertexBufferSharedPtr posVertexBuffer
		 * 			The vertex-buffer where the patches will have to write to
		 */
		void createRegions(HardwareVertexBufferSharedPtr posVertexBuffer);
		
		/**
		 * Forces all vertex-data to be rewritten to graphicsmemory 
		 */
		void refreshAll();
		
		/**
		 * Sets the pointers to the regions of other patches in the regions of
		 * this patch
		 * @param	int index
		 * 			The number of the patch the set of regions is of
		 * @param	std::vector<ClipmapRegion*> *regions
		 * 			A pointer to the vector pointing the regions of the other 
		 * 			patch
		 */
		void setOtherRegions(int index, std::vector<ClipmapRegion*> *regions);
		
		/**
		 * Set a pointer to the other patches
		 * @param	ClipmapPatch** neighbour
		 * 			a pointer to an array of pointers to the other patches
		 */
		void setOtherPatches(ClipmapPatch** neighbour);
		
protected:
    
		/// The offset where this patch will start in the vertexbuffer
		int mPatchOffset;

        /// Flag that indicates if this patch needs to be re-rendered
        bool mNeedRendering;

		/// A flag to indicate that this patch is initialised properly
		bool mInit;

		/// The last known position of the camera
		Vector3 campos;
		
		/// The depth of the real geometry, not the synthesized geometry
		int nbOfLevels;

		/// The	geometryreader to query for the planetary data
		SeperateCubemappedPlanetReader *mGeometryReader;
		
		/// The minimum an maximum found detail level to render
		int minlevel, maxlevel;
		
		/**
		 * Returns where above the surface of the sphere the given point is.
		 * The coordinates are relative to this patch. 
		 * @param	Vector3* point
		 * 			The point to project onto the sphere.
		 * @return	Vector3
		 * 			Vector.x : The relative position to this patch. 0 is the 
		 * 				left side, 1 is the right side, <0 or >1 for positions
		 * 				outside this patch 
		 * 			Vector.y : The height above the surface of the sphere
		 * 				(not above the landscape on the sphere)
		 * 			Vector.z : The relative position to this patch. 0 is the 
		 * 				top side, 1 is the bottom side, <0 or >1 for positions
		 * 				outside this patch 
		 */
		Vector3 getRelativePointInSphere(Vector3* point);
		
		/// The scale for the heightvalues
		Real mScaleHeight;
		/// The minimum radius of the planet
		Real mOffset;
		
		/**
		 *  The index of which cubemap the map is
		 * @invar (0 <= mMap) && (mMap <= 5)
		 */
		short mMap;
		
		/**
		 * Returns where above the standard-patch the given point is.
		 * The standardpatch is the patch through which the positive y-axis
		 * passes
		 * @param	Vector3* point
		 * 			The point to project onto the sphere.
		 * @return	Vector3
		 * 			Vector.x : The relative position to the standardpatch. 0 is the 
		 * 				left side, 1 is the right side, <0 or >1 for positions
		 * 				outside this patch 
		 * 			Vector.y : The height above the surface of the sphere
		 * 				(not above the landscape on the sphere)
		 * 			Vector.z : The relative position to the standardpatch. 0 is the 
		 * 				top side, 1 is the bottom side, <0 or >1 for positions
		 * 				outside this patch 
		 */
		Vector3 getVector(Vector3 point);
		Vector3 getVector(Real x, Real y, Real z);
		
		/**
		 * Creates a region of the given level and associates the correct
		 * HardwareVertexBuffer with it.
		 * @param	int level
		 * 			The level to be created
		 * @param	HardwareVertexBufferSharedPtr posVertexBuffer
		 * 			The hardwareVertexBuffer to write the geometrydata to
		 */
		void createRegion(int level, HardwareVertexBufferSharedPtr posVertexBuffer);

		/**
		 * Returns the width in number of quads in the given level
		 * @param	int level
		 * 			the level to return the number of quads from
		 * @return	int
		 * 			The width (and height) of the level in number of quads
		 */
		int getLevelSize(int level);
		
		/**
		 * Returns the depth of real (not synthesized) geometry of the planet
		 */
		int getDepth();
		
		/**
		 * Returns the size of the quads of the given level
		 * @param	int level
		 * 			the level to return the size of the quads from
		 * @return	Real
		 * 			The size of the quads
		 */
		Real getLevelQuadSize(int level);
		
		/**
		 * Returns the height of the given point on the given level
		 * @param	int x
		 * 			The x-coordinate
		 * @param	int y
		 * 			The y-coordinate
		 * @param	int level
		 * 			The level tot return the height from
		 * @return	Real
		 * 			The height of the given point
		 */
		Real getElevation(int x, int y, int level);

        /// Position of the planet in world coordinates
        Vector3	mWorld;
        /// Scaling info
        Vector3	mScale;
        
        /// A list of pointers to the other clipmappatches
        ClipmapPatch **otherPatches;
	};

}

#endif
