/****************************************************************************
* VCGLib                                                            o o     *
* Visual and Computer Graphics Library                            o     o   *
*                                                                _   O  _   *
* Copyright(C) 2004-2016                                           \/)\/    *
* Visual Computing Lab                                            /\/|      *
* ISTI - Italian National Research Council                           |      *
*                                                                    \      *
* All rights reserved.                                                      *
*                                                                           *
* This program is free software; you can redistribute it and/or modify      *
* it under the terms of the GNU General Public License as published by      *
* the Free Software Foundation; either version 2 of the License, or         *
* (at your option) any later version.                                       *
*                                                                           *
* This program is distributed in the hope that it will be useful,           *
* but WITHOUT ANY WARRANTY; without even the implied warranty of            *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the             *
* GNU General Public License (http://www.gnu.org/licenses/gpl.txt)          *
* for more details.                                                         *
*                                                                           *
****************************************************************************/

#ifndef __VCG_GLTRIMESH
#define __VCG_GLTRIMESH

#include <queue>
#include <vector>
#include <map>
#include <algorithm>
#include <iostream>

//#include <GL/glew.h>
#include <wrap/gl/space.h>
#include <wrap/gl/math.h>
#include <vcg/space/color4.h>
#include <wrap/gl/gl_type_name.h>

namespace vcg 
{
	//template <> GL_TYPE::SCALAR<double>() { return GL_DOUBLE; }

	// classe base di glwrap usata solo per poter usare i vari drawmode, normalmode senza dover
	// specificare tutto il tipo (a volte lunghissimo)
	// della particolare classe glwrap usata.
	class GLW
	{
	public:
		enum DrawMode	{DMNone, DMBox, DMPoints, DMWire, DMHidden, DMFlat, DMSmooth, DMFlatWire, DMRadar, DMLast} ;
		enum NormalMode	{NMNone, NMPerVert, NMPerFace, NMPerWedge, NMLast};
		enum ColorMode	{CMNone, CMPerMesh, CMPerFace, CMPerVert, CMLast};
		enum TextureMode{TMNone, TMPerVert, TMPerWedge, TMPerWedgeMulti};
		enum Hint {
			HNUseTriStrip		  = 0x0001,				// ha bisogno che ci sia la fftopology gia calcolata!
			//		HNUseEdgeStrip		  = 0x0002,			//
			HNUseDisplayList	  = 0x0004,
			HNCacheDisplayList	  = 0x0008,		// Each mode has its dl;
			HNLazyDisplayList	  = 0x0010,			// Display list are generated only when requested
			HNIsTwoManifold		  = 0x0020,			// There is no need to make DetachComplex before .
			HNUsePerWedgeNormal	  = 0x0040,		//
			HNHasFFTopology       = 0x0080,		// E' l'utente che si preoccupa di tenere aggiornata la topologia ff
			HNHasVFTopology       = 0x0100,		// E' l'utente che si preoccupa di tenere aggiornata la topologia vf
			HNHasVertNormal       = 0x0200,		// E' l'utente che si preoccupa di tenere aggiornata le normali per faccia
			HNHasFaceNormal       = 0x0400,		// E' l'utente che si preoccupa di tenere aggiornata le normali per vertice
			HNUseVArray           = 0x0800,
			HNUseLazyEdgeStrip	  = 0x1000,		// Edge Strip are generated only when requested
			HNUseVBO              = 0x2000,		// Use Vertex Buffer Object
			HNIsPolygonal         = 0x4000    // In wireframe modes, hide faux edges
		};

		enum Change {
			CHVertex		= 0x01,
			CHNormal		= 0x02,
			CHColor			= 0x04,
			CHFace			= 0x08,
			CHFaceNormal	= 0x10,
			CHRender        = 0x20,
			CHAll			= 0xff
		};
		enum HintParami {
			HNPDisplayListSize =0,
			HNPPointDistanceAttenuation =1,
			HNPPointSmooth = 2
		};
		enum HintParamf {
			HNPCreaseAngle =0,	// crease angle in radians
			HNPZTwist = 1,				// Z offset used in Flatwire and hiddenline modality
			HNPPointSize = 2		// the point size used in point rendering
		};

		template<class MeshType>
		class VertToSplit
		{
		public:
			typename MeshType::face_base_pointer f;
			char z;
			char edge;
			bool newp;
			typename MeshType::vertex_pointer v;
		};

		// GL Array Elemet
		class GLAElem {
		public :
			int glmode;
			int len;
			int start;
		};

		std::vector<unsigned int> TMId;
	};

	template <class MeshType,  bool partial = false , class FACE_POINTER_CONTAINER = std::vector<typename MeshType::FacePointer> >
	class GlTrimesh : public GLW
	{
	public:

		typedef typename MeshType::VertexType VertexType;
		typedef typename MeshType::FaceType FaceType;
		typedef typename MeshType::VertexType::CoordType CoordType;
		typedef typename MeshType::VertexType::ScalarType ScalarType;
		typedef typename MeshType::VertexIterator VertexIterator;
		typedef typename MeshType::EdgeIterator EdgeIterator;
		typedef typename MeshType::FaceIterator FaceIterator;

		FACE_POINTER_CONTAINER face_pointers;

		MeshType *m;
		unsigned int array_buffers[3];

		int curr_hints;      // the current hints

		// The parameters of hints
		int   HNParami[8];
		float HNParamf[8];

		DrawMode cdm; // Current DrawMode
		NormalMode cnm; // Current NormalMode
		ColorMode ccm; // Current ColorMode

		static NormalMode convertDrawModeToNormalMode(DrawMode dm)
		{
			switch(dm)
			{
			case(DMFlat):
			case(DMFlatWire):
			case(DMRadar):
				return NMPerFace;
			case(DMPoints):
			case(DMWire):
			case(DMSmooth):
				return NMPerVert;
			default:
				return NMNone;
			}
			return NMNone;
		}

		GlTrimesh()
		{
			m=0;
			dl=0xffffffff;
			curr_hints=HNUseLazyEdgeStrip;
			cdm=DMNone;
			ccm=CMNone;
			cnm=NMNone;

			SetHintParamf(HNPCreaseAngle,float(M_PI/5));
			SetHintParamf(HNPZTwist,0.00005f);
			SetHintParamf(HNPPointSize,1.0f);
			SetHintParami(HNPPointDistanceAttenuation, 1);
			SetHintParami(HNPPointSmooth, 0);
		}

		~GlTrimesh()
		{
			//Delete the VBOs
			if(curr_hints&HNUseVBO)
			{
				for(int i=0;i<3;++i)
					if(glIsBuffer(GLuint(array_buffers[i])))
						glDeleteBuffersARB(1, (GLuint *)(array_buffers+i));
			}
		}

		unsigned int dl;
		std::vector<unsigned int> indices;

		void SetHintParami(const HintParami hip, const int value)
		{
			HNParami[hip]=value;
		}
		int GetHintParami(const HintParami hip) const
		{
			return HNParami[hip];
		}
		void SetHintParamf(const HintParamf hip, const float value)
		{
			HNParamf[hip]=value;
		}
		float GetHintParamf(const HintParamf hip) const
		{
			return HNParamf[hip];
		}
		void SetHint(Hint hn)
		{
			curr_hints |= hn;
		}
		void ClearHint(Hint hn)
		{
			curr_hints&=(~hn);
		}

		void Update(/*Change c=CHAll*/)
		{
			if(m==0) return;

			if(curr_hints&HNUseVArray || curr_hints&HNUseVBO)
			{
				indices.clear();
				for(FaceIterator fi = m->face.begin(); fi != m->face.end(); ++fi)
				{
					indices.push_back((unsigned int)((*fi).V(0) - &(*m->vert.begin())));
					indices.push_back((unsigned int)((*fi).V(1) - &(*m->vert.begin())));
					indices.push_back((unsigned int)((*fi).V(2) - &(*m->vert.begin())));
				}

				if(curr_hints&HNUseVBO)
				{
					if(!glIsBuffer(array_buffers[1]))
						glGenBuffers(2,(GLuint*)array_buffers);
					glBindBuffer(GL_ARRAY_BUFFER,array_buffers[0]);
					glBufferData(GL_ARRAY_BUFFER_ARB, m->vn * sizeof(VertexType),
						(char *)&(m->vert[0].P()), GL_STATIC_DRAW_ARB);

					glBindBuffer(GL_ARRAY_BUFFER,array_buffers[1]);
					glBufferData(GL_ARRAY_BUFFER_ARB, m->vn * sizeof(VertexType),
						(char *)&(m->vert[0].N()), GL_STATIC_DRAW_ARB);
				}

				glVertexPointer(3,GL_TYPE_NM<ScalarType>::SCALAR(),sizeof(VertexType),0);
				glNormalPointer(GL_TYPE_NM<ScalarType>::SCALAR(),sizeof(VertexType),0);
			}

			//int C=c;
			//if((C&CHVertex) || (C&CHFace)) {
			//	ComputeBBox(*m);
			//	if(!(curr_hints&HNHasFaceNormal)) m->ComputeFaceNormal();
			//	if(!(curr_hints&HNHasVertNormal)) m->ComputeVertexNormal();
			//	C= (C | CHFaceNormal);
			//}
			//if((C&CHFace) && (curr_hints&HNUseEdgeStrip)) 		 ComputeEdges();
			//if((C&CHFace) && (curr_hints&HNUseLazyEdgeStrip)) ClearEdges();
			//if(MeshType::HasFFTopology())
			//	if((C&CHFace) && (curr_hints&HNUseTriStrip)) 		{
			//			if(!(curr_hints&HNHasFFTopology)) m->FFTopology();
			//			ComputeTriStrip();
			//		}
			//if((C&CHFaceNormal) && (curr_hints&HNUsePerWedgeNormal))		{
			//	  if(!(curr_hints&HNHasVFTopology)) m->VFTopology();
			//		CreaseWN(*m,MeshType::scalar_type(GetHintParamf(HNPCreaseAngle)));
			//}
			//if(C!=0) { // force the recomputation of display list
			//	cdm=DMNone;
			//	ccm=CMNone;
			//	cnm=NMNone;
			//}
			//if((curr_hints&HNUseVArray) && (curr_hints&HNUseTriStrip))
			//	{
			//	 ConvertTriStrip<MeshType>(*m,TStrip,TStripF,TStripVED,TStripVEI);
			//	}
		}

		void Draw(DrawMode dm ,ColorMode cm, TextureMode tm)
		{
			switch(dm)
			{
			case	DMNone    : Draw<DMNone    >(cm,tm); break;
			case	DMBox     : Draw<DMBox     >(cm,tm); break;
			case	DMPoints  : Draw<DMPoints  >(cm,tm); break;
			case	DMWire    : Draw<DMWire    >(cm,tm); break;
			case	DMHidden  : Draw<DMHidden  >(cm,tm); break;
			case	DMFlat    : Draw<DMFlat    >(cm,tm); break;
			case	DMSmooth  : Draw<DMSmooth  >(cm,tm); break;
			case	DMFlatWire: Draw<DMFlatWire>(cm,tm); break;
			default : break;
			}
		}

		template< DrawMode dm >
		void Draw(ColorMode cm, TextureMode tm)
		{
			switch(cm)
			{
			case	CMNone    : Draw<dm,CMNone   >(tm); break;
			case	CMPerMesh : Draw<dm,CMPerMesh>(tm); break;
			case	CMPerFace : Draw<dm,CMPerFace>(tm); break;
			case	CMPerVert : Draw<dm,CMPerVert>(tm); break;
			default : break;
			}
		}

		template< DrawMode dm, ColorMode cm >
		void Draw(TextureMode tm)
		{
			switch(tm)
			{
			case	TMNone          : Draw<dm,cm,TMNone          >(); break;
			case	TMPerVert       : Draw<dm,cm,TMPerVert       >(); break;
			case	TMPerWedge      : Draw<dm,cm,TMPerWedge      >(); break;
			case	TMPerWedgeMulti : Draw<dm,cm,TMPerWedgeMulti >(); break;
			default : break;
			}
		}

		template< DrawMode dm, ColorMode cm, TextureMode tm>
		void Draw()
		{
			if(!m) return;
			if((curr_hints & HNUseDisplayList)){
				if (cdm==dm && ccm==cm){
					glCallList(dl);
					return;
				}
				else {
					if(dl==0xffffffff) dl=glGenLists(1);
					glNewList(dl,GL_COMPILE);
				}
			}

			glPushMatrix();
			switch(dm)
			{
			case DMNone		  : break;
			case DMBox		  : DrawBBox(cm);break;
			case DMPoints   : DrawPoints<NMPerVert,cm>();break;
			case DMHidden		:	DrawHidden();break;
			case DMFlat			:	DrawFill<NMPerFace,cm,tm>();break;
			case DMFlatWire :	DrawFlatWire<NMPerFace,cm,tm>();break;
			case DMRadar		:	DrawRadar<NMPerFace,cm>();break;
			case DMWire		  :	DrawWire<NMPerVert,cm>();break;
			case DMSmooth   : DrawFill<NMPerVert,cm,tm>();break;
			default : break;
			}
			glPopMatrix();

			if((curr_hints & HNUseDisplayList)){
				cdm=dm;
				ccm=cm;
				glEndList();
				glCallList(dl);
			}
		}


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


		template <NormalMode nm, ColorMode cm, TextureMode tm>
		void DrawFill()
		{
			if(m->fn==0) return;

			if(cm == CMPerMesh)
				glColor(m->C());

			if(tm == TMPerWedge || tm == TMPerWedgeMulti )
				glDisable(GL_TEXTURE_2D);

			if(curr_hints&HNUseVBO)
			{
				if( (cm==CMNone) || (cm==CMPerMesh) )
				{
					if (nm==NMPerVert)
						glEnableClientState (GL_NORMAL_ARRAY);
					glEnableClientState (GL_VERTEX_ARRAY);

					if (nm==NMPerVert)
					{
						glBindBuffer(GL_ARRAY_BUFFER,array_buffers[1]);
						glNormalPointer(GL_TYPE_NM<ScalarType>::SCALAR(),sizeof(typename MeshType::VertexType),0);
					}
					glBindBuffer(GL_ARRAY_BUFFER,array_buffers[0]);
					glVertexPointer(3,GL_TYPE_NM<ScalarType>::SCALAR(),sizeof(typename MeshType::VertexType),0);

					glDrawElements(GL_TRIANGLES ,m->fn*3,GL_UNSIGNED_INT, &(*indices.begin()) );
					glDisableClientState (GL_VERTEX_ARRAY);
					if (nm==NMPerVert)
						glDisableClientState (GL_NORMAL_ARRAY);

					glBindBuffer(GL_ARRAY_BUFFER, 0);

					return;

				}
			}

			if(curr_hints&HNUseVArray)
			{
				if( (cm==CMNone) || (cm==CMPerMesh) )
				{
					if (nm==NMPerVert)
						glEnableClientState (GL_NORMAL_ARRAY);
					glEnableClientState (GL_VERTEX_ARRAY);

					if (nm==NMPerVert)
						glNormalPointer(GL_TYPE_NM<ScalarType>::SCALAR(),sizeof(typename MeshType::VertexType),&(m->vert.begin()->N()[0]));
					glVertexPointer(3,GL_TYPE_NM<ScalarType>::SCALAR(),sizeof(typename MeshType::VertexType),&(m->vert.begin()->P()[0]));

					glDrawElements(GL_TRIANGLES ,m->fn*3,GL_UNSIGNED_INT, &(*indices.begin()) );
					glDisableClientState (GL_VERTEX_ARRAY);
					if (nm==NMPerVert)
						glDisableClientState (GL_NORMAL_ARRAY);

					return;
				}
			}
			else

				if(curr_hints&HNUseTriStrip)
				{
					//if( (nm==NMPerVert) && ((cm==CMNone) || (cm==CMPerMesh)))
					//	if(curr_hints&HNUseVArray){
					//		glEnableClientState (GL_NORMAL_ARRAY  );
					//		glNormalPointer(GL_FLOAT,sizeof(MeshType::VertexType),&(m->vert[0].cN()));
					//		glEnableClientState (GL_VERTEX_ARRAY);
					//		glVertexPointer(3,GL_FLOAT,sizeof(MeshType::VertexType),&(m->vert[0].cP()));
					//		std::vector<GLAElem>::iterator vi;
					//		for(vi=TStripVED.begin();vi!=TStripVED.end();++vi)
					//					glDrawElements(vi->glmode ,vi->len,GL_UNSIGNED_SHORT,&TStripVEI[vi->start] );
					//
					//		glDisableClientState (GL_NORMAL_ARRAY  );
					//		glDisableClientState (GL_VERTEX_ARRAY);
					//		return;
					//	}

					//std::vector< MeshType::VertexType *>::iterator vi;
					//glBegin(GL_TRIANGLE_STRIP);
					//if(nm == NMPerFace) fip=TStripF.begin();

					//for(vi=TStrip.begin();vi!=TStrip.end(); ++vi){
					//	if((*vi)){
					//		if(nm==NMPerVert) glNormal((*vi)->cN());
					//		if(nm==NMPerFace) glNormal((*fip)->cN());
					//		glVertex((*vi)->P());
					//		}
					//	else
					//		{
					//			glEnd();
					//			glBegin(GL_TRIANGLE_STRIP);
					//		}
					//	if(nm == NMPerFace) ++fip;
					//	}
					//glEnd();
				}
				else
				{
					typename FACE_POINTER_CONTAINER::iterator fp;
					FaceIterator fi;

					short curtexname=-1;
					if(partial)
						fp = face_pointers.begin();
					else
						fi = m->face.begin();

					if(tm==TMPerWedgeMulti)
					{
						curtexname=(*fi).WT(0).n();
						if ((curtexname >= 0) && (curtexname < (int)TMId.size()))
						{
							glEnable(GL_TEXTURE_2D);
							glBindTexture(GL_TEXTURE_2D,TMId[curtexname]);
						}
						else
						{
							glDisable(GL_TEXTURE_2D);
						}
					}

					if(tm==TMPerWedge)
						glEnable(GL_TEXTURE_2D);

					if(tm==TMPerVert && !TMId.empty()) // in the case of per vertex tex coord we assume that we have a SINGLE texture.
					{
						curtexname = 0;
						glEnable(GL_TEXTURE_2D);
						glBindTexture(GL_TEXTURE_2D,TMId[curtexname]);
					}
					glBegin(GL_TRIANGLES);

					while( (partial)?(fp!=face_pointers.end()):(fi!=m->face.end()))
					{
						FaceType & f = (partial)?(*(*fp)): *fi;

						if(!f.IsD())
						{
							if(tm==TMPerWedgeMulti)
								if(f.WT(0).n() != curtexname)
								{
									curtexname=(*fi).WT(0).n();
									glEnd();

									if (curtexname >= 0)
									{
										glEnable(GL_TEXTURE_2D);
										if(!TMId.empty())
											glBindTexture(GL_TEXTURE_2D,TMId[curtexname]);
									}
									else
									{
										glDisable(GL_TEXTURE_2D);
									}

									glBegin(GL_TRIANGLES);
								}

								if(nm == NMPerFace)	glNormal(f.cN());
								if(nm == NMPerVert)	glNormal(f.V(0)->cN());
								if(nm == NMPerWedge)glNormal(f.WN(0));

								if(cm == CMPerFace)	glColor(f.C());
								if(cm == CMPerVert)	glColor(f.V(0)->C());
								if(tm==TMPerVert) glTexCoord(f.V(0)->T().P());
								if( (tm==TMPerWedge)||(tm==TMPerWedgeMulti) )glTexCoord(f.WT(0).t(0));
								glVertex(f.V(0)->P());

								if(nm == NMPerVert)	glNormal(f.V(1)->cN());
								if(nm == NMPerWedge)glNormal(f.WN(1));
								if(cm == CMPerVert)	glColor(f.V(1)->C());
								if(tm==TMPerVert) glTexCoord(f.V(1)->T().P());
								if( (tm==TMPerWedge)|| (tm==TMPerWedgeMulti)) glTexCoord(f.WT(1).t(0));
								glVertex(f.V(1)->P());

								if(nm == NMPerVert)	glNormal(f.V(2)->cN());
								if(nm == NMPerWedge)glNormal(f.WN(2));
								if(cm == CMPerVert) glColor(f.V(2)->C());
								if(tm==TMPerVert) glTexCoord(f.V(2)->T().P());
								if( (tm==TMPerWedge)|| (tm==TMPerWedgeMulti)) glTexCoord(f.WT(2).t(0));
								glVertex(f.V(2)->P());
						}

						if(partial)
							++fp;
						else
							++fi;
					}

					glEnd();

				}
		}

		// A draw wireframe that hides faux edges
		template <NormalMode nm, ColorMode cm>
		void DrawWirePolygonal()
		{
			if(cm == CMPerMesh)   glColor(m->C());
			FaceIterator fi;
			typename FACE_POINTER_CONTAINER::iterator fp;

			if(partial)
				fp = face_pointers.begin();
			else
				fi = m->face.begin();

			glBegin(GL_LINES);

			while( (partial)?(fp!=face_pointers.end()):(fi!=m->face.end()))
			{
				typename MeshType::FaceType & f = (partial)?(*(*fp)): *fi;

				if(!f.IsD())
				{

					if(nm == NMPerFace)	glNormal(f.cN());
					if(cm == CMPerFace)	glColor(f.C());

					if (!f.IsF(0)) {
						if(nm == NMPerVert)	glNormal(f.V(0)->cN());
						if(nm == NMPerWedge)glNormal(f.WN(0));
						if(cm == CMPerVert)	glColor(f.V(0)->C());
						glVertex(f.V(0)->P());

						if(nm == NMPerVert)	glNormal(f.V(1)->cN());
						if(nm == NMPerWedge)glNormal(f.WN(1));
						if(cm == CMPerVert)	glColor(f.V(1)->C());
						glVertex(f.V(1)->P());
					}

					if (!f.IsF(1)) {
						if(nm == NMPerVert)	glNormal(f.V(1)->cN());
						if(nm == NMPerWedge)glNormal(f.WN(1));
						if(cm == CMPerVert)	glColor(f.V(1)->C());
						glVertex(f.V(1)->P());

						if(nm == NMPerVert)	glNormal(f.V(2)->cN());
						if(nm == NMPerWedge)glNormal(f.WN(2));
						if(cm == CMPerVert)	glColor(f.V(2)->C());
						glVertex(f.V(2)->P());
					}

					if (!f.IsF(2)) {
						if(nm == NMPerVert)	glNormal(f.V(2)->cN());
						if(nm == NMPerWedge)glNormal(f.WN(2));
						if(cm == CMPerVert)	glColor(f.V(2)->C());
						glVertex(f.V(2)->P());

						if(nm == NMPerVert)	glNormal(f.V(0)->cN());
						if(nm == NMPerWedge)glNormal(f.WN(0));
						if(cm == CMPerVert)	glColor(f.V(0)->C());
						glVertex(f.V(0)->P());
					}

				}

				if(partial)
					++fp;
				else
					++fi;
			}

			glEnd();
		}

		/// Basic Point drawing fucntion
		// works also for mesh with deleted vertices
		template<NormalMode nm, ColorMode cm>
		void DrawPointsBase()
		{
			glBegin(GL_POINTS);
			if(cm==CMPerMesh) glColor(m->C());

			for(VertexIterator vi=m->vert.begin();vi!=m->vert.end();++vi)if(!(*vi).IsD())
			{
				if(nm==NMPerVert) glNormal((*vi).cN());
				if(cm==CMPerVert) glColor((*vi).C());
				glVertex((*vi).P());
			}
			glEnd();
		}

		/// Utility function that computes in eyespace the current distance between the camera and the center of the bbox of the mesh
		double CameraDistance(){
			CoordType res;
			Matrix44<ScalarType> mm;
			glGetv(GL_MODELVIEW_MATRIX,mm);
			CoordType  c=m->bbox.Center();
			res=mm*c;
			return Norm(res);
		}

		template<NormalMode nm, ColorMode cm>
		void DrawPoints()
		{
			glPushAttrib(GL_ENABLE_BIT | GL_POINT_BIT);
			if(GetHintParami(HNPPointSmooth)>0) glEnable(GL_POINT_SMOOTH);
			else glDisable(GL_POINT_SMOOTH);
			glPointSize(GetHintParamf(HNPPointSize));
			if(GetHintParami(HNPPointDistanceAttenuation)>0)
			{
				float camDist = (float)CameraDistance();
				float quadratic[] = { 0.0f, 0.0f, 1.0f/(camDist*camDist) , 0.0f };
				glPointParameterfv( GL_POINT_DISTANCE_ATTENUATION, quadratic );
				glPointParameterf( GL_POINT_SIZE_MAX, 16.0f );
				glPointParameterf( GL_POINT_SIZE_MIN, 1.0f );
			}
			else
			{
				float quadratic[] = { 1.0f, 0.0f, 0.0f};
				glPointParameterfv( GL_POINT_DISTANCE_ATTENUATION, quadratic );
				glPointSize(GetHintParamf(HNPPointSize));
			}

			if(m->vn!=(int)m->vert.size())
			{
				DrawPointsBase<nm,cm>();
			}
			else
			{
				if(cm==CMPerMesh)
					glColor(m->C());
				if (m->vert.size() != 0)
				{
					// Perfect case, no deleted stuff,
					// draw the vertices using vertex arrays
					if (nm==NMPerVert)
					{
						glEnableClientState (GL_NORMAL_ARRAY);
						glNormalPointer(GL_TYPE_NM<ScalarType>::SCALAR(),sizeof(VertexType),&(m->vert.begin()->N()[0]));
					}
					if (cm==CMPerVert)
					{
						glEnableClientState (GL_COLOR_ARRAY);
						glColorPointer(4,GL_UNSIGNED_BYTE,sizeof(typename MeshType::VertexType),&(m->vert.begin()->C()[0]));
					}

					glEnableClientState (GL_VERTEX_ARRAY);
					glVertexPointer(3,GL_TYPE_NM<ScalarType>::SCALAR(),sizeof(VertexType),&(m->vert.begin()->P()[0]));

					glDrawArrays(GL_POINTS,0,m->vn);

					glDisableClientState (GL_VERTEX_ARRAY);
					if (nm==NMPerVert)  glDisableClientState (GL_NORMAL_ARRAY);
					if (cm==CMPerVert)  glDisableClientState (GL_COLOR_ARRAY);
				}
			}
			glPopAttrib();
			return;
		}

		void DrawHidden()
		{
			//const float ZTWIST=HNParamf[HNPZTwist];
			glPushAttrib(GL_ENABLE_BIT | GL_CURRENT_BIT | GL_LIGHTING_BIT );
			glEnable(GL_POLYGON_OFFSET_FILL);
			glPolygonOffset(1.0, 1);
			//glDepthRange(ZTWIST,1.0f);
			glDisable(GL_LIGHTING);
			glColorMask(GL_FALSE,GL_FALSE,GL_FALSE,GL_FALSE);
			DrawFill<NMNone,CMNone,TMNone>();
			glDisable(GL_POLYGON_OFFSET_FILL);
			glEnable(GL_LIGHTING);
			glColorMask(GL_TRUE,GL_TRUE,GL_TRUE,GL_TRUE);
			//glDepthRange(0.0f,1.0f-ZTWIST);
			DrawWire<NMPerVert,CMNone>();
			glPopAttrib();
			//	glDepthRange(0,1.0f);
		}

		template <NormalMode nm, ColorMode cm, TextureMode tm>
		void DrawFlatWire()
		{
			//const float ZTWIST=HNParamf[HNPZTwist];
			//glDepthRange(ZTWIST,1.0f);
			glPushAttrib(GL_ENABLE_BIT | GL_CURRENT_BIT | GL_LIGHTING_BIT );
			glEnable(GL_POLYGON_OFFSET_FILL);
			glPolygonOffset(1.0, 1);
			DrawFill<nm,cm,tm>();
			glDisable(GL_POLYGON_OFFSET_FILL);
			//glDepthRange(0.0f,1.0f-ZTWIST);
			glEnable(GL_COLOR_MATERIAL);
			glColorMaterial(GL_FRONT_AND_BACK,GL_AMBIENT_AND_DIFFUSE);
			//glColorMaterial(GL_FRONT,GL_DIFFUSE);
			glColor3f(.3f,.3f,.3f);
			DrawWire<nm,CMNone>();
			glPopAttrib();
			//glDepthRange(0,1.0f);
		}

		template <NormalMode nm, ColorMode cm>
		void DrawRadar()
		{
			const float ZTWIST=HNParamf[HNPZTwist];
			glEnable(GL_BLEND);
			glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
			glDepthMask(0);
			glDepthRange(ZTWIST,1.0f);

			if (cm == CMNone)
				glColor4f(0.2f, 1.0f, 0.4f, 0.2f);
			//	DrawFill<nm,cm,TMNone>();
			Draw<DMFlat,CMNone,TMNone>();

			glDepthMask(1);
			glColorMask(GL_FALSE,GL_FALSE,GL_FALSE,GL_FALSE);
			//	DrawFill<nm,cm,TMNone>();
			Draw<DMFlat,CMNone,TMNone>();

			glDepthRange(0.0f,1.0f-ZTWIST);
			glColorMask(GL_TRUE,GL_TRUE,GL_TRUE,GL_TRUE);
			glColor4f(0.1f, 1.0f, 0.2f, 0.6f);
			Draw<DMWire,CMNone,TMNone>();
			glDisable(GL_BLEND);
			glDepthRange(0,1.0f);

		}



#ifdef GL_TEXTURE0_ARB
		// Multitexturing nel caso voglia usare due texture unit.
		void DrawTexture_NPV_TPW2()
		{
			unsigned int texname=(*(m->face.begin())).WT(0).n(0);
			glBindTexture(GL_TEXTURE_2D,TMId[texname]);
			typename MeshType::FaceIterator fi;
			glBegin(GL_TRIANGLES);
			for(fi=m->face.begin();fi!=m->face.end();++fi)if(!(*fi).IsD()){
				if(texname!=(*fi).WT(0).n(0))	{
					texname=(*fi).WT(0).n(0);
					glEnd();
					glBindTexture(GL_TEXTURE_2D,TMId[texname]);
					glBegin(GL_TRIANGLES);
				}
				glMultiTexCoordARB(GL_TEXTURE0_ARB, (*fi).WT(0).t(0));
				glMultiTexCoordARB(GL_TEXTURE1_ARB, (*fi).WT(0).t(0));
				glNormal((*fi).V(0)->N());
				glVertex((*fi).V(0)->P());

				glMultiTexCoordARB(GL_TEXTURE0_ARB, (*fi).WT(1).t(0));
				glMultiTexCoordARB(GL_TEXTURE1_ARB, (*fi).WT(1).t(0));
				glNormal((*fi).V(1)->N());
				glVertex((*fi).V(1)->P());

				glMultiTexCoordARB(GL_TEXTURE0_ARB, (*fi).WT(2).t(0));
				glMultiTexCoordARB(GL_TEXTURE1_ARB, (*fi).WT(2).t(0));
				glNormal((*fi).V(2)->N());
				glVertex((*fi).V(2)->P());
			}
			glEnd();
		}

#endif


		/*int MemUsed()
		{
		int tot=sizeof(GlTrimesh);
		tot+=sizeof(mesh_type::edge_type)*edge.size();
		tot+=sizeof(MeshType::VertexType *) * EStrip.size();
		tot+=sizeof(MeshType::VertexType *) * TStrip.size();
		tot+=sizeof(MeshType::FaceType *)   * TStripF.size();
		return tot;
		}*/

	private:

		template <NormalMode nm, ColorMode cm>
		void DrawWire()
		{
			//if(!(curr_hints & (HNUseEdgeStrip | HNUseLazyEdgeStrip) ) )
			if ( (curr_hints & HNIsPolygonal) )
			{
				DrawWirePolygonal<nm,cm>();
			}
			else
			{
				glPushAttrib(GL_POLYGON_BIT);
				glPolygonMode(GL_FRONT_AND_BACK ,GL_LINE);
				DrawFill<nm,cm,TMNone>();
				glPopAttrib();
			}
			if(m->fn==0 && m->en>0)
			{
				glPushAttrib(GL_ENABLE_BIT);
				glDisable(GL_LIGHTING);
				glBegin(GL_LINES);
				for(EdgeIterator ei=m->edge.begin();ei!=m->edge.end(); ++ei)
				{
					glVertex((*ei).V(0)->P());
					glVertex((*ei).V(1)->P());
				}
				glEnd();
				glPopAttrib();
			}
			//	{
			//			if(!HasEdges()) ComputeEdges();

			//if(cm==CMPerMesh)	glColor(m->C());
			//std::vector< MeshType::VertexType *>::iterator vi;
			//glBegin(GL_LINE_STRIP);
			//for(vi=EStrip.begin();vi!=EStrip.end(); ++vi){
			//	if((*vi)){
			//			glNormal((*vi)->N());
			//			glVertex((*vi)->P());
			//		}
			//	else
			//		{
			//			glEnd();
			//			glBegin(GL_LINE_STRIP);
			//		}
			//}
			//glEnd();
			//	}
		}

		void DrawBBox(ColorMode cm)
		{
			if(cm==CMPerMesh) glColor(m->C());
			glBoxWire(m->bbox);
		}


	};// end class
} // end namespace

#endif
