/****************************************************************************
* VCGLib                                                            o o     *
* Visual and Computer Graphics Library                            o     o   *
*                                                                _   O  _   *
* Copyright(C) 2004                                                \/)\/    *
* 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 __VCGLIB_IMPORT_OBJ
#define __VCGLIB_IMPORT_OBJ

#include <wrap/callback.h>
#include <vcg/complex/trimesh/allocate.h>
#include <wrap/io_trimesh/io_mask.h>
#include <wrap/io_trimesh/io_material.h>
#ifdef __gl_h_
#include <wrap/gl/glu_tesselator.h>
#endif
#include <vcg/space/color4.h>

#include <fstream>
#include <string>
#include <vector>


namespace vcg {
namespace tri {
namespace io {

/** 
This class encapsulate a filter for importing obj (Alias Wavefront) meshes.
Warning: this code assume little endian (PC) architecture!!!
*/
template <class OpenMeshType>
class ImporterOBJ
{
public:

	typedef typename OpenMeshType::VertexPointer VertexPointer;
	typedef typename OpenMeshType::ScalarType ScalarType;
	typedef typename OpenMeshType::VertexType VertexType;
	typedef typename OpenMeshType::FaceType FaceType;
	typedef typename OpenMeshType::VertexIterator VertexIterator;
	typedef typename OpenMeshType::FaceIterator FaceIterator;
	typedef typename OpenMeshType::CoordType CoordType;

	class Info
	{
	public:

  Info()
  {
    mask	= 0;
    cb		= 0;
    numTexCoords=0;
  }

	/// It returns a bit mask describing the field preesnt in the ply file
  int mask;  

  /// a Simple callback that can be used for long obj parsing. 
  // it returns the current position, and formats a string with a description of what th efunction is doing (loading vertexes, faces...)
  CallBackPos *cb;

  /// number of vertices
  int numVertices;
 	/// number of faces (the number of triangles could be 
  /// larger in presence of polygonal faces
	int numFaces;
 	/// number of texture coords indexes
	int numTexCoords;
  /// number of normals
  int numNormals;

	}; // end class


	//struct OBJFacet
	//{
	//  CoordType n;
	//	CoordType t;
	//  CoordType v[3];
	//
	//	short attr;  // material index
	//};
	struct ObjIndexedFace
	{ 
	void set(const int & num){v.resize(num);n.resize(num); t.resize(num);}
	std::vector<int> v;
	std::vector<int> n;
	std::vector<int> t;
	int tInd;
	bool  edge[3];// useless if the face is a polygon, no need to have variable length array
	Color4b c;
	};

	struct ObjTexCoord
	{
	float u;
	float v;
	};

	enum OBJError {
		// Successfull opening
	E_NOERROR													= 0x000,	//	0  (position of correspondig string in the array)

	// Non Critical Errors (only odd numbers)
	E_NON_CRITICAL_ERROR							= 0x001,
	E_MATERIAL_FILE_NOT_FOUND					= 0x003,	//  1
	E_MATERIAL_NOT_FOUND							= 0x005,	//  2
	E_TEXTURE_NOT_FOUND								= 0x007,	//  3
	E_VERTICES_WITH_SAME_IDX_IN_FACE	= 0x009,  //	4

	// Critical Opening Errors (only even numbers)
	E_CANTOPEN										=	0x00A,	//  5
	E_UNESPECTEDEOF								= 0x00C,	//  6
	E_ABORTED											= 0x00E,	//  7
	E_NO_VERTEX										= 0x010,	//  8
	E_NO_FACE											= 0x012,	//  9
	E_BAD_VERTEX_STATEMENT				= 0x014,	// 10
	E_BAD_VERT_TEX_STATEMENT			= 0x016,	// 11
	E_BAD_VERT_NORMAL_STATEMENT	  = 0x018,	// 12
	E_LESS_THAN_3VERTINFACE				= 0x01A,	// 13
	E_BAD_VERT_INDEX							= 0x01C,	// 14
	E_BAD_VERT_TEX_INDEX 					= 0x01E,	// 15
	E_BAD_VERT_NORMAL_INDEX 			= 0x020		// 16
	};

	// to check if a given error is critical or not.
	static bool ErrorCritical(int err)
	{ 
  if(err<0x00A && err>=0) return false;
  return true;
	}

	static const char* ErrorMsg(int error)
	{
  static const char* obj_error_msg[] =
  {
		"No errors",																									//  0

		"Material library file wrong or not found, a default white material is used",						//  1
		"Some materials definitions were not found, a default white material is used where no material was available",  // 2
		"Texture file not found",																			//  3
		"Identical index vertices found in the same face",						//	4

		"Can't open file",																						//  5
		"Premature End of file",																			//  6
		"File opening aborted",																				//  7
		"No vertex field found",																			//  8
		"No face field found",																				//  9
		"Vertex statement with less than 3 coords",										// 10
		"Texture coords statement with less than 2 coords",						// 11
		"Vertex normal statement with less than 3 coords",						// 12  
		"Face with less than 3 vertices",															// 13
		"Bad vertex index in face",																		// 14
		"Bad texture coords index in face",														// 15
		"Bad vertex normal index in face"															// 16
	};

	// due to approximation, following line works well for either even (critical err codes)
	// or odd (non critical ones) numbers
	error = (int) error/2;

  if(error>15 || error<0) return "Unknown error";
  else return obj_error_msg[error];
	};

	// Helper functions that checks the range of indexes 
	// putting them in the correct range if less than zero (as in the obj style)

	static bool GoodObjIndex(int &index, const int maxVal)
	{
  if (index > maxVal)	return false;
  if (index < 0)
	{
		index += maxVal+1;
		if (index<0 || index > maxVal)	return false;
	}
  return true;
	}

	static int Open(OpenMeshType &mesh, const char *filename, int &loadmask, CallBackPos *cb=0)
	{
  Info oi;
  oi.mask=-1;
  oi.cb=cb;
  int ret=Open(mesh,filename,oi);
  loadmask=oi.mask;
  return ret;
	}

	/*!
	* Opens an object file (in ascii format) and populates the mesh passed as first
	* accordingly to read data
	* \param m The mesh model to be populated with data stored into the file
	* \param filename The name of the file to be opened
	* \param oi A structure containing infos about the object to be opened
	*/
	static int Open( OpenMeshType &m, const char * filename, Info &oi)
	{
	int result = E_NOERROR;

	m.Clear();
		CallBackPos *cb = oi.cb;

	// if LoadMask has not been called yet, we call it here
	if (oi.mask == -1)
		LoadMask(filename, oi);

		const int inputMask = oi.mask;
  Mask::ClampMask<OpenMeshType>(m,oi.mask);

	if (oi.numVertices == 0)
		return E_NO_VERTEX;

	// Commented out this test. You should be allowed to load point clouds.
	//if (oi.numFaces == 0)
	//	return E_NO_FACE;

	std::ifstream stream(filename);
	if (stream.fail())
		return E_CANTOPEN;

	std::vector<Material>	materials;  // materials vector
	std::vector<ObjTexCoord>	texCoords;  // texture coordinates
	std::vector<CoordType>  normals;		// vertex normals
  std::vector<ObjIndexedFace> indexedFaces;
	std::vector< std::string > tokens;
	std::string	header;

	short currentMaterialIdx = 0;			// index of current material into materials vector
  Color4b currentColor=Color4b::LightGray;	// we declare this outside code block since other 
				     										// triangles of this face will share the same color

	Material defaultMaterial;					// default material: white
	materials.push_back(defaultMaterial);

	int numVertices  = 0;  // stores the number of vertices been read till now
	int numTriangles = 0;  // stores the number of faces been read till now
	int numTexCoords = 0;  // stores the number of texture coordinates been read till now
	int numVNormals	 = 0;  // stores the number of vertex normals been read till now

	int numVerticesPlusFaces = oi.numVertices + oi.numFaces;
  int extraTriangles=0;
	// vertices and faces allocatetion
	VertexIterator vi = Allocator<OpenMeshType>::AddVertices(m,oi.numVertices);
	//FaceIterator   fi = Allocator<OpenMeshType>::AddFaces(m,oi.numFaces);

  ObjIndexedFace	ff; 

	while (!stream.eof())
	{
		tokens.clear();
		TokenizeNextLine(stream, tokens);

		unsigned int numTokens = static_cast<unsigned int>(tokens.size());
		if (numTokens > 0)
		{
			header.clear();
			header = tokens[0];

			if (header.compare("v")==0)	// vertex
			{
				if (numTokens < 4) return E_BAD_VERTEX_STATEMENT;

				(*vi).P()[0] = (ScalarType) atof(tokens[1].c_str());
				(*vi).P()[1] = (ScalarType) atof(tokens[2].c_str());
				(*vi).P()[2] = (ScalarType) atof(tokens[3].c_str());
				++numVertices;

				// assigning vertex color
				// ----------------------
					if (((oi.mask & vcg::tri::io::Mask::IOM_VERTCOLOR) != 0) && (m.HasPerVertexColor()))
					{
						if(numTokens>=7)
						{
							ScalarType rf(atof(tokens[4].c_str())), gf(atof(tokens[5].c_str())), bf(atof(tokens[6].c_str()));
							ScalarType scaling = (rf<=1 && gf<=1 && bf<=1) ? 255. : 1;
							
							unsigned char r			= (unsigned char) ((ScalarType) atof(tokens[4].c_str()) * scaling);
							unsigned char g			= (unsigned char) ((ScalarType) atof(tokens[5].c_str()) * scaling);
							unsigned char b			= (unsigned char) ((ScalarType) atof(tokens[6].c_str()) * scaling);
							unsigned char alpha = (unsigned char) ((numTokens>=8 ? (ScalarType) atof(tokens[7].c_str()) : 1)  * scaling);
							(*vi).C() = Color4b(r, g, b, alpha);
						}
						else
						{
							(*vi).C() = currentColor;
						}
					}

				++vi;  // move to next vertex iterator

				// callback invocation, abort loading process if the call returns false
				if ((cb !=NULL) && (((numTriangles + numVertices)%100)==0) && !(*cb)((100*(numTriangles + numVertices))/numVerticesPlusFaces, "Vertex Loading"))
					return E_ABORTED;
			}
			else if (header.compare("vt")==0)	// vertex texture coords
			{
				if (numTokens < 3) return E_BAD_VERT_TEX_STATEMENT;

				ObjTexCoord t;
				t.u = static_cast<float>(atof(tokens[1].c_str()));
				t.v = static_cast<float>(atof(tokens[2].c_str()));
				texCoords.push_back(t);

				numTexCoords++;
			}
			else if (header.compare("vn")==0)  // vertex normal
			{
				if (numTokens != 4) return E_BAD_VERT_NORMAL_STATEMENT;

				CoordType n;
				n[0] = (ScalarType) atof(tokens[1].c_str());
				n[1] = (ScalarType) atof(tokens[2].c_str());
				n[2] = (ScalarType) atof(tokens[3].c_str());	
				normals.push_back(n);

				numVNormals++;
			}
				else if( (header.compare("f")==0) || (header.compare("q")==0) )  // face
			{
					bool QuadFlag = false; // QOBJ format by Silva et al for simply storing quadrangular meshes.				
					if(header.compare("q")==0) { QuadFlag=true; assert(numTokens == 5); }

				if (numTokens < 4) return E_LESS_THAN_3VERTINFACE;
				int vertexesPerFace = static_cast<int>(tokens.size()-1);

				if( (vertexesPerFace>3) && OpenMeshType::FaceType::HasPolyInfo() ){
            //_BEGIN___ if  you are loading a GENERIC POLYGON mesh
					ff.set(vertexesPerFace);
					for(int i=0;i<vertexesPerFace;++i) // remember index starts from 1 instead of 0
							SplitToken(tokens[i+1], ff.v[i], ff.n[i], ff.t[i], inputMask);

		 if ( oi.mask & vcg::tri::io::Mask::IOM_WEDGTEXCOORD )
				{
					// verifying validity of texture coords indices
		          for(int i=0;i<vertexesPerFace;i++)
				  if(!GoodObjIndex(ff.t[i],oi.numTexCoords)) 
				   return E_BAD_VERT_TEX_INDEX;

					ff.tInd=materials[currentMaterialIdx].index;
				}

				// verifying validity of vertex indices
				std::vector<int> tmp = ff.v;
				std::sort(tmp.begin(),tmp.end());
				std::unique(tmp.begin(),tmp.end());
				if(tmp.size() != ff.v.size())
					result = E_VERTICES_WITH_SAME_IDX_IN_FACE;

        for(int i=0;i<vertexesPerFace;i++)
            if(!GoodObjIndex(ff.v[i],numVertices))
              return E_BAD_VERT_INDEX;

				// assigning face normal
				if ( oi.mask & vcg::tri::io::Mask::IOM_WEDGNORMAL )
				{
					// verifying validity of vertex normal indices
          for(int i=0;i<vertexesPerFace;i++)
            if(!GoodObjIndex(ff.n[i],numVNormals)) return E_BAD_VERT_NORMAL_INDEX;
				}

				// assigning face color
                        if( oi.mask & vcg::tri::io::Mask::IOM_FACECOLOR)
					 ff.c = currentColor;

				++numTriangles;
		        indexedFaces.push_back(ff);

				// callback invocation, abort loading process if the call returns false
				if ((cb !=NULL)&& (((numTriangles + numVertices)%100)==0) )
					{
					  if (!(*cb)( (100*(numTriangles +numVertices))/ numVerticesPlusFaces, "Face Loading"))
					  return E_ABORTED;
					}
			//_END  ___ if  you are loading a GENERIC POLYGON mesh 
				}else
#ifdef __gl_h_
				{
			//_BEGIN___ if  you are loading a  TRIMESH mesh 
                        std::vector<std::vector<vcg::Point3f> > polygonVect(1); // it is a vector of polygon loops
                        polygonVect[0].resize(vertexesPerFace);
                        std::vector<int> indexVVect(vertexesPerFace);
                        std::vector<int> indexNVect(vertexesPerFace);
                        std::vector<int> indexTVect(vertexesPerFace);
                        std::vector<int> indexTriangulatedVect;

                        for(int pi=0;pi<vertexesPerFace;++pi)
                        {
                            SplitToken(tokens[pi+1], indexVVect[pi],indexNVect[pi],indexTVect[pi], inputMask);
                            GoodObjIndex(indexVVect[pi],numVertices);
                            GoodObjIndex(indexTVect[pi],oi.numTexCoords);
                           polygonVect[0][pi]=m.vert[indexVVect[pi]].cP();
                        }

                        vcg::glu_tesselator::tesselate<vcg::Point3f>(polygonVect, indexTriangulatedVect);
                        extraTriangles+=((indexTriangulatedVect.size()/3) -1);
#ifdef QT_VERSION
                        if( int(indexTriangulatedVect.size()/3) != vertexesPerFace-2)
                        {
                            qDebug("Warning there is a degenerate poligon of %i verteces that was triangulated into %i triangles",vertexesPerFace,int(indexTriangulatedVect.size()/3));
                            for(size_t qq=0;qq<polygonVect[0].size();++qq)
                                qDebug("      (%f %f %f)",polygonVect[0][qq][0],polygonVect[0][qq][1],polygonVect[0][qq][2]);
                             for(size_t qq=0;qq<tokens.size();++qq) qDebug("<%s>",tokens[qq].c_str());
                        }
#endif
                        //qDebug("Triangulated a face of %i vertexes into %i triangles",polygonVect[0].size(),indexTriangulatedVect.size());

                        for(size_t pi=0;pi<indexTriangulatedVect.size();pi+=3)
                        {
                            int i0= indexTriangulatedVect [pi+0];
                            int i1= indexTriangulatedVect [pi+1];
                            int i2= indexTriangulatedVect [pi+2];
                            //qDebug("Triangle %i (%i %i %i)",pi/3,i0,i1,i2);

                            ff.set(3);
                            ff.v[0]= indexVVect[i0];
                            ff.v[1]= indexVVect[i1];
                            ff.v[2]= indexVVect[i2];
                            ff.t[0]= indexTVect[i0];
                            ff.t[1]= indexTVect[i1];
                            ff.t[2]= indexTVect[i2];

                            // Setting internal edges: only edges formed by consecutive edges are external.
                            if( (i0+1)%vertexesPerFace == i1) ff.edge[0]=false;
                            else ff.edge[0]=true;
                            if( (i1+1)%vertexesPerFace == i2) ff.edge[1]=false;
                            else ff.edge[1]=true;
                            if( (i2+1)%vertexesPerFace == i0) ff.edge[2]=false;
                            else ff.edge[2]=true;

        if ( oi.mask & vcg::tri::io::Mask::IOM_WEDGTEXCOORD )
                            { // verifying validity of texture coords indices
                                for(int i=0;i<3;i++)
                                    if(!GoodObjIndex(ff.t[i],oi.numTexCoords))  return E_BAD_VERT_TEX_INDEX;
                                ff.tInd=materials[currentMaterialIdx].index;
                            }

                            // verifying validity of vertex indices
                            if ((ff.v[0] == ff.v[1]) || (ff.v[0] == ff.v[2]) || (ff.v[1] == ff.v[2]))
                                result = E_VERTICES_WITH_SAME_IDX_IN_FACE;

                            for(int i=0;i<3;i++)
                                if(!GoodObjIndex(ff.v[i],numVertices)) return E_BAD_VERT_INDEX;

                            // assigning face normal
                            if ( oi.mask & vcg::tri::io::Mask::IOM_WEDGNORMAL )
                            {   // verifying validity of vertex normal indices
                                for(int i=0;i<3;i++)
                                    if(!GoodObjIndex(ff.n[i],numVNormals))	return E_BAD_VERT_NORMAL_INDEX;
                            }

                            // assigning face color
                            if( oi.mask & vcg::tri::io::Mask::IOM_FACECOLOR) ff.c = currentColor;

                            ++numTriangles;
                            indexedFaces.push_back(ff);
                        }

                    }
#else
				{
                        ff.set(3);
                        for(int i=0;i<3;++i)
						{		// remember index starts from 1 instead of 0
							SplitToken(tokens[i+1], ff.v[i], ff.n[i], ff.t[i], inputMask);
							if(QuadFlag) { ff.v[i]+=1; }
						}
						if ( oi.mask & vcg::tri::io::Mask::IOM_WEDGTEXCOORD )
						{
					// verifying validity of texture coords indices
          for(int i=0;i<3;i++)
            if(!GoodObjIndex(ff.t[i],oi.numTexCoords)) 
              return E_BAD_VERT_TEX_INDEX;

					ff.tInd=materials[currentMaterialIdx].index;
				}

				// verifying validity of vertex indices
				if ((ff.v[0] == ff.v[1]) || (ff.v[0] == ff.v[2]) || (ff.v[1] == ff.v[2]))
					       result = E_VERTICES_WITH_SAME_IDX_IN_FACE;

        for(int i=0;i<3;i++)
            if(!GoodObjIndex(ff.v[i],numVertices))
              return E_BAD_VERT_INDEX;

				// assigning face normal
				if ( oi.mask & vcg::tri::io::Mask::IOM_WEDGNORMAL )
                        {   // verifying validity of vertex normal indices
          for(int i=0;i<3;i++)
            if(!GoodObjIndex(ff.n[i],numVNormals)) return E_BAD_VERT_NORMAL_INDEX;
				}

				// assigning face color
				// --------------------
				if( oi.mask & vcg::tri::io::Mask::IOM_FACECOLOR)		
          ff.c = currentColor;

				// by default there are no internal edge
				ff.edge[0]=ff.edge[1]=ff.edge[2]=false;
				if(vertexesPerFace>3) ff.edge[2]=true;
				++numTriangles;
        indexedFaces.push_back(ff);
        	/*
					// A face polygon composed of more than three vertices is triangulated
					// according to the following schema:
					//                     v5
					//                    /  \   
					//                   /    \    
					//                  /      \   
					//                 v1------v4 
					//                 |\      /
					//                 | \    /
					//                 |  \  /
					//                v2---v3
					//
					// As shown above, the 5 vertices polygon (v1,v2,v3,v4,v5)
					// has been split into the triangles (v1,v2,v3), (v1,v3,v4) e (v1,v4,v5).
					// This way vertex v1 becomes the common vertex of all newly generated
					// triangles, and this may lead to the creation of very thin triangles.
					*/

				int iVertex = 3;
				while (iVertex < vertexesPerFace)  // add other triangles
				{
							oi.mask |= Mask::IOM_BITPOLYGONAL;
					ObjIndexedFace ffNew=ff;
          			int v4_index;
					int vt4_index;
					int vn4_index;

							SplitToken(tokens[++iVertex], v4_index, vn4_index, vt4_index, inputMask);
							if(QuadFlag) { v4_index+=1; }
					if(!GoodObjIndex(v4_index, numVertices))
						return E_BAD_VERT_INDEX;

					// assigning wedge texture coordinates
					// -----------------------------------
					if( oi.mask & vcg::tri::io::Mask::IOM_WEDGTEXCOORD )
					{
						// verifying validity of texture coords index
						// ------------------------------------------
            if(!GoodObjIndex(vt4_index,oi.numTexCoords))
              return E_BAD_VERT_TEX_INDEX;

				    if ( oi.mask & vcg::tri::io::Mask::IOM_WEDGNORMAL )
                if(!GoodObjIndex(vn4_index,numVNormals))
                  return E_BAD_VERT_NORMAL_INDEX;

         		ffNew.t[1]=ff.t[2];
            ffNew.t[2]=vt4_index;            
					}

					if ((ff.v[0] == v4_index) || (ff.v[2] == v4_index)) result = E_VERTICES_WITH_SAME_IDX_IN_FACE;
					ffNew.v[1]=ff.v[2];
          ffNew.v[2]=v4_index;            

					// assigning face normal
					// ---------------------
					if ( oi.mask & vcg::tri::io::Mask::IOM_WEDGNORMAL )
					{									  
            ffNew.n[1]=ff.n[2];
            ffNew.n[2]=vn4_index;            
					}
					// Setting internal edges: edge 1 (the opposite to vertex 0) is always an external edge. 
          ffNew.edge[0]=true;
					ffNew.edge[1]=false;
					if(iVertex < vertexesPerFace) ffNew.edge[2]=true;
																	else	ffNew.edge[2]=false;
					++numTriangles;
          ++extraTriangles;
          indexedFaces.push_back(ffNew);
					ff.v[2] = v4_index;
				}
				// callback invocation, abort loading process if the call returns false
				if ((cb !=NULL)&& (((numTriangles + numVertices)%100)==0) )
					{
					  if (!(*cb)( (100*(numTriangles +numVertices))/ numVerticesPlusFaces, "Face Loading"))
					  return E_ABORTED;
						}						
                    }//_END___ if  you are loading a  TRIMESH mesh
#endif
					}
			else if (header.compare("mtllib")==0)	// material library
			{
				// obtain the name of the file containing materials library
				std::string materialFileName = tokens[1];
				if (!LoadMaterials( materialFileName.c_str(), materials, m.textures))
					result = E_MATERIAL_FILE_NOT_FOUND;
			}
			else if (header.compare("usemtl")==0)	// material usage
			{
				std::string materialName = tokens[1];
				bool found = false;
				unsigned i = 0;
				while (!found && (i < materials.size()))
				{
					std::string currentMaterialName = materials[i].materialName;
					if (currentMaterialName == materialName)
					{
						currentMaterialIdx = i;
					  Material &material = materials[currentMaterialIdx];
					  Point3f diffuseColor = material.Kd;
					  unsigned char r			= (unsigned char) (diffuseColor[0] * 255.0);
					  unsigned char g			= (unsigned char) (diffuseColor[1] * 255.0);
					  unsigned char b			= (unsigned char) (diffuseColor[2] * 255.0);
					  unsigned char alpha = (unsigned char) (material.Tr  * 255.0);
					  currentColor= Color4b(r, g, b, alpha);
						found = true;
					}
					++i;
				}

				if (!found)
				{
					currentMaterialIdx = 0;
					result = E_MATERIAL_NOT_FOUND;
				}
			}
			// we simply ignore other situations
		} // end for each line...
	} // end while stream not eof
	assert((numTriangles +numVertices) == numVerticesPlusFaces+extraTriangles);

	FaceIterator   fi = Allocator<OpenMeshType>::AddFaces(m,numTriangles);
  //-------------------------------------------------------------------------------

	// Now the final pass to convert indexes into pointers for face to vert/norm/tex references
		for(int i=0; i<numTriangles; ++i)
  {
			assert(m.face.size() == size_t(m.fn));
	m.face[i].Alloc(indexedFaces[i].v.size()); // it does not do anything if it is a trimesh

    for(unsigned int j=0;j<indexedFaces[i].v.size();++j)
    {
				m.face[i].V(j) = &(m.vert[indexedFaces[i].v[j]]);

				if (((oi.mask & vcg::tri::io::Mask::IOM_WEDGTEXCOORD) != 0) && (m.HasPerWedgeTexCoord()))
				{
          ObjTexCoord t = texCoords[indexedFaces[i].t[j]];
			    m.face[i].WT(j).u() = t.u;
			    m.face[i].WT(j).v() = t.v;
			    m.face[i].WT(j).n() = indexedFaces[i].tInd;
      }
      if ( oi.mask & vcg::tri::io::Mask::IOM_VERTTEXCOORD ) {
          ObjTexCoord t = texCoords[indexedFaces[i].t[j]];
          m.face[i].V(j)->T().u() = t.u;
          m.face[i].V(j)->T().v() = t.v;
          m.face[i].V(j)->T().n() = indexedFaces[i].tInd;
      }
      if ( oi.mask & vcg::tri::io::Mask::IOM_WEDGNORMAL )
        m.face[i].WN(j).Import(normals[indexedFaces[i].n[j]]);		

      if ( oi.mask & vcg::tri::io::Mask::IOM_VERTNORMAL )
        m.face[i].V(j)->N().Import(normals[indexedFaces[i].n[j]]);

			// set faux edge flags according to internals faces
				if (indexedFaces[i].edge[j])
				{
					m.face[i].SetF(j);
    }
				else
				{
					m.face[i].ClearF(j);
				}
			}

			if (((oi.mask & vcg::tri::io::Mask::IOM_FACECOLOR) != 0) && (m.HasPerFaceColor()))
			{
				m.face[i].C() = indexedFaces[i].c;
			}

			if (((oi.mask & vcg::tri::io::Mask::IOM_WEDGNORMAL) != 0) && (m.HasPerWedgeNormal()))
			{
    					// face normal is computed as an average of wedge normals
	    m.face[i].N().Import(m.face[i].WN(0)+m.face[i].WN(1)+m.face[i].WN(2));
			}
			else
			{
				// computing face normal from position of face vertices
				if (m.HasPerFaceNormal())
				{
					face::ComputeNormalizedNormal(m.face[i]);
  }
			}
		}

  return result;
	} // end of Open


	/*!
	* Read the next valid line and parses it into "tokens", allowing
	*	the tokens to be read one at a time.
	* \param stream	The object providing the input stream
	*	\param tokens	The "tokens" in the next line
	*/
	inline static const void TokenizeNextLine(std::ifstream &stream, std::vector< std::string > &tokens)
	{
		if(stream.eof()) return;
		std::string line;
		do
			std::getline(stream, line);
		while ((line[0] == '#' || line.length()==0) && !stream.eof());  // skip comments and empty lines

		if ((line[0] == '#') || (line.length() == 0))  // can be true only on last line of file
			return;

		size_t from		= 0; 
		size_t to			= 0;
		size_t length = line.size();
		tokens.clear();
		do
		{
			while (from!=length && (line[from]==' ' || line[from]=='\t' || line[from]=='\r') )
				from++;
      if(from!=length)
      {
				to = from+1;
				while (to!=length && line[to]!=' ' && line[to] != '\t' && line[to]!='\r')
					to++;
				tokens.push_back(line.substr(from, to-from).c_str());
				from = to;
      }
		}
		while (from<length);
	} // end TokenizeNextLine

	inline static const void SplitToken(std::string token, int &vId, int &nId, int &tId, int mask)
	{
  		  std::string vertex;
			  std::string texcoord;
				std::string normal;

 				if( ( mask & Mask::IOM_WEDGTEXCOORD ) && (mask & Mask::IOM_WEDGNORMAL) )   SplitVVTVNToken(token, vertex, texcoord, normal);
 				if(!( mask & Mask::IOM_WEDGTEXCOORD ) && (mask & Mask::IOM_WEDGNORMAL) )   SplitVVNToken(token, vertex, normal);
 				if( ( mask & Mask::IOM_WEDGTEXCOORD ) &&!(mask & Mask::IOM_WEDGNORMAL) )   SplitVVTToken(token, vertex, texcoord);
 				if(!( mask & Mask::IOM_WEDGTEXCOORD ) &&!(mask & Mask::IOM_WEDGNORMAL) )   SplitVToken(token, vertex);

		vId = atoi(vertex.c_str()) - 1;
		if(mask & Mask::IOM_WEDGTEXCOORD) tId = atoi(texcoord.c_str()) - 1;
		if(mask & Mask::IOM_WEDGNORMAL)   nId = atoi(normal.c_str())   - 1;
	}

	inline static const void SplitVToken(std::string token, std::string &vertex) 
	{
		vertex = token; 
	}

	inline static const void SplitVVTToken(std::string token, std::string &vertex, std::string &texcoord)
	{
		vertex.clear();
		texcoord.clear();

		size_t from		= 0; 
		size_t to			= 0;
		size_t length = token.size();

		if(from!=length)
    {
			char c = token[from];
			vertex.push_back(c);

			to = from+1;
            while (to<length && ((c = token[to]) !='/'))
			{
				vertex.push_back(c);
				++to;
			}
			++to;
            while (to<length && ((c = token[to]) !=' '))
			{
				texcoord.push_back(c);
				++to;
			}
		}
	}	// end of SplitVVTToken

	inline static const void SplitVVNToken(std::string token, std::string &vertex, std::string &normal)
	{
		vertex.clear();
		normal.clear();

		size_t from		= 0; 
		size_t to			= 0;
		size_t length = token.size();

		if(from!=length)
    {
			char c = token[from];
			vertex.push_back(c);

			to = from+1;
			while (to!=length && ((c = token[to]) !='/'))
			{
				vertex.push_back(c);
				++to;
			}
			++to;
			++to;  // should be the second '/'
			while (to!=length && ((c = token[to]) !=' '))
			{
				normal.push_back(c);
				++to;
			}
		}
	}	// end of SplitVVNToken

	inline static const void SplitVVTVNToken(std::string token, std::string &vertex, std::string &texcoord, std::string &normal)
	{
		vertex.clear();
		texcoord.clear();
		normal.clear();

		size_t from		= 0; 
		size_t to			= 0;
		size_t length = token.size();

		if(from!=length)
    {
			char c = token[from];
			vertex.push_back(c);

			to = from+1;
			while (to!=length && ((c = token[to]) !='/'))
			{
				vertex.push_back(c);
				++to;
			}
			++to;
			while (to!=length && ((c = token[to]) !='/'))
			{
				texcoord.push_back(c);
				++to;
			}
			++to;
			while (to!=length && ((c = token[to]) !=' '))
			{
				normal.push_back(c);
				++to;
			}
		}
	}	// end of SplitVVTVNToken

	/*!
	* Retrieves infos about kind of data stored into the file and fills a mask appropriately
	* \param filename The name of the file to open
	*	\param mask	A mask which will be filled according to type of data found in the object
	* \param oi A structure which will be filled with infos about the object to be opened
  	*/

	static bool LoadMask(const char * filename, Info &oi)
	{
 		std::ifstream stream(filename);
		if (stream.fail()) return false;

		 // obtain length of file:
    stream.seekg (0, std::ios::end);
		int length = stream.tellg();
    stream.seekg (0, std::ios::beg);

    if (length == 0) return false;

		bool bHasPerFaceColor		= false;
		bool bHasNormals 				= false;
		bool bHasPerVertexColor = false;

    oi.numVertices=0;
    oi.numFaces=0;
    oi.numTexCoords=0;
    oi.numNormals=0;
		int lineCount=0;
		int totRead=0;
    std::string line;
	  while (!stream.eof())
    {
      lineCount++;
			std::getline(stream, line);
      totRead+=line.size();
      if(oi.cb && (lineCount%1000)==0) 
					(*oi.cb)( (int)(100.0*(float(totRead))/float(length)), "Loading mask...");
			if(line.size()>2)
      {
        if(line[0]=='v')
        {
          if(line[1]==' ')
					{
						oi.numVertices++;
						if(line.size()>=7)
							bHasPerVertexColor = true;
					}
          if(line[1]=='t') oi.numTexCoords++;
          if(line[1]=='n') {
            oi.numNormals ++;
            bHasNormals = true;
          }
        }
        else {
					if((line[0]=='f') || (line[0]=='q')) oi.numFaces++;
           else
						if(line[0]=='u' && line[1]=='s') bHasPerFaceColor = true; // there is a usematerial so add per face color
			}
      }
		}
		oi.mask = 0;
		if (oi.numTexCoords)	
		{
			if (oi.numTexCoords==oi.numVertices)
				oi.mask |= vcg::tri::io::Mask::IOM_VERTTEXCOORD;

			oi.mask |= vcg::tri::io::Mask::IOM_WEDGTEXCOORD;
			// Usually if you have tex coords you also have materials
			oi.mask |= vcg::tri::io::Mask::IOM_FACECOLOR; 
		}
  if(bHasPerFaceColor)		oi.mask |= vcg::tri::io::Mask::IOM_FACECOLOR; 
  if(bHasPerVertexColor)	oi.mask |= vcg::tri::io::Mask::IOM_VERTCOLOR; 
  if (bHasNormals) {
    if (oi.numTexCoords==oi.numVertices)
      oi.mask |= vcg::tri::io::Mask::IOM_VERTNORMAL;
    else
      oi.mask |= vcg::tri::io::Mask::IOM_WEDGNORMAL;
  }

  return true;
	}

	static bool LoadMask(const char * filename, int &mask)
	{
  Info oi;
  bool ret=LoadMask(filename, oi);
  mask= oi.mask;
  return ret;
	}

	static bool LoadMaterials(const char * filename, std::vector<Material> &materials, std::vector<std::string> &textures)
	{
		// assumes we are in the right directory

		std::ifstream stream(filename);
		if (stream.fail())
			return false;

		std::vector< std::string > tokens;
		std::string	header;

		materials.clear();
		Material currentMaterial;
		currentMaterial.index = (unsigned int)(-1);

		bool first = true;
		while (!stream.eof())
		{
			tokens.clear();
			TokenizeNextLine(stream, tokens);

			if (tokens.size() > 0)
			{
				header.clear();
				header = tokens[0];

				if (header.compare("newmtl")==0)
				{
					if (!first)
					{
						materials.push_back(currentMaterial);
						currentMaterial = Material();
						currentMaterial.index = (unsigned int)(-1);
					}
					else
						first = false;
					//strcpy(currentMaterial.name, tokens[1].c_str());
                                        if(tokens.size() < 2)
						return false; 
					currentMaterial.materialName=tokens[1];
				}
				else if (header.compare("Ka")==0)
				{
                                    if (tokens.size() < 4)
                                        return false;
                                    float r = (float) atof(tokens[1].c_str());
                                    float g = (float) atof(tokens[2].c_str());
                                    float b = (float) atof(tokens[3].c_str());

                                    currentMaterial.Ka = Point3f(r, g, b);
				}
				else if (header.compare("Kd")==0)
				{
                                    if (tokens.size() < 4)
                                        return false;
                                    float r = (float) atof(tokens[1].c_str());
                                    float g = (float) atof(tokens[2].c_str());
                                    float b = (float) atof(tokens[3].c_str());

                                    currentMaterial.Kd = Point3f(r, g, b);
				}
				else if (header.compare("Ks")==0)
				{
                                    if (tokens.size() < 4)
                                        return false;
                                    float r = (float) atof(tokens[1].c_str());
                                    float g = (float) atof(tokens[2].c_str());
                                    float b = (float) atof(tokens[3].c_str());

                                    currentMaterial.Ks = Point3f(r, g, b);
				}
				else if (	(header.compare("d")==0) ||
									(header.compare("Tr")==0)	)	// alpha
				{
                                    if (tokens.size() < 2)
                                        return false;
                                    currentMaterial.Tr = (float) atof(tokens[1].c_str());
				}
				else if (header.compare("Ns")==0)  // shininess        
				{
                                    if (tokens.size() < 2)
                                        return false;
                                    currentMaterial.Ns = float(atoi(tokens[1].c_str()));
				}
				else if (header.compare("illum")==0)	// specular illumination on/off
				{
                                    if (tokens.size() < 2)
                                        return false;
                                    int illumination = atoi(tokens[1].c_str());
                                    //currentMaterial.bSpecular = (illumination == 2);
                                    currentMaterial.illum = illumination;
				}
				else if( (header.compare("map_Kd")==0)	|| (header.compare("map_Ka")==0) ) // texture name
				{
                                        if (tokens.size() < 2)
                                            return false;
					std::string textureName = tokens[1];
					//strcpy(currentMaterial.textureFileName, textureName.c_str());
					 currentMaterial.map_Kd=textureName;

					// adding texture name into textures vector (if not already present)
					// avoid adding the same name twice
					bool found = false;
					unsigned int size = static_cast<unsigned int>(textures.size());
					unsigned j = 0;
					while (!found && (j < size))
					{
						if (textureName.compare(textures[j])==0)
						{
							currentMaterial.index = (int)j;
							found = true;
						}
						++j;
					}
					if (!found)
					{
						textures.push_back(textureName);
						currentMaterial.index = (int)size;
					}
				}
				// we simply ignore other situations
			}
		}
		materials.push_back(currentMaterial);  // add last read material

		stream.close();

		return true;
	}

}; // end class
} // end Namespace tri
} // end Namespace io
} // end Namespace vcg

#endif  // ndef __VCGLIB_IMPORT_OBJ
