#if !defined(HFSLIM_HPP)
	#define HFSLIM_HPP

	#include "ImageLib\image.h"
	#include "terra\terra.hpp"

	struct DetailLevelInfo {
		unsigned short width, height;
		unsigned char *array;
	};

#define GRID_TEST

	struct ToDrawTriangle
	{
		Point3D v[3];

		Point3D center;
		Scalar	area;

		void	SetPoint(int idx,float x,float y,float z);
		void	GetSurfaceAreaAndCentroid();

		Point3D	&GetV0() { return v[0]; }
		Point3D	&GetV1() { return v[1]; }
		Point3D	&GetV2() { return v[2]; }
	};
	
	class HFSlim {
	public:
		HFSlim();

		~HFSlim();

		void
			FillFromImage(Image &image, int smooth=3);

		void
   			TestInstance() const
				{}
	
		void
			SetScale (Stuff::Scalar xs, Stuff::Scalar ys, Stuff::Scalar zs)
				{ Check_Object(this); dX = xs; dY = ys; dZ = zs; }

		void
			SetDetailTextureInfo(
				const char *name,
				Scalar xOff, Scalar zOff,
				Scalar xFac, Scalar zFac,
				Scalar dStart, Scalar dEnd,
				MLRState::AlphaMode alphaMode
			) 
			{
				detailInfo.name = name;
				detailInfo.xOff = xOff;
				detailInfo.zOff = zOff;
				detailInfo.xFac = xFac;
				detailInfo.zFac = zFac;
				detailInfo.dStart = dStart;
				detailInfo.dEnd = dEnd;
				detailInfo.alphaMode = alphaMode;
			}

		void 
			SetBorderPixels(int bp)
				{ borderPixelFun = bp/256.0f; }

		int GetFullPolyCount() {return (X-1)*(Z-1)*2; }
		int GetOptPolyCount() {return OptPolyCount; }


		ElementRenderer::GroupElement*
			GetGroup()
					{ return terrainGroup; }
		
		Scalar
			GetXinM()
					{ return (X-1) * dX; }
		
		Scalar
			GetZinM()
					{ return (Z-1) * dZ; }
		
		int
			GetX()
					{ return X; }
		
		int
			GetZ()
					{ return Z; }
		
		Stuff::Scalar
			GetXScale()
					{ return dX; }
		
		Stuff::Scalar
			GetZScale()
					{ return dZ; }
		
		Stuff::Scalar
			GetYScale()
					{ return dY; }
	
		Scalar
			GetX(int x)
				{ return xOffset + x*dX; }
		
		Scalar
			GetZ(int z)
					{ return zOffset + z*dZ; }

		Scalar
			GetX(Scalar x)
				{ return xOffset + x; }
		
		Scalar
			GetZ(Scalar z)
					{ return zOffset + z; }

		Scalar
			GetHeight(int, int);
		
		Stuff::ExtentBox
			GetExtents();
			
		int
			GetNrOfIZs()
				{ return nrOfIZs; };

		void
			SetOffset(Stuff::Scalar offset)
				{ Offset = offset; };

		bool
			SetMegaTexture(
				int nrOfLevels,
				int bucketSize,
				const char *mega_base_name,
				DetailLevelInfo *dli
			);

		void
			OptimizeHField(int depth=0, int binSize=64);
		
		Stuff::Scalar 
			GetOffset()
				{ return Offset; };

		bool
			IsFullDisplayed()
		{
			return 	(terrainGroup->FindElement(fullTerrainGroup->GetName())!=NULL);
		} 

		bool
			IsOptDisplayed() 
		{
			return 	(terrainGroup->FindElement(simpleTerrainGroup->GetName())!=NULL);
		} 
		
		void
			DisplayFull(bool bl);
		void
			DisplayOpt(bool bl); 

		ElementRenderer::GroupElement *
			GetFullTerrain() { Check_Pointer(this); return fullTerrainGroup; }
#ifdef GRID_TEST
		ElementRenderer::GridElement *
#else
		ElementRenderer::ListElement *
#endif
			GetOptTerrain() { Check_Pointer(this); return simpleTerrainGroup; }

		void
			SetAllDepth(int d)
				{ maxAllDepth = d; }

		void
			SetOffset(Stuff::Scalar xo, Stuff::Scalar zo)
				{ Check_Object(this); xOffset = xo; zOffset = zo; }

	protected:
		void
			Blur2D(int);

		bool
			CreateMesh(
				ListElement *parent,
				int index,
				int levDiff,
				DynamicArrayOf<ToDrawTriangle*>& tdtrilist,
				Stuff::ExtentBox*,
				MidLevelRenderer::MLRState *state = NULL
			);

		bool
			BinSort(
				ListElement *parent,
				int index,
				int levDiff,
				DynamicArrayOf<ToDrawTriangle*>& tdtrilist,
				int binSize,
				Stuff::ExtentBox*,
				MidLevelRenderer::MLRState *state = NULL
			);

		void
			BuildTile(
				ElementRenderer::ListElement *,
				int index,
				int depth, int maxdepth,
				DynamicArrayOf<unsigned char>& onOffPoints,
				int maxNumOfTrianglesPerMesh,
				Scalar xOffset,
				Scalar zOffset,
				Scalar xGrid,
				Scalar zGrid,
				int tileX,
				int tileZ,
				const char *texRoot
			);

		void
			DescentToUnderMountain(
				int depth,
#ifdef GRID_TEST
				ElementRenderer::GridElement *group,
#else
				ElementRenderer::ListElement *group,
#endif
				DynamicArrayOf<unsigned char>& onOffPoints,
				int maxNumOfTrianglesPerMesh,
				Scalar x0,
				Scalar z0,
				Scalar x1,
				Scalar z1,
				int tileX,
				int tileZ,
				const char *texRoot
			);

		DynamicArrayOf<ToDrawTriangle*> toSortTriangles;
		DynamicArrayOf<ToDrawTriangle>  tdtrilist;

		// X, Z number of points, the length of the terrain would be (X-1)*dX
		int X, Z, visHeight;
		Stuff::Scalar dX, dY, dZ;
		int OptPolyCount;
		Stuff::DynamicArrayOf<float> field;
		
		int side, meshX, meshZ;


		ElementRenderer::GroupElement *terrainGroup;
		ElementRenderer::GroupElement *fullTerrainGroup;

#ifdef GRID_TEST
		ElementRenderer::GridElement *simpleTerrainGroup;
#else
		ElementRenderer::ListElement *simpleTerrainGroup;
#endif
		Stuff::DynamicArrayOf<MidLevelRenderer::MLRShape*> terrain;
		
		MidLevelRenderer::MLRState state0, state1;

		MidLevelRenderer::DetailTextureInfo detailInfo;

		Scalar borderPixelFun;
		unsigned char maxAllDepth;
		Scalar xOffset, zOffset;

		int
			nrOfIZs;

	private:
		Stuff::Scalar Offset;
	};
#endif
