
/* ******************************************************************
   disclaimer:
   i.w. (wald@graphics.cs.uni-sb.de), feb 18th,2001.
   this file was adapted from the ply/plytriangulate.c file...

   note: it has shown to work with the unc powerplant model (which
   uses per-face-colors), and also with stanfords david head (which uses
   vertex colors). it has not (yet) been tested with anything else.
   ply-files that use different entities cannot currently be parsed, but
   will exit with a error message.
   currently supportet entities are vertices with position and/or normal and/or
   color, and faces with vertexlists and possible per-face colors.
   (which should probably rather be diffuse colors....)
   
   todo: 
   
   - per-face colors should (probably) be diffuse colors 

   - insert an option to put all ply-files into the same
   indexedfaceset (right now, each has its own)
   
   - find and remove duplicate colors and normals. (right now, each
   are specified per vertex, even if they are the same...  
   
*******************************************************************/

// #define IMPORT_PLYLIST     // define this symbol if you want parse() to
// parse a list of ply files instead of a single ply file

#include <stdio.h>
#include <math.h>
#include <strings.h>

extern "C" {
#include "ply.h"
}
#include <iostream.h>

#include "importer.H"
#include "IndexedFaceSet.H"
#include "Group.H"
#include "Geometry.H"
#include "Transform.H"
#include "Shape.H"
#include "Material.H"
#include "Appearance.H"

namespace ply {

#define FALSE           0
#define TRUE            1

#define X               0
#define Y               1
#define Z               2

PlyProperty vert_props[] = { /* list of property information for a vertex */
  {"x", PLY_FLOAT, PLY_FLOAT, offsetof(Vertex,pos[X]), 0, 0, 0, 0},
  {"y", PLY_FLOAT, PLY_FLOAT, offsetof(Vertex,pos[Y]), 0, 0, 0, 0},
  {"z", PLY_FLOAT, PLY_FLOAT, offsetof(Vertex,pos[Z]), 0, 0, 0, 0},
  {"nx", PLY_FLOAT, PLY_FLOAT, offsetof(Vertex,nor[X]), 0, 0, 0, 0},
  {"ny", PLY_FLOAT, PLY_FLOAT, offsetof(Vertex,nor[Y]), 0, 0, 0, 0},
  {"nz", PLY_FLOAT, PLY_FLOAT, offsetof(Vertex,nor[Z]), 0, 0, 0, 0},
  {"diffuse_red", PLY_UCHAR, PLY_UCHAR, offsetof(Vertex,col[X]), 0, 0, 0, 0},
  {"diffuse_green", PLY_UCHAR, PLY_UCHAR, offsetof(Vertex,col[Y]), 0, 0, 0, 0},
  {"diffuse_blue", PLY_UCHAR, PLY_UCHAR, offsetof(Vertex,col[Z]), 0, 0, 0, 0},
};

PlyProperty face_props[] = { /* list of property information for a face */
  {"vertex_indices", PLY_INT, PLY_INT, offsetof(Face,verts),1, PLY_UCHAR, PLY_UCHAR, offsetof(Face,nverts)},
  {"red", PLY_UCHAR, PLY_UCHAR, offsetof(Face,col[X]),0, 0, 0, 0},
  {"green", PLY_UCHAR, PLY_UCHAR, offsetof(Face,col[Y]),0, 0, 0, 0},
  {"blue", PLY_UCHAR, PLY_UCHAR, offsetof(Face,col[Z]),0, 0, 0, 0},
};

static int nelems;
static char **element_list;
static int num_comments;
static char **comments;
static int num_obj_info;
static char **obj_info;
static int file_type;

/******************************************************************************
vrml stuff.
******************************************************************************/

void importer::Finalize()
{
  if (vtxPos) { delete vtxPos; vtxPos = NULL; };
  if (vtxNor) { delete vtxNor; vtxNor = NULL; };
  if (vtxCol) { delete vtxCol; vtxCol = NULL; };
  if (faceCol) { delete faceCol; faceCol = NULL; };
  if (coordIndex) { delete coordIndex; coordIndex = NULL; };
}

void importer::PrepareFaces(Face &fType,int nfaces)
{
  cerr << "preparing faces" << endl;
  if (fType.colValid) 
    faceCol = new MFColor(nfaces);
  
  coordIndex = new MFInt32;
  static int totalFaces = 0;
  totalFaces += nfaces;
  cerr << "total faces : " << totalFaces << endl;
}

void importer::PrepareVertices(Vertex &vType, int nverts)
{ 
  cerr << "preparing vertices" << endl;
  if (vType.posValid) {
    cerr << nverts << endl;
    vtxPos = new MFVec3f(nverts);
  } else
    vtxPos = NULL;
  
  if (vType.norValid)
    vtxNor = new MFVec3f(nverts);
  else
    vtxNor = NULL;
  
  if (vType.colValid) 
    vtxCol = new MFColor(nverts);
}
 
importer::importer()
{ 
  Init(); 
}

void importer::Init() 
{
  vtxPos = NULL;
  vtxCol = NULL;
  vtxNor = NULL;
  
  faceCol = NULL;
}

void importer::ImportSinglePly(FILE *file, world *world)
{
  Init();
  if (!world->sceneGraph)
    world->sceneGraph = new MFNode;
  
  Material *material = new Material;
  SFColor gray(.6,.6,.6); // get a color to assign
  material->assign_ambientIntensity(SFFloat(.6));
  material->assign_diffuseColor(gray); // set the diffuse color to gray
  material->assign_emissiveColor(gray);// set the emissive color to gray
  
  Appearance *appearance = new Appearance;
  appearance->assign_material(material);
  
  cerr << "calling the file-reader..." << endl;
  ReadPlyFile(file);
  
  IndexedFaceSet *ifs = new IndexedFaceSet;
  
  Shape *shape = new Shape;
  
  if (vtxPos) {
    Coordinate *coord = new Coordinate;
    coord->assign_point(*vtxPos); 
    ifs->assign_coord(coord);
  }
  
  if (vtxNor) {
    Normal *normal = new Normal;
    normal->assign_vector(*vtxNor); 
    ifs->assign_normal(normal);
  }
  
  if (vtxCol) {
    Color *color = new Color;
    color->assign_color(*vtxCol); 
    ifs->assign_color(color);
    ifs->assign_colorPerVertex(TRUE);
  } else if (faceCol) {
    Color *color = new Color;
    color->assign_color(*faceCol); 
    ifs->assign_color(color);
    ifs->assign_colorPerVertex(FALSE);
  }
  
  if (coordIndex)
    ifs->assign_coordIndex(*coordIndex);
  
  shape->assign_geometry(ifs);
  shape->assign_appearance(appearance);
  world->sceneGraph->append(shape);
  Finalize();
}

void importer::ImportPly(file *file,
			    world *world)
{
  //  world->make_current();  ---- world->parse() already does this
  
#ifdef IMPORT_PLYLIST
  // if defined, the input file contains only a _LIST_OF_ ply files,
  // which are opened and parsed in turn...
  char line[2000];
  while (fgets(line,2000,file->fp) && !feof(file->fp))
    {
      *strstr(line,"\n") = 0;
      FILE *f = fopen(line,"r");
      cerr << line << endl;
      if (!f) perror("");
      ImportSinglePly(f,world);
      fclose(f);
    }
#else
  // if we are here, the file we're reading is supposed to be a
  // valid ply file
  ImportSinglePly(file->fp,world);
#endif
  cerr << "done parsing: sceneGraph contains " << world->sceneGraph->size
       << " objects..." << endl;
}

void importer::AddVertex(Vertex &v) 
{
  v.pos[0] /= 10000.;
  v.pos[1] /= 10000.;
  v.pos[2] /= 10000.;
  
  SFVec3f pos(v.pos[X],v.pos[Y],v.pos[Z]);
  
  vtxPos->append(pos);
  
  if (v.colValid) {
    vtxCol->append(SFColor(v.col[0]/255.,v.col[1]/255.,v.col[2]/255.));
  }
  if (v.norValid) {
    vtxNor->append(SFVec3f(v.nor[0],v.nor[1],v.nor[2]));
  }
}

void importer::AddFace(Face &f)
{
  for (int i=0;i<f.nverts;i++)
    coordIndex->append(f.verts[i]);
  coordIndex->append(-1);
  
  if (f.colValid) {
    faceCol->append(SFColor(f.col[0]/255.,f.col[1]/255.,f.col[2]/255.));
  }
}

void importer::ReadPlyFile(FILE *file)
{
  int i,j;
  PlyFile *ply;
  int nprops;
  int num_elems;
  PlyProperty **plist;
  char *elem_name;
  float version;
    
  /*** Read in the original PLY object ***/
    
    
  ply  = ply_read (file, &nelems, &element_list);
  ply_get_info (ply, &version, &file_type);
    
  for (i = 0; i < nelems; i++) {
	
    /* get the description of the first element */
    elem_name = element_list[i];
    plist = ply_get_element_description (ply, elem_name, &num_elems, &nprops);
	
    if (equal_strings ("vertex", elem_name))
      {
	Vertex thisVtx;
	thisVtx.Reset();
	    
	int nverts = num_elems;
	bool has_x, has_y, has_z;
	has_x = has_y = has_z = FALSE;

	for (j=0; j<nprops; j++)
	  {
	    if (equal_strings("x", plist[j]->name)) {
	      ply_get_property (ply, elem_name, &vert_props[0]);  /* x */
	      has_x = TRUE;
	    } else if (equal_strings("y", plist[j]->name)) {
	      ply_get_property (ply, elem_name, &vert_props[1]);  /* y */
	      has_y = TRUE;
	    } else if (equal_strings("z", plist[j]->name)) {
	      ply_get_property (ply, elem_name, &vert_props[2]);  /* z */
	      has_z = TRUE;
	    } else if (equal_strings("nx", plist[j]->name)) {
	      ply_get_property (ply, elem_name, &vert_props[3]);  /* nx */
	      thisVtx.norValid = true; // assume the rest follows...
	    } else if (equal_strings("ny", plist[j]->name)) {
	      ply_get_property (ply, elem_name, &vert_props[4]);  /* nx */
	    } else if (equal_strings("nz", plist[j]->name)) {
	      ply_get_property (ply, elem_name, &vert_props[5]);  /* nx */
	    } else if (equal_strings("diffuse_red", plist[j]->name)) {
	      ply_get_property (ply, elem_name, &vert_props[6]);  /* nx */
	      thisVtx.colValid = true; // assume green and blue follow...
	    } else if (equal_strings("diffuse_green", plist[j]->name)) {
	      ply_get_property (ply, elem_name, &vert_props[7]);  /* nx */
	    } else if (equal_strings("diffuse_blue", plist[j]->name)) {
	      ply_get_property (ply, elem_name, &vert_props[8]);  /* nx */
	    } else {
	      cerr << "unknown vertex property " << plist[j]->name << endl;
	      exit(0);
	    }
	  }
	
	/* test for necessary properties */
	if ((!has_x) || (!has_y) || (!has_z))
	  {
	    fprintf(stderr, "Vertices don't have x, y, and z\n");
	    exit(-1);
	  }
	thisVtx.posValid = TRUE;
	  
	PrepareVertices(thisVtx,nverts);
	for (j = 0; j < num_elems; j++) {
	  ply_get_element (ply, (void *) &thisVtx);
	  AddVertex(thisVtx);
	}
      }
      
    else if (equal_strings ("face", elem_name)) {
	
      int nfaces = num_elems;
      Face thisFace;
      thisFace.Reset();

      bool has_fverts = FALSE;
      for (j=0; j<nprops; j++) {
	if (equal_strings("vertex_indices", plist[j]->name)) {
	  ply_get_property(ply, elem_name, &face_props[0]);/* vertex_indices */
	  has_fverts = TRUE;
	} else if (equal_strings("red", plist[j]->name)) {
	  ply_get_property(ply, elem_name, &face_props[1]);/* vertex_indices */
	  thisFace.colValid = true; // assume the rest to follow...
	} else if (equal_strings("green", plist[j]->name)) {
	  ply_get_property(ply, elem_name, &face_props[2]);/* vertex_indices */
	} else if (equal_strings("blue", plist[j]->name)) {
	  ply_get_property(ply, elem_name, &face_props[3]);/* vertex_indices */
	} else {
	  cerr << "unknown face property : " << plist[j]->name << endl;
	  exit(1);
	}
      }
      if (!has_fverts) {
	fprintf(stderr, "Faces must have vertex indices\n");
	exit(-1);
      }

      PrepareFaces(thisFace,nfaces);
      for (j = 0; j < num_elems; j++) {
	ply_get_element (ply, (void *) &thisFace);
	AddFace(thisFace);
      }
    } else {
      cerr << "unknown ply element " << elem_name << endl;
      exit(1);
    }
  }
  
  comments = ply_get_comments (ply, &num_comments);
  obj_info = ply_get_obj_info (ply, &num_obj_info);
  
  //   ply_close (ply);
}

importer* importer::instantiate(file* file_handle)
{
  // TODO: no checking is done, we simply assume the input file is a valid ply file
  return new ply::importer;
}

bool importer::parse(world* world, char **strpool)
{
  *strpool = 0;
  ImportPly(world->file, world);
  return true;
}

}  // namespace ply
