#include "ofxObjLoader.h"

ofxObjLoader::ofxObjLoader(){
  verbose=false;
}


ofxObjLoader::ofxObjLoader(char file[]){
  ofxObjLoader();
  loadFile(file);
}


void ofxObjLoader::loadFile(char file[]){

  string newFileName = ofToDataPath(file);
  //validate the file name
  if(strlen(file)==0){
    complain("filename string was empty");
    return;
  }

  //make sure the file exists on the disk
  ifstream existanceChecker(newFileName.c_str());
  if(!existanceChecker.is_open()){
    complain("file not found");
    return;
  }
  existanceChecker.close();


  // we will now slowly parse through the file f
  ifstream f;
  f.open(newFileName.c_str(),iostream::binary);
  while(f.good()){

    //get the first token on this line. it's a command
    char objCommand[256];
    f.getline(objCommand,256,' ');

    if(!strcmp(objCommand,"v")){
      vertices.push_back(objVertex());
      f >> vertices.back().x;
      f >> vertices.back().y;
      f >> vertices.back().z;

      ignoreRestOfLine(&f);

    }else if(!strcmp(objCommand,"#")){//comment
      ignoreRestOfLine(&f);

    }else if(!strcmp(objCommand,"vt")){
      texCoords.push_back(objVertex());
      f >> texCoords.back().x;
      f >> texCoords.back().y;
      f >> texCoords.back().z;
      ignoreRestOfLine(&f);

    }else if(!strcmp(objCommand,"vn")){
      normals.push_back(objVertex());
      f >> normals.back().x;
      f >> normals.back().y;
      f >> normals.back().z;
      ignoreRestOfLine(&f);


    }else if(!strcmp(objCommand,"f") || !strcmp(objCommand,"fo")){
      char x[256];
      int strlen_x;
      faces.push_back(objFace());

      /**
	    get the number of vertices in this face and store it in the face struct.
      */
      int oldpos = f.tellg(); //push file ptr
      f.getline(x,256);
      strlen_x=strlen(x);
      faces.back().count = 1;
      for(int i=0;i<strlen_x;i++){
	    if(x[i]==' '||x[i]=='\t')faces.back().count++;
      }
      f.seekg(oldpos);//pop file ptr

      /**
	     get the number of forward slashes per line in order to categorise.
      */
      oldpos = f.tellg(); //push file ptr
      f.getline(x,256);
      strlen_x=strlen(x);
      faces.back().type = 1;
      for(int i=0;i<strlen_x;i++){
	    if(x[i]=='/')faces.back().type++;
	    else if(x[i]==' '||x[i]=='\t')break;//only count the first one.
      }
      f.seekg(oldpos);//pop file ptr

      /**
	   validate vertex size to make sure this is a type of face-spec
	   we know how to parse which is only 2, 3, and 4
      */
      if(faces.back().count>4 ){
	    complain("Unrecognized face vertex count.");
	    ignoreRestOfLine(&f);
	    continue;
      }


      /**
	   validate vertex.type - the  data-depth. ie. how many numbers per chunk.
      */
      if(faces.back().type!=3  && faces.back().type!=2 && faces.back().type!=1 ){
	    complain("Unrecognized face vertex type.");
	    ignoreRestOfLine(&f);
	    continue;
      }

      /**
	     loop through and collect the appropriate data into the arrays.
      */
      for(int thisVertexID=0;thisVertexID<faces.back().count;thisVertexID++){
	    if(faces.back().count>=thisVertexID+1){

	    //depending on the syntax of the face vertex chunk, parse accordingly
	    if(faces.back().type==3){
	      // %i/%i/%i

	      //collect the geometric vertex
	      f.getline(x,256,'/');
	      faces.back().vertices[thisVertexID] = strtol(x,NULL,10);

	      //collect the texture vertex
	      f.getline(x,256,'/');
	      faces.back().texCoords[thisVertexID] = strtol(x,NULL,10);

	      //collect the vertex normal, but beware of the end-of-line
	      if(faces.back().count==thisVertexID+1)f.getline(x,256);
	      else f.getline(x,256,' ');
	      faces.back().normals[thisVertexID] = strtol(x,NULL,10);




	  }else if(faces.back().type==2){
	    // %i/%i

	    //collect the geometric vertex
	    f.getline(x,256,'/');
	    faces.back().vertices[thisVertexID] = strtol(x,NULL,10);

	    //collect the texture vertex, but beware of the end-of-line
	    if(faces.back().count==thisVertexID+1)f.getline(x,256);
	    else f.getline(x,256,' ');
	    faces.back().texCoords[thisVertexID] = strtol(x,NULL,10);

	  }else if(faces.back().type==1){
	    // %i

	    //collect the geometric vertex, but beware of the end-of-line
	    if(faces.back().count==thisVertexID+1)f.getline(x,256);
	    else f.getline(x,256,' ');
	    faces.back().vertices[thisVertexID] = strtol(x,NULL,10);
	  }

	}

      }

    }else if(strlen(objCommand)==0){
    }else{
      if(objCommand[0]!='#'){
		char s[256];
		sprintf(s,"command token not parsed: %s",objCommand);
		complain(s);
	   }
      ignoreRestOfLine(&f);
    }
  }
  f.close();

}

ofxObjLoader::~ofxObjLoader(){

}


objVertex ofxObjLoader::getVertex(int i){
  if(i<vertices.size())return vertices[i];
  else return vertices[0];
}



void ofxObjLoader::renderFace(int i){
  /**
     draw the verts
  */

  for(int ii=0;ii<faces[i].count;ii++){
	if(faces[i].type>3){
	  glTexCoord2f(texCoords[faces[i].texCoords[ii]-1].x,
		   texCoords[faces[i].texCoords[ii]-1].y);
	}

	if(faces[i].type>2){
	  glNormal3f(normals[faces[i].normals[ii]-1].x,
		 normals[faces[i].normals[ii]-1].y,
		 normals[faces[i].normals[ii]-1].z);
	}

	int index = faces[i].vertices[ii]-1;
	float x=vertices[index].x;
	float y=vertices[index].y;
	float z=vertices[index].z;
	glVertex3f(x,y,z);
  }
}


void ofxObjLoader::fillFaces(){
#if defined(__DARWIN__)
  glEnable(GL_RESCALE_NORMAL);
#endif
  for(int i=0;i<faces.size();i++){
    glBegin(GL_TRIANGLE_FAN);
    renderFace(i);
    glEnd();
  }
}



void ofxObjLoader::outlineFaces(){
  for(int i=0;i<faces.size();i++){
    glBegin(GL_LINE_LOOP);
    renderFace(i);
    glEnd();
  }
}


void ofxObjLoader::pointVertices(){
  for(int i=0;i<faces.size();i++){
    glBegin(GL_POINTS);
    renderFace(i);
    glEnd();
  }
}


void ofxObjLoader::ignoreRestOfLine(ifstream *f){
  char buff[256];
  f->getline(buff,256);
  //ok, now skip empty lines as well.
  if(f->peek()=='\n'||f->peek()=='\r')ignoreRestOfLine(f);
}

void ofxObjLoader::complain(char *s){
  if(verbose)fprintf(stderr,"ofxObjLoader: %s\n",s);
}


objVertex::objVertex(float xx,float yy,float zz){
	x=xx;
	y=yy;
	z=zz;
}

objVertex::objVertex(){
	x=0;
	y=0;
	z=0;
}
