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

File: ClipmapRegion.h
Desc: A clipmregion. The level of detail with its functionality
		
Date: 2005/01/01

Author: Wim Lemkens

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

#ifndef CLIPMAPREGION_H
#define CLIPMAPREGION_H

#include "ClipmapConfig.h"
#include "SeperateCubemappedPlanetReader.h"
#include "Vector3ix.h"
#include "Vector2i.h"
#include <stdlib.h>

#include "OgreHardwareBufferManager.h"


using namespace std;

namespace Ogre {

class ClipmapRegion{

public:

	/**
	 * The constructor of the clipmapregion
	 * @param	Vector3ix upperLeftQuad
	 * 			The position of upper left corner of the clipregion in 
	 * 			coordinates relative to the map of the level of detail it 
	 * 			represents
	 * @param	Vector3ix lowerRightQuad
	 * 			The position of lower right corner of the clipregion in 
	 * 			coordinates relative to the map of the level of detail it 
	 * 			represents
	 * @param	Vector3ix innerUpperLeftQuad
	 * 			The position of upper left corner of the next (higherlevel) 
	 * 			clipregion in coordinates relative to the map of the level of 
	 * 			detail the current clipregion represents
	 * @param	Vector3ix innerLowerRightQuad
	 * 			The position of lower right corner of the next (higherlevel) 
	 * 			clipregion in coordinates relative to the map of the level of 
	 * 			detail the current clipregion represents
	 * @param	SeperateCubemappedPlanetReader* geometryReader
	 * 			The geometryreader that contains the vertex-data
	 * @param	HardwareVertexBufferSharedPtr &vertexBuffer
	 * 			The hardware-vertex-buffer the vertices are to be written to
	 * @param	ClipmapRegion* previousRegion
	 * 			The lower detail region surrounding this region
	 * @param	int map
	 * 			The side of the cubemap this region is on
	 * @param	int vertexOffset=0
	 * 			The position in the vertexbuffer the vertices of this region are
	 * 			going to be written to
	 * @post	The position of the clipregion will be defined
	 * @post	The region is marked as changed
	 * @post	The side of the cubemap this region is on is marked
	 * @post	The correct geometry-reader is assigned
	 * @post	The vertices
	 * @post	The correct offset in the vertexbuffer is registred
	 * @post	The lowerlevel region is registred
	 * @post	The region is marked as not having moved
	 */
	ClipmapRegion(Vector3ix upperLeftQuad,Vector3ix lowerRightQuad, 
				Vector3ix innerUpperLeftQuad, Vector3ix innerLowerRightQuad,
				SeperateCubemappedPlanetReader* geometryReader,
				HardwareVertexBufferSharedPtr &vertexBuffer,
				ClipmapRegion* previousRegion,
				int map,
				int vertexOffset=0);
	~ClipmapRegion();
	
	/**
	 * Moves the region by adding a number of lines to the region in a given 
	 * direction
	 * @param	int amount
	 * 			The number of lines to be added
	 * @param	int direction
	 * 			The direction the lines are to be added
	 * @post	If the amount is negative that number of lines will be 
	 * 			removed
	 * @post	If the region grows to big it will be automaticly cropped in a
	 * 			way that the region still moves in the given direction
	 */
	int addExteriorRows(int amount, int direction);
	
	/**
	 * Resizes the hole in the region by adding lines of geometry in a given 
	 * direction
	 * @param	int amount
	 * 			The number of lines to be added
	 * @param	int direction
	 * 			The direction the lines are to be added
	 * @post	If the amount is negative that number of lines will be 
	 * 			removed
	 */	
	void addInteriorRows(int amount, int direction);

	/**
	 * Resizes the region by removing a number of lines of geometry in a given 
	 * direction
	 * @param	int amount
	 * 			The number of lines to be removed
	 * @param	int direction
	 * 			The direction the lines are to be removed
	 * @post	If the amount is negative that number of lines will be 
	 * 			added
	 */
	void removeExteriorRows(int amount, int diretion);

	/**
	 * Resizes the hole in the region by removing lines of geometry in a given 
	 * direction
	 * @param	int amount
	 * 			The number of lines to be removed
	 * @param	int direction
	 * 			The direction the lines are to be removed
	 * @post	If the amount is negative that number of lines will be 
	 * 			added
	 */	
	void removeInteriorRows(int amount, int diretion);
	
	/// A flag marking if the region has changed and so the vertexbuffers have
	/// to be updated
	bool changed;
	
	/**
	 * Sets the geometryreader
	 */
	void setGeometryReader(SeperateCubemappedPlanetReader* geometryReader);
	
	/**
	 * Sets the vertexbuffer and the offset to write to
	 * @param	HardwareVertexBufferSharedPtr &vertexBuffer
	 * 			The vertexbuffer to write the vertices to
	 * @param	int patchOffset
	 * 			The offset in the bertexbuffer the vertices should be written to
	 */
	void setVertexBuffer(HardwareVertexBufferSharedPtr &vertexBuffer, int patchOffset);
	
	/**
	 * Sets the indexbuffer
	 */
	void setIndexBuffer(HardwareIndexBufferSharedPtr &indexBuffer);
	
	/**
	 * Update the vertexbuffer
	 * @post	All the vertexdata (without transitions )of this clipregion will 
	 * 			be written to the hardwarevertexbuffer on the correct offset. 
	 */
	void updateVertexBuffer();
	
	/**
	 * Calculate the indices and update them to the indexbuffer
	 * @param	The offset of vertices to start from
	 */
	int updateIndexBuffer(int offset);

	/**
	 * Add a column of vertices to the memory
	 * @post	The new column will overwrite an old column if the resulting 
	 * 			region would be to big
	 */
	void addCollumn(int offset,int startx,int starty);

	/**
	 * Add a row of vertices to the memory
	 * @post	The new row will overwrite an old row if the resulting 
	 * 			region would be to big
	 */
	void addRow(int offset,int startx,int starty);

	///	The hardwarevertexbuffer		
	HardwareVertexBufferSharedPtr vertexBuffer ;
	///	The hardwareindexbuffer		
	HardwareIndexBufferSharedPtr indexBuffer ;
	
	/**
	 * Sets the offset in the vertexbuffer the vertexdata of this region will
	 * be in
	 */
	void setVertexOffset(int vertexOffset);
	
	
	/**
	 * Returns the current number of indices for this region
	 */
	int getIndexCount();
	
	/**
	 * Returns an array of the indices of the geometry
	 */	
#if USE_16BIT_INDICES
	unsigned short* getIndices();
#else
	unsigned int* getIndices();
#endif

	/**
	 * Moves the region an amount in a given direction
	 * @param	int amount
	 * 			The amount to move the region
	 * @param	int direction
	 * 			The direction to move the region to
	 */
	int move(int amount, int direction);

	/**
	 * Returns if the region has a hole. (If it isn't the highest level 
	 * of detail displayed region)
	 */
	bool hasHole() const;

	/**
	 * Closes the hole
	 * @post	innerUpperLeftQuad = Vector3ix(centerx,centerz,innerUpperLeftQuad.z);
	 * @post	innerLowerRightQuad = Vector3ix(centerx-1,centerz-1,innerUpperLeftQuad.z);
	 * 
	 */
	void closeHole();
	
	///	2 variables containing the position the camera is the closest to
	int centerx, centerz;
	
	/**
	 * Returns if the region is visible
	 * 		The region is visible if it isn't too big (of too low detail) and it
	 * 		lays on the clipmap
	 */
	bool isVisible() const;
	
	/**
	 * Returns if the region is too big (of too low level of detail)
	 */
	bool isTooBig() const;
	
	/**
	 * Set the upperleft quad defining the hole in the region
	 */
	void setInnerUpperLeftQuad(Vector3ix innerUpperLeftQuad);
	/**
	 * Set the lowerright quad defining the hole in the region
	 */
	void setInnerLowerRightQuad(Vector3ix innerLowerRightQuad);
	
	/**
	 * Returns the upperleft quad defining the hole in the region
	 */
	const Vector3ix getInnerUpperLeftQuad() const;

	/**
	 * Returns the lowerright quad defining the hole in the region
	 */
	const Vector3ix getInnerLowerRightQuad() const;

	/**
	 * Returns the upperleft quad defining the region
	 */
	const Vector3ix getUpperLeftQuad() const;

	/**
	 * Returns the lowerright quad defining the region
	 */
	const Vector3ix getLowerRightQuad() const;

	/**
	 * Returns the size of the complete region.
	 * @return	The size of a region if it is smaller as the size of the
	 * 			level of detail the region belongs to
	 * @return	The size of the level of detail the region belongs to
	 */
	int getPreferredWidth() const;
	int getPreferredHeight() const;

	/**
	 * Returns the current size of the region
	 */
	int getWidth() const;
	int getHeight() const;
	
	/**
	 * Returns the level of detail the region belongs to
	 */
	int getLevel() const;
	
	/**
	 * Update the vertices to the new position
	 */
	void updateVertices();

	/**
	 * Update the vertices to the movement of the given amount in the given 
	 * direction
	 * @param	int amount
	 * 			The amount to move the region
	 * @param	int direction
	 * 			The direction to move the region in
	 */
	void updateVertices(int amount, int direction);
		
	/**
	 * Returns the size of the complete region.
	 * @return	The size of a region if it is smaller as the size of the
	 * 			level of detail the region belongs to
	 * @return	The size of the level of detail the region belongs to
	 */
	inline int getSize() const;
	
	/**
	 * Returns if the clipregion partly or wholy lays on the clipmap
	 */
	bool isOnPatch() const;

	/**
	 * Update the area of vertices in the center of the transitionzones from the
	 * RAM-vertexbuffer to the HW-vertexbuffer
	 */
	void refreshCenter();
	
	/**
	 * Update all vertices from the RAM-vertexbuffer to the HW-vertexbuffer
	 */
	void refreshAll();

	/**
	 * Returns if this clipregio has been cropped because it partly lays of the
	 * clipregion
	 */
	bool isOverlappingEdge(int direction);

	/**
	 * Set the array of regions
	 * @param	int index
	 * 			The side of the cubemap to register the regions of
	 * @param	std::vector<ClipmapRegion*> *regions
	 * 			A pointer to the vector of clipregions
	 */
	void setOtherRegions(int index, std::vector<ClipmapRegion*> *regions);
	
private:
	///	The offset of the vertexdata of this region in the data of the clipmap
	int vertexOffset;
	///	The offset of the vertexdata of the containing clipmap in all the 
	/// vertexdata
	int patchOffset;
	
	///	The geometry-reader to query the vertexdata from
	SeperateCubemappedPlanetReader* mGeometryReader;
	
	/// The factor the clipregion has to be compared to, to see if it will be
	/// shown, or is visualy to small to be shown
	long clipSize;
	
	/// The number of vertices and indices
	int vertexCount, indexCount;
	
	/// The RAM-vertexbuffer
	Real vertices[REGIONSURF*VERTEXSIZE];
	
	///	The RAM indexbuffer
	//max #indices = 
	//	all quads in the region REGIONSIZE*REGIONSIZE*6
	// 		(every quad exists out of 2 triangles with 3 vertices each (duhu)
	//	+ connection triangles on the border with the next level
	
	//		.---.
	//		|\ /|	<-this line should theoreticly be 0
	//		._._.
#if USE_16BIT_INDICES
	unsigned short indices[REGIONSIZE*REGIONSIZE*6+REGIONSIZE*3*4*3/2];
#else
	unsigned int indices[REGIONSIZE*REGIONSIZE*6+REGIONSIZE*3*4*3/2];
#endif

	/**
	 * Calculates a part of the indices of the region
	 * @param	unsigned short* &index
	 * 			unsigned int* &index
	 * 			The RAM-indexbuffer
	 */	 
	//AAABBDD
	//AAABBDD
	//AAA  DD
	//AAACCDD
	//AAACCDD
#if USE_16BIT_INDICES
	void calculateAll(unsigned short* &index);
	void calculateA(unsigned short* &index);
	void calculateB(unsigned short* &index);
	void calculateC(unsigned short* &index);
	void calculateD(unsigned short* &index);
#else
	void calculateAll(unsigned int* &index);
	void calculateA(unsigned int* &index);
	void calculateB(unsigned int* &index);
	void calculateC(unsigned int* &index);
	void calculateD(unsigned int* &index);
#endif

	///	The position in the RAM-vertexbuffer the region starts and the position
	/// it ends
	int startOffset, endOffset;
	
	/**
	 * Returns the position of a given vertex in the RAM-vertexbuffer
	 * @param	int x
	 * 			The x-coordinate relative to the region to get the position of
	 * @param	int y
	 * 			The y-coordinate relative to the region to get the position of
	 */
	int getVertexPosition(int x, int y) const;

	/**
	 * Start calculating the indices of the region
	 */
	void calculateIndices();

	/**
	 * initialize the RAM-vertexbuffer
	 */	
	void initializeVertices();
	
	/// The quad on the top left of the region
	Vector3ix upperLeftQuad;
	/// The quad on the lower right of the region
	Vector3ix lowerRightQuad;
	/// The quad left on top of the next interior region 
	Vector3ix innerUpperLeftQuad;
	/// The quad right down of the next interior region 
	Vector3ix innerLowerRightQuad;
	
	/**
	 * Returns the position in the level of detail the region is in of a vertex
	 * given the coordinates relative to this region.
	 * @see	Vector3 getRelativeCoordinates(int x, int y) const;
	 */
	Vector3i getVertexCoordinates(int x, int y) const;
	
	/// A pointer to the clipregion of one level of detail lower then this one
	ClipmapRegion* previousRegion;

	/**
	 * Update the transitionzones for a given direction and a given amount
	 */
	void updateTransition(int amount, int direction);
	
	/**
	 * Update the vertices for a given direction and a given amount, without a 
	 * transitionzone
	 */
	void updatePlain(int amount, int direction);
	
	/**
	 * Calculate the blendingfactor for a given point
	 * @param	int pos
	 * 			The x-coordinate of the point to blend. 
	 * 			The coordinate is relative to the level of detail the region is
	 * 			in
	 */
	inline Real calculateAlphaX(int pos);

	/**
	 * Calculate the blendingfactor for a given point
	 * @param	int pos
	 * 			The y-coordinate of the point to blend. 
	 * 			The coordinate is relative to the level of detail the region is
	 * 			in
	 */
	inline Real calculateAlphaY(int pos);
	
	/**
	 * Puts a vertex in the (RAM)vertexbuffer
	 * @param	int pos		
	 * 			The position in the buffer to write to
	 * @param	Vector3 vertex	
	 * 			The vertex-coordinates
	 * @param	Vector3 normal	
	 * 			The normal if applicable
	 * @param	Real* vertices	
	 * 			The vertexbuffer
	 */
	inline void setVertex(int pos, Vector3 vertex, Real* vertices);
	inline void setVertex(int pos, Vector3 vertex, Vector3 normal, Real* vertices);
	
	/**
	 * Copies a vertex from the RAM-vertexbuffer to the HW-vertexbuffer
	 * @param	int pos
	 * 			The position in the RAM buffer to copy from
	 * @param	Real* vertices
	 * 			The RAM buffer
	 */
	inline void commitVertex(int pos, Real* vertices);
	
	/**
	 * Blends a vertex with a vertex from the level of detail lower
	 * @param	int x	
	 * 			The x-coordinate relative to this region of the vertex to blend
	 * @param	int y	
	 * 			The y-coordinate relative to this region of the vertex to blend
	 * @param	Real alpha	
	 * 			The amount the vertex is to be blended
	 */
	Vector3 blendVertex(int x, int y, Real alpha);

	/**
	 * Blends a normal with a normal from the level of detail lower
	 * @param	int x	
	 * 			The x-coordinate relative to this region of the normal to blend
	 * @param	int y	
	 * 			The y-coordinate relative to this region of the normal to blend
	 * @param	Real alpha	
	 * 			The amount the normal is to be blended
	 */
	Vector3 blendNormal(int x, int y, Real alpha);
	
	/**
	 * Returns the position in this region of a vertex
	 * given the coordinates in the level of detail the region is in.
	 * @see	Vector3i getVertexCoordinates(int x, int y) const;
	 */
	Vector3 getRelativeCoordinates(int x, int y) const;
	
	/**
	 * Returns if the region has the exact size in the given direction
	 * (relative to the center)
	 */
	bool isCompleteInLeft();
	bool isCompleteInRight();
	bool isCompleteInUp();
	bool isCompleteInDown();

	/**
	 * Returns if the region is to big in the given direction
	 * (relative to the center)
	 */
	bool isTooBigOnLeft();
	bool isTooBigOnRight();
	bool isTooBigOnUp();
	bool isTooBigOnDown();
	
	/**
	 * Returns if the region is to small in the given direction
	 * (relative to the center)
	 */
	bool isTooSmallOnLeft();
	bool isTooSmallOnRight();
	bool isTooSmallOnUp();
	bool isTooSmallOnDown();

	/**
	 * Returns if the transitionregion is on this clipmap
	 */
	bool hasBorderRegionOnLeft();
	bool hasBorderRegionOnRight();
	bool hasBorderRegionOnUp();
	bool hasBorderRegionOnDown();

	/**
	 * Calculate the zero-area triangles on the given side of the region
	 * @param	unsigned short* &index
	 * 			unsigned int* &index
	 * 			The indexbuffer
	 */	
#if USE_16BIT_INDICES
	void calculateN(unsigned short* &index);
	void calculateE(unsigned short* &index);
	void calculateS(unsigned short* &index);
	void calculateW(unsigned short* &index);
#else
	void calculateN(unsigned int* &index);
	void calculateE(unsigned int* &index);
	void calculateS(unsigned int* &index);
	void calculateW(unsigned int* &index);
#endif

	/// The amount the region has moved in the x and y direction
	int xmove,ymove;

	/**
	 * Moves the region, but instead of adding line-by-line just re-initialise
	 * the region on the new position
	 */
	int moveAndReset(int amount, int direction);
	
	/**
	 * Returns the size of the given level
	 * @param	int level
	 * 			The level to return the size from
	 */
	int getLevelSize(int level) const ;
	
	/**
	 * Returns the vertex on the given coordinates (relative to the level of 
	 * detail)
	 */
	Vertex* getQuad(Vector3ix quad);
	
	///	The side of the cubemap this region is on
	int mMap;

	/**
	 * Create the transition region for the given edge of the clipregion in a 
	 * given range.
	 * @param	int left
	 * 			The left margin of the range (if applicable)
	 * @param	int right
	 * 			The right margin of the range (if applicable)
	 * @param	int top
	 * 			The top margin of the range (if applicable)
	 * @param	int bottom
	 * 			The bottom margin of the range (if applicable)
	 * @param	Real* vertices
	 * 			The RAM vertexbuffer where the base-vertices come from
	 * @param	int amountOver=0
	 * 			The amount the transitionregion is partly over the edge of the
	 * 			clipmap
	 * 			
	 */
	void blendN(int left, int right, Real* vertices, int amountOver=0);
	void blendS(int left, int right, Real* vertices, int amountOver=0);
	void blendW(int top, int bottom, Real* vertices, int amountOver=0);
	void blendE(int top, int bottom, Real* vertices, int amountOver=0);

	/**
	 * Create the transition region on the given corner of the clipregion 
	 * @param	Real* vertices
	 * 			The RAM vertexbuffer where the base-vertices come from
	 * @param	int amountOverX=0
	 * 			The amount the transitionregion is partly over the vertical edge
	 * 			of the clipmap
	 * @param	int amountOverY=0
	 * 			The amount the transitionregion is partly over the horizontal
	 * 			edge of the clipmap
	 */
	void blendNE(Real* vertices, int amountOverX=0, int amountOverY=0);
	void blendNW(Real* vertices, int amountOverX=0, int amountOverY=0);
	void blendSE(Real* vertices, int amountOverX=0, int amountOverY=0);
	void blendSW(Real* vertices, int amountOverX=0, int amountOverY=0);
	
	/**
	 * Commits a block of vertices (to RAMbuffer). The given coordinates are
	 * relative to the clipregion
	 * @param	int x1
	 * 			The left border of the block
	 * @param	int y1
	 * 			The upper border of the block
	 * @param	int x2
	 * 			The right border of the block
	 * @param	int y2
	 * 			The lower border of the block
	 * @param	Real* vertices
	 * 			The RAM vertexbuffer where the base-vertices come from
	 * @see		inline void commitVertex(int pos, Real* vertices);
	 */
	void commitBlock(int x1,int y1, int x2, int y2, Real* vertices);
	
	/**
	 * Commit the transitionregions (to RAMbuffer) on top and on the bottom of
	 * the clipregion in a given range
	 * @param	int start
	 * 			The left side of the range
	 * @param	int end
	 * 			The right side of the range
	 * @param	Real* vertices
	 * 			The RAM vertexbuffer where the base-vertices come from
	 */
	void commitVertical(int start, int end, Real* vertices);

	/**
	 * Commit the transitionregions (to RAMbuffer) on the left and the right side of the 
	 * clipregion in a given range
	 * @param	int start
	 * 			The top of the range
	 * @param	int end
	 * 			The bottom of the range
	 * @param	Real* vertices
	 * 			The RAM vertexbuffer where the base-vertices come from
	 */
	void commitHorizontal(int start, int end, Real* vertices);
	
	/**
	 * Commit the transitionsregions (to RAMbuffer) on the given side of the 
	 * clipregion for agiven movement
	 * @param	int amount
	 * 			the amount the region has moved
	 * @param	Real* vertices
	 * 			The RAM vertexbuffer where the base-vertices come from
	 */
	void commitLeft(int amount, Real* vertices);
	void commitRight(int amount, Real* vertices);
	void commitTop(int amount, Real* vertices);
	void commitBottom(int amount, Real* vertices);
	
	/**
	 * Calculate the transitionregions on the given side
	 * @param	Real* vertices
	 * 			The RAM vertexbuffer where the base-vertices come from
	 */
	void updateLeft(Real* vertices);
	void updateRight(Real* vertices);
	void updateTop(Real* vertices);
	void updateBottom(Real* vertices);
	
	/**
	 * Return the size of the transitionregion of the neighbouring region in the
	 * given direction. (The width that is left after cropping for 
	 * clipmapboundaries)
	 * @param	int direction
	 * 			The direction of the neighbouring region to get the 
	 * 			transitionsize of.
	 */
	int getAmountOverEdge(int direction);
	
	/**
	 * Return the size that is cropped of the transitionregion in the given
	 * direction. 
	 * @param	int direction
	 * 			The direction to get the cropped size of.
	 */	
	int getRealAmountOverEdge(int direction);

	///	An array pointing to the regions of all clipmaps
	std::vector<ClipmapRegion*> *otherRegions[6];
	
	/**
	 * Return the index of the neighbour in the given direction
	 * @param	int direction
	 * 			The direction 
	 */
	int getNeighbour(int direction);

	/**
	 * Return in which direction, relative to the neighbour, in the given 
	 * direction, this clipmap is.
	 * @param	int direction
	 * 			The direction of the neighbour to get the index from
	 */
	int getNeighbourDirection(int direction);

};

}
	
#endif // CLIPMAPREGION_H
