//
//    File: vrml2_mesh.cc
//
//    (C) 2000-2008 Helmut Cantzler
//
//    Licensed under the terms of the Lesser General Public License.
//

#include <string.h>

#include "vrml2_mesh.h"
#include "matrix3.h"

int Vrml2Mesh::readNextToken(FILE *f, char *s)
{
  int len, res;

  res=fscanf(f,"%100s", s);

  // check if it is a comment
  while (res != EOF && s[0] == '#')
    {
      do    // Reads till end of the line
	{
	  res=fgetc(f);
	}
      while (res != EOF && res != '\n');

      res=fscanf(f,"%100s",s);
    }

  // separate "{" and "}" (bad file formating)
  len= (int) strlen(s);
  if (len > 1)
    {
      if (s[len-1] == '{')
	{
	  s[len-1]=0;
	  ungetc('{', f);
	}
      if (s[len-1] == '}')
	{
	  s[len-1]=0;
	  ungetc('}', f);
	}
    }

  // separate "[" and "]" (bad file formating)
  if (len > 1)
    {
      if (s[len-1] == '[')
	{
	  s[len-1]=0;
	  ungetc('[', f);
	}
      if (s[len-1] == ']')
	{
	  s[len-1]=0;
	  ungetc(']', f);
	}
    }

  if ((*updateProgress)(ftell(f)))
    {
      s[0]=0;
      res=EOF;
    }

  return res;
}

int Vrml2Mesh::readVertex(FILE *f, float *x, float *y, float *z)
{
  if (readNextToken(f, buffer) == EOF || sscanf(buffer, "%f", x) != 1)
    return FALSE;
  if (readNextToken(f, buffer) == EOF || sscanf(buffer, "%f", y) != 1)
    return FALSE;
  if (readNextToken(f, buffer) == EOF || sscanf(buffer, "%f%*c", z) != 1)
    return FALSE;

  return TRUE;
}

int Vrml2Mesh::readTriangle(FILE *f, int *p1, int *p2, int *p3)
{
  if (readNextToken(f, buffer) == EOF || sscanf(buffer, "%d%*c", p1) != 1)
    return FALSE;
  if (readNextToken(f, buffer) == EOF || sscanf(buffer, "%d%*c", p2) != 1)
    return FALSE;
  if (readNextToken(f, buffer) == EOF || sscanf(buffer, "%d%*c", p3) != 1)
    return FALSE;

  return TRUE;
}

int Vrml2Mesh::readTextCoord(FILE *f, float *s, float *t)
{
  if (readNextToken(f, buffer) == EOF || sscanf(buffer, "%f", s) != 1)
    return FALSE;
  if (readNextToken(f, buffer) == EOF || sscanf(buffer, "%f%*c", t) != 1)
    return FALSE;

  return TRUE;
}

int Vrml2Mesh::read(FILE *f, int (*update)(int pos),
		    void (*setTotal)(int size))
{
  list<Vertex*> vertices;
  char s1[101], s2[21];

  updateProgress=update;

  fseek(f, 0, SEEK_END);
  (*setTotal)(ftell(f));
  fseek(f, 0, SEEK_SET);

  if (fscanf(f,"%100s %20s", s1, s2) != 2) // Read header
    {
      FILE_ERROR(f, "VRML header format error");
      return 2;
    }

  while (fgetc(f) != '\n') // Reads till end of the line
    ;

  // check header
  if (strcasecmp(s1,"#VRML") != 0 || strcasecmp(s2,"V2.0") != 0)
    {
      FILE_ERROR(f, "VRML2 header format error");
      return 3;
    }

  int error, res, shapeNr;

  error = shapeNr = 0;
  do
    {
      // look for a starting point
      do
	{
	  res = readNextToken(f, s1);
	}
      while (res != EOF && strcasecmp(s1,"Shape") != 0 && 
	     strcasecmp(s1,"Transform") != 0);

      if (strcasecmp(s1,"Shape") == 0)
	error = newShape(f, &vertices, ++shapeNr);

      if (strcasecmp(s1,"Transform") == 0)
	error = newTransform(f, &vertices, &shapeNr, 1);

    }
  while (res != EOF);

  return error;
}

int Vrml2Mesh::newShape(FILE *f, list<Vertex*> *vertices, int shapeNr)
{
  list<Triangle*> *shapeTriangles;
  list<Vertex*> *shapeVertices;
  vector<Vertex*> vertexIndex;
  vector< pair<float, float>* > shapeTexture;
  list<int> shapeTextureIndex;
  int res, brakesCount;
  char textureName[100], s1[101];

  shapeTriangles = new list<Triangle*>;
  shapeVertices = new list<Vertex*>;
  textureName[0]=0;

  brakesCount=0;
  do
    {
      res = readNextToken(f, s1);

      if (strcmp(s1,"}") == 0)
	brakesCount--;
      if (strcmp(s1,"{") == 0)
	brakesCount++;
      
      // look for texture
      if (strcasecmp(s1,"ImageTexture") == 0)
	{
	  // Find the file name
	  while (strcasecmp(s1,"url") != 0)
	    res=fscanf(f,"%100s",s1);
	  res=fscanf(f,"%100s", s1);
	  if (strcasecmp(s1,"[") == 0)
	    res=fscanf(f,"%100s", s1);

	  // remove heading and following \"
	  if (s1[0] == '\"')
	    strcpy(textureName, s1+1);
	  else
	    strcpy(textureName, s1);
	  if (textureName[strlen(textureName)-1] == '\"')
	    textureName[strlen(textureName)-1]=0;

	  // Reads till end of texture
	  while (fgetc(f) != '}') ;
	}
	
      // look for points
      if (strcasecmp(s1,"coord") == 0)
	{
	  res=fscanf(f,"%100s",s1);

	  // defining a point set
	  if (strcasecmp(s1,"def") == 0)
	    ; // fix me !!!
	  // using an old points set
	  if (strcasecmp(s1,"use") == 0)
	    ; // fix me !!!
	  // normal points
	  else
	    {
	      Vertex *v;
	      float x, y, z;

	      // Reads till begin of points
	      while (fgetc(f) != '[') ;

	      while (readVertex(f, &x, &y, &z))
		{
		  v = new Vertex(x,y,z);
		  addVertex(v);

		  // save in a vector for the triangles, shape and transform
                  vertexIndex.push_back(v);
		}

	      // Reads till end of coord
	      while (fgetc(f) != '}') ;
	    }
 	}

      // look for triangles
      if (strcasecmp(s1,"coordIndex") == 0 && vertexIndex.size() > 0)
	{
	  Triangle *tri;
	  int p1, p2, p3, end;

	  // Reads till begin of triangles
	  while (fgetc(f) != '[') ;

	  while (readTriangle(f, &p1, &p2, &p3))
	    {
	      fscanf(f,"%d%*c",&end);
	      if (end == -1)
		{
		  // end (-1) is reached => number of points is 3 => ok
		  tri = new Triangle(vertexIndex[p1],
				     vertexIndex[p2],
				     vertexIndex[p3]);
		  addTriangle(tri);

		  shapeTriangles->push_back(tri);
		}
	      else
		// no end (-1) => seek to end (-1)
		while (end != -1 && res == 1)
		  res=fscanf(f,"%d%*c",&end);
	    }
  	}

      // look for texture coordinates
      if (strcasecmp(s1,"texCoord") == 0 && vertexIndex.size() > 0)
	{
	  float s, t;

	  // Reads till begin of texture coordinates
	  while (fgetc(f) != '[') ;
		 
	  while (readTextCoord(f, &s, &t))
	    shapeTexture.push_back(new pair<float,float>(s,t));

	  // Reads till end of coord
	  while (fgetc(f) != '}') ;
	}

      // look for texture coordinate index
      if (strcasecmp(s1,"texCoordIndex") == 0 && vertexIndex.size() > 0)
	{
	  int p1, p2, p3, end;

	  fscanf(f,"%100s", s1);
	  if (strcasecmp(s1,"[") == 0)
	    while (readTriangle(f, &p1, &p2, &p3))
	      {
		fscanf(f,"%d%*c",&end);
		if (end == -1)
		  {
		    // end (-1) is reached => number of points is 3 => ok
		    shapeTextureIndex.push_back(p1);
		    shapeTextureIndex.push_back(p2);
		    shapeTextureIndex.push_back(p3);
		  }
		else
		  // no end (-1) => seek to end (-1)
		  while (end != -1 && res == 1)
		    res=fscanf(f,"%d%*c",&end);
	      }
	}
    }
  while (res != EOF && brakesCount != 0);

  // copy the vertices of this shape over
  // once for the shape and once for the transform
  shapeVertices->insert(shapeVertices->end(), 
			vertexIndex.begin(), vertexIndex.end());
  vertices->insert(vertices->end(), vertexIndex.begin(), vertexIndex.end());
      
  vector< pair<float, float>* >::iterator itex;

  if (shapeTriangles->size() > 0 && shapeTexture.size() > 0)
    // assignment of texture coordiantes with texture index
    if (shapeTextureIndex.size() != 0)
      {
	list<Triangle*>::iterator it;
	list<int>::iterator itexi;

	itexi=shapeTextureIndex.begin();
	for (it=shapeTriangles->begin(); it != shapeTriangles->end(); it++)
	  for (int i=0; i < 3; i++, itexi++)
	    (*it)->setTextCoordinates(i, shapeTexture[*itexi]);
      }
    // assignment of texture coordiantes without texture index
    else
      {
	  vector<Vertex*>::iterator iv;

	  itex=shapeTexture.begin();
	  for(iv=vertexIndex.begin(); iv != vertexIndex.end(); iv++, itex++)
              (*iv)->setTextCoordinates(*itex);
       }

  for(itex=shapeTexture.begin(); itex != shapeTexture.end(); itex++)
    delete (*itex);

  //  printf(" Shape: %d vertices and %d trinagles\n", shapeVertices.size(),
  //	 shapeTriangles->size());

  shapes->push_back(new Shape(shapeTriangles, shapeVertices, textureName));

  return 0;
}

int Vrml2Mesh::newTransform(FILE *f, list<Vertex*> *vertices, int *shapes, int iter)
{
  list<Vertex*> transformVertices;
  list<Vertex*>::iterator iv;
  float trans[3], rota[4], scale[3];
  int res, brakesCount;
  char s1[100];

  // init transform variables
  trans[0]=trans[1]=trans[2]=0.0;
  rota[0]=rota[1]=rota[2]=rota[3]=0.0;
  scale[0]=scale[1]=scale[2]=1.0;

  //  for (int i=0; i < iter; i++)
  //    putchar(' ');
  //  printf("Transform...\n");

  res = brakesCount = 0;
  do
    {
      res = readNextToken(f, s1);

      if (strcmp(s1,"}") == 0)
	brakesCount--;
      if (strcmp(s1,"{") == 0)
	brakesCount++;

      // looking for transformation stuff
      if (strcasecmp(s1,"translation") == 0)
	res=fscanf(f,"%f %f %f",&trans[0],&trans[1],&trans[2]);
      if (strcasecmp(s1,"rotation") == 0)
	res=fscanf(f,"%f %f %f %f",&rota[0],&rota[1],&rota[2],&rota[3]);
      if (strcasecmp(s1,"scale") == 0)
	res=fscanf(f,"%f %f %f",&scale[0],&scale[1],&scale[2]);

      // looking for shapes
      if (strcasecmp(s1,"Shape") == 0)
	if (newShape(f, &transformVertices, ++(*shapes)) != 0)
	  return 1;

      // looking for transforms
      if (strcasecmp(s1,"Transform") == 0)
	if (newTransform(f, &transformVertices, shapes, iter+1) != 0)
	  return 1;
    }
  while (res != EOF && brakesCount != 0);

  // built the rotation matrix r
  float B[3][3] = {{0, -rota[2], rota[1]},
		   {rota[2], 0, -rota[0]},
		   {-rota[1], rota[0], 0}};
  float C[3][3] = {{rota[0]*rota[0], rota[0]*rota[1], rota[0]*rota[2]},
		   {rota[1]*rota[0], rota[1]*rota[1], rota[1]*rota[2]},
		   {rota[2]*rota[0], rota[2]*rota[1], rota[2]*rota[2]}};
  Matrix3<float> r, a, b(B), c(C);

  a.setIdentity();
  a.mul(cos(rota[3]));
  b.mul(sin(rota[3]));
  c.mul(1-cos(rota[3]));
  
  r.add(&a);
  r.add(&b);
  r.add(&c);
	
  // do rotation, scaling and translation
  for (iv=transformVertices.begin(); iv != transformVertices.end(); iv++)
  {
    (*iv)->rotate(&r);
    (*iv)->scale(scale[0], scale[1], scale[2]);
    (*iv)->move(trans[0], trans[1], trans[2]);

    // copy over
    vertices->push_back(*iv);
  }

  return 0;
}

void Vrml2Mesh::write(FILE *f, const char *comment)
{
  int n, i, j, isTextured;
  list<Triangle*>::iterator it;
  list<Triangle*> *shapeTriangles;
  list<Vertex*>::iterator iv;
  list<Vertex*> *shapeVertices;
  Vertex v;
  
  fprintf(f,"#VRML V2.0 utf8\n");
  fprintf(f,"# %s\n", comment);

  fprintf(f, "Group {\n");
  fprintf(f, "\tchildren [\n");

  for (i=0; i < numberOfShapes(); i++)
    {
      const char *textureName = getShape(i)->getTextureName();
      isTextured = textureName != NULL && strcmp(textureName,"") != 0;

      shapeVertices = getShape(i)->vertices;
      shapeTriangles = getShape(i)->triangles;

      fprintf(f,"Shape {\n");
      fprintf(f,"\tappearance Appearance {\n");
      if (isTextured)
	{
	  fprintf(f, "\t\ttexture ImageTexture {\n");
	  fprintf(f, "\t\t\turl \"%s\"\n", textureName);
	  fprintf(f, "\t\t}\n");
	}
      fprintf(f,"\t}\n");

      fprintf(f,"\tgeometry IndexedFaceSet {\n");
      fprintf(f,"\t\tcoord Coordinate {\n");
      fprintf(f,"\t\t\tpoint [");
      if (shapeVertices != NULL && shapeVertices->size() > 0)
	{
	  n=0;
	  for (iv=shapeVertices->begin(); iv != shapeVertices->end(); iv++)
	    {
	      calcOriginalCoordinates(*iv, &v);
	      fprintf(f,"\n\t\t\t\t%f %f %f", v.x(), v.y(), v.z());
	      (*iv)->number=n;
	      n++;
	    }
	}
      else
	fprintf(f,"\n\t\t\t");
      fprintf(f," ]\n\t\t}\n");

      fprintf(f,"\t\tcoordIndex [");
      if (shapeTriangles != NULL && shapeTriangles->size() > 0)
	{
	  for (it=shapeTriangles->begin(); it != shapeTriangles->end(); it++)
	    fprintf(f,"\n\t\t\t\t%d, %d, %d, -1",
		    (*it)->vertices[0]->number, (*it)->vertices[1]->number,
		    (*it)->vertices[2]->number);
	}
      else
	fprintf(f,"\n\t\t");
      fprintf(f," ]\n");

      if (isTextured && shapeTriangles != NULL && shapeTriangles->size() > 0)
	{
	  map< Vertex*, pair<float, float> > textMap;
	  map< Vertex*, pair<float, float> >::iterator iTextMap;
	  pair<float, float> st;

	  // save texture coordinates per vertex
	  //   vertices only have one pair of texture coordinates
	  //   in one shape

	  for (it=shapeTriangles->begin(); it != shapeTriangles->end(); it++)
	    for (j=0; j < 3; j++)
	      textMap[ (*it)->vertices[j] ] = pair<float, float>
		((*it)->getTextS(j), (*it)->getTextT(j));

          fprintf(f,"\t\ttexCoord  TextureCoordinate {\n");
          fprintf(f,"\t\t\tpoint [");
	  for (iv=shapeVertices->begin(); iv != shapeVertices->end(); iv++)
	    {
	      iTextMap=textMap.find(*iv);
	      if (iTextMap == textMap.end())
		fprintf(f,"\n\t\t\t\t0.0 0.0");
	      else
		{
		  st = (*iTextMap).second;
		  fprintf(f,"\n\t\t\t\t%f %f", st.first, st.second);
		}
	    }
          fprintf(f," ]\n\t\t}\n");

          fprintf(f,"\t\ttexCoordIndex [");
	  for (it=shapeTriangles->begin(); it != shapeTriangles->end(); it++)
	    fprintf(f,"\n\t\t\t\t%d, %d, %d, -1",
		    (*it)->vertices[0]->number, (*it)->vertices[1]->number,
		    (*it)->vertices[2]->number);
          fprintf(f," ]\n");

	  // alternatively, save texture coordinates per triangle
	  //   produces much bigger vrml files

	  /*
	  fprintf(f,"\t\ttexCoord  TextureCoordinate {\n");
	  fprintf(f,"\t\t\tpoint [\n");

	  for (it=shapeTriangles->begin(); it != shapeTriangles->end(); it++)
	    for (j=0; j < 3; j++)
	      fprintf(f,"\t\t\t\t%f %f", (*it)->getTextS(j),
		      (*it)->getTextT(j));
	  fprintf(f," ]\n\t\t}\n");

	  fprintf(f,"\t\ttexCoordIndex [\n");
	  n=0;
	  for (it=shapeTriangles->begin(); it != shapeTriangles->end(); it++)
	    for (j=0; j < 3; j++, n+=3)
	      fprintf(f,"\t\t\t\t%d, %d, %d, -1", n, n+1, n+2);

	  fprintf(f," ]\n");
	  */
	}

      fprintf(f,"\t\t}\n}\n");
    }

  fprintf(f, "\t]\n}\n");
}
