#if !defined(HFSLIMMZ_HPP)
	#define HFSLIMMZ_HPP

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

	namespace ElementRenderer {
		class GridElement;
		class GroupElement;
		class ListElement;
	}

	extern bool doWater;
	extern Scalar waterLevel;
	extern bool doBSP;
	extern bool doPrecise;
	extern bool doInterest;
	extern bool doTileCap;
	extern bool doTileCapYovin;
	extern bool doDensityMap;
	extern bool silentMode;

	struct TriangleAbstract
	{
		TriangleAbstract():
			plane(
				Normal3D::Down,
				0.0f
			)
			{ center = Point3D::Identity; area = 0.0f; centerValid = false; }


		bool centerValid;
		Point3D center;
		Scalar	area;
		Plane plane;

		virtual Point3D &GetPoint(int) = 0;
		bool GetSurfaceAreaAndCentroid();
	};

//	===================== not so precise ===========================

	struct ToDrawTriangle :
		public TriangleAbstract
	{
		ToDrawTriangle() : TriangleAbstract()
			{ v[0] = Point3D::Identity; v[1] = Point3D::Identity; v[2] = 
Point3D::Identity; }

		Point3D v[3];

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

		ToDrawTriangle&
			operator= (const ToDrawTriangle& tdt)
		{
			v[0] = tdt.v[0]; v[1] = tdt.v[1]; v[2] = tdt.v[2];
			center = tdt.center; area = tdt.area; plane = tdt.plane; centerValid = 
tdt.centerValid;
			return *this;
		}

		Point3D &GetPoint(int i) { return v[i]; }
	};

//	========================== Precision ===========================

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

	struct TriangleStruct;

	struct TrianglePointAngleStruct {
		TrianglePointAngleStruct() { tri = NULL; angle = 0.0f; pos = -1; }

		TriangleStruct *tri;
		float angle;
		int pos;
	};

	struct PointStruct {
		PointStruct() : myTri(16) { p = Point3D::Identity; connectedTo = 0; }
		Point3D p;

		int connectedTo;
		DynamicArrayOf<TrianglePointAngleStruct> myTri;

		void Add (TriangleStruct *ts, int pos);

		void CalculateAngles();

		bool
			CheckAngles(float xmin, float zmin, float xmax, float zmax, float dX, float dZ);
		bool
			CheckAngles();	//	checks for adjecent triangles with angle of close to 180

		float
			GetAngles();
		float
			GetExpectedAngles(float xmin, float zmin, float xmax, float zmax);

		void
   			TestInstance() const;
	};

	struct TriangleStruct :
		public TriangleAbstract
	{
		TriangleStruct() : TriangleAbstract()
			{ v[0] = 0; v[1] = 0; v[2] = 0; }

		static PointStruct *points;

		int v[3];

		bool	GetSurfaceAreaAndCentroid()
		{
			Verify(points!=NULL);
			if(v[0]==v[1] || v[0]==v[2] || v[1]==v[2])
			{
				center = GetPoint(0);
				centerValid = true;
				area = 0.0f;
				return false;
			}

			return TriangleAbstract::GetSurfaceAreaAndCentroid();
		}

		Point3D	&GetPoint(int i) { Verify(points!=NULL); return points[v[i]].p; }
	};


//======================= Water ===================================

	struct WaterEdge
	{
		int p[2];
		Normal3D normal;

		bool inside[2];
		int used;

		WaterEdge()
			{ p[0] = -1; p[1] = -1; normal = Normal3D::Down; inside[0] = false;  
inside[1] = false; used = 0; }
	};

	struct WaterEdgePoint
	{
		Stuff::Point3D p;
		int toEdge[8];
		int toEdgeCount;
		int used;
		int onEdge;

		WaterEdgePoint()
			{ toEdge[0] = -1; toEdge[1] = -1; used = 0; onEdge = 0; toEdgeCount = 0;}

		WaterEdgePoint&
			operator=(const WaterEdgePoint& wep)
				{	p = wep.p; toEdge[0] = wep.toEdge[0]; toEdge[1] = wep.toEdge[1];
					used = wep.used; onEdge = wep.onEdge; toEdgeCount = wep.toEdgeCount; 
return *this; }
	};

	struct WaterPools
	{
		Stuff::DynamicArrayOf<WaterEdge> waterEdge;
		Stuff::DynamicArrayOf<WaterEdgePoint> waterEdgePoints;

		int waterEdgeCount;
		int waterEdgePointCount;

		Stuff::Scalar x0, x1, z0, z1, waterLevel;

		WaterPools(int nr, Scalar wl) :
			waterEdge(nr), waterEdgePoints(2*nr)
				{ waterEdgeCount = 0; waterEdgePointCount = 0; waterLevel = wl; }

		void
			AddEdge(Stuff::Point3D& p0, Stuff::Point3D& p1, const Stuff::Normal3D& 
n);

		MidLevelRenderer::MLRPrimitiveBase*
			LinkEdges();

		void
			SetTileEdges(Stuff::Scalar _x0, Stuff::Scalar _x1, Stuff::Scalar _z0, 
Stuff::Scalar _z1)
				{x0 = _x0; x1 = _x1; z0 = _z0; z1 = _z1; }

		int
			OnWhatEdge(WaterEdgePoint& wep);
		int
			OnWhatEdge(Stuff::Point3D& wep);
	};

//	============================= Naturals ==========================

	struct CulturalLoc {
		Stuff::Point3D loc;
		BYTE yaw;
		BYTE nr;
	};

//	======================== Height field slim ======================

	class HFSlimMZ {
	public:
		HFSlimMZ();

		~HFSlimMZ();

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

		void
			FillInterestDataFromImage(Image &image);

		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,
				const char *altname,
				Scalar xOff, Scalar zOff,
				Scalar xFac, Scalar zFac,
				Scalar dStart, Scalar dEnd,
				MLRState::AlphaMode alphaMode
			)
			{
				detailInfo.name = name;
				detailInfo.altname = altname;
				detailInfo.xOff = xOff;
				detailInfo.zOff = zOff;
				detailInfo.xFac = xFac;
				detailInfo.zFac = zFac;
				detailInfo.dStart = dStart;
				detailInfo.dEnd = dEnd;
				detailInfo.alphaMode = alphaMode;
			}

		void
			SetWaterDetailTextureInfo(
				const char *name,
				Scalar xOff, Scalar zOff,
				Scalar xFac, Scalar zFac,
				Scalar dStart, Scalar dEnd,
				MLRState::AlphaMode alphaMode,
				int movieFrames
			)
			{
				waterDetailInfo.name = name;
				waterDetailInfo.altname = NULL;
				waterDetailInfo.xOff = xOff;
				waterDetailInfo.zOff = zOff;
				waterDetailInfo.xFac = xFac;
				waterDetailInfo.zFac = zFac;
				waterDetailInfo.dStart = dStart;
				waterDetailInfo.dEnd = dEnd;
				waterDetailInfo.alphaMode = alphaMode;
				waterDetailInfo.movieFrames = movieFrames;
			}

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

		int GetOptPolyCount() {return OptPolyCount; }

		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 x*dX; }

		Scalar
			GetZ(int z)
					{ return z*dZ; }

		Scalar
			GetX(Scalar x)
				{ return x; }

		Scalar
			GetZ(Scalar z)
					{ return z; }

		Scalar
			GetHeight(int, int);

		Stuff::ExtentBox
			GetExtents();

		void
			GetZoneDimensions(int *xd, int *zd)
				{ *xd = xDim; *zd = zDim; }

		Stuff::DynamicArrayOf<ElementRenderer::GridElement*>*
			GetTerrainArray()
				{ return &simpleTerrainGroup; }

		Stuff::DynamicArrayOf<ElementRenderer::GroupElement*>*
			GetWaterArray()
				{ return &waterGroup; }

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

		bool
			SetMegaTexture(
				int nrOfLevels,
				int bucketSize,
				const char *megaBaseName,
				const char *output
			);

		void
			SetWaterInfo(const char *name, Scalar ratio, int movieFrames)
				{ waterTextureName = name;  waterTextureRatio = ratio; waterMovieFrames = movieFrames; }

		void
			OptimizeHField(int depth=0, int binSize=64, const char *name=NULL);

		Stuff::Scalar
			GetOffset()
				{ return Offset; };

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

		void
			SetCenter(Stuff::Scalar xo, Stuff::Scalar zo)
				{ Check_Object(this); xCenterOffset = xo; zCenterOffset = zo; }

		Stuff::Scalar
			GetCenterX()
				{ Check_Object(this); return xCenterOffset; }
		Stuff::Scalar
			GetCenterZ()
				{ Check_Object(this); return zCenterOffset; }

		void
			SetCulturals(CulturalLoc*, int);

		void
			SetCulturalFadeValues(Stuff::Scalar fadeIn, Stuff::Scalar fadeOut)
				{ Check_Object(this); culturalFadeIn = fadeIn; culturalFadeOut = 
fadeOut; }

	protected:
		void
			Blur2D(int);

		typedef Stuff::Scalar frameArray[8][4];

		bool
			CreateMesh(
				ElementRenderer::ListElement *parent,
				int index,
				int levDiff,
				DynamicArrayOf<TriangleAbstract*>& tdtrilist,
				frameArray *frame,
				MidLevelRenderer::MLRState *state = NULL
			);

		bool
			CreateMesh2(
				ElementRenderer::ListElement *parent,
				int index,
				int levDiff,
				DynamicArrayOf<TriangleAbstract*>& tdtrilist,
				frameArray *frame,
				MidLevelRenderer::MLRState *state = NULL,
				bool uw = false
			);

		MLRPrimitiveBase*
			CreateWater(
				Scalar x0, Scalar x1,
				Scalar z0, Scalar z1,
				Scalar waterLevel
			);

		bool
			BinSort(
				ElementRenderer::ListElement *parent,
				int index,
				int levDiff,
				DynamicArrayOf<TriangleAbstract*>& tdtrilist,
				int binSize,
				frameArray *frame,
				MidLevelRenderer::MLRState *state = NULL
			);

		bool
			BinSort2(
				ElementRenderer::ListElement *parent,
				int index,
				int levDiff,
				DynamicArrayOf<TriangleAbstract*>& tdtrilist,
				int binSize,
				frameArray *frame,
				MidLevelRenderer::MLRState *state = NULL,
				bool uw = false
			);

		void
			BuildTile(
				ElementRenderer::ListElement *,
				ElementRenderer::GroupElement *,
				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
			BuildTile(
				DynamicArrayOf<TriangleStruct*>& tempTrianglesPtr,
				int index,
				int depth, int maxdepth,
				int numOfTriangles,
				DynamicArrayOf<unsigned char>& onOffPoints,
				int maxNumOfTrianglesPerMesh,
				Scalar xOffset,
				Scalar zOffset,
				Scalar xGrid,
				Scalar zGrid,
				int tileX,
				int tileZ
			);

		void
			CreateTile(
				ElementRenderer::ListElement *list,
				ElementRenderer::GroupElement *waterList,
				DynamicArrayOf<TriangleStruct*>& tempTrianglesPtr,
				int index,
				int depth, int maxdepth,
				int maxNumOfTrianglesPerMesh,
				Scalar xOffset,
				Scalar zOffset,
				Scalar xGrid,
				Scalar zGrid,
				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, Xoffset, Zoffset, visHeight;

		Stuff::Scalar dX, dY, dZ;
		int OptPolyCount;
		Stuff::DynamicArrayOf<float> field;

		int side, meshX, meshZ;

		Stuff::DynamicArrayOf<ElementRenderer::GridElement*> simpleTerrainGroup;
		Stuff::DynamicArrayOf<ElementRenderer::GroupElement*> waterGroup;

		Stuff::DynamicArrayOf<MidLevelRenderer::MLRShape*> terrain;

		MidLevelRenderer::MLRState state0, state1, state2;

		MidLevelRenderer::DetailTextureInfo detailInfo, waterDetailInfo;

		const char *waterTextureName;
		Scalar waterTextureRatio;
		int waterMovieFrames;
		int waterDetailMovieFrames;

		Scalar borderPixelFun;
		unsigned char maxAllDepth;

		Stuff::Scalar xCenterOffset, zCenterOffset;

		Stuff::Scalar
			culturalFadeIn, culturalFadeOut;

		int
			xDim, zDim;

	private:
		Stuff::DynamicArrayOf<BYTE> interestData;
		DirectMap<real> *map;
		GreedySubdivision *mesh;
		Stuff::Scalar Offset;
	};
#endif

