#ifndef __VCGLIB_EXPORT_RIB
#define __VCGLIB_EXPORT_RIB

#define RIB_EXPORT_STEPS 7

#include<wrap/io_trimesh/io_mask.h>
#include<wrap/callback.h>
#include<vcg/complex/algorithms/clean.h>


#include <stdio.h>
#include <QTime>

namespace vcg {
namespace tri {
namespace io {


template <class SaveMeshType>
class ExporterRIB
{

public:
typedef typename SaveMeshType::VertexPointer VertexPointer;
typedef typename SaveMeshType::ScalarType ScalarType;
typedef typename SaveMeshType::VertexType VertexType;
typedef typename SaveMeshType::FaceType FaceType;
typedef typename SaveMeshType::FacePointer FacePointer;
typedef typename SaveMeshType::VertexIterator VertexIterator;
typedef typename SaveMeshType::FaceIterator FaceIterator;

static int Save(SaveMeshType &m,  const char * filename, bool binary, CallBackPos *cb=0) {
  return Save(m,filename, Mask::IOM_ALL, binary, cb);
}

static int Save(SaveMeshType &mm,  const char * filename, int savemask, bool /*binary*/, CallBackPos *cb=0)
{
  //ignore binary for now!

  //working with a copy of mesh
  SaveMeshType &m = mm;

  int cbStep = 100/RIB_EXPORT_STEPS, cbValue = 0, step = 0;
  if(*cb != 0) cb(cbValue, "Start exporting");
  
  FILE *fout = fopen(filename,"wb");
  if(fout==NULL) {
    return E_CANTOPEN;
  }

  QTime tt; tt.start(); //debug

  fprintf(fout,"#generated by VCGLIB\n"); //commento d'intestazione????

  //initial declaring
  if(m.HasPerVertexColor() && (savemask & Mask::IOM_VERTCOLOR))
    fprintf(fout,"Declare \"Cs\" \"facevarying color\"\n");

  if((HasPerVertexTexCoord(m) && (savemask & Mask::IOM_VERTTEXCOORD)) || 
	  (HasPerWedgeTexCoord(m) && (savemask & Mask::IOM_WEDGTEXCOORD)))
    fprintf(fout,"Declare \"st\" \"facevarying float[2]\"\n");
  
  if(HasPerVertexNormal(m) && (savemask & Mask::IOM_VERTNORMAL))
    fprintf(fout,"Declare \"N\" \"facevarying normal\"\n");

  if(HasPerVertexQuality(m) && (savemask & Mask::IOM_VERTQUALITY))
    fprintf(fout,"Declare \"Q\" \"facevarying float\"\n");

  tri::Clean<SaveMeshType>::RemoveUnreferencedVertex(m);
  Allocator<SaveMeshType>::CompactVertexVector(m);
  Allocator<SaveMeshType>::CompactFaceVector(m);
  
  //first step: faces topology
  fprintf(fout,"PointsPolygons\n[\n");
  int incr = m.fn/cbStep, i=0;
  for(i=0; i<m.fn; i++) {
    fprintf(fout,"3 ");//\n");
  	if((*cb != 0) && i%incr == 0) cb(++cbValue, "Exporting face topology");
  }
  fprintf(fout,"\n");
  fprintf(fout,"]\n[\n");
  qDebug("PointsPolygons %i",tt.elapsed());
  cbValue = (++step)*cbStep; i=0;
 
  //second step: index of vertex for face
  UpdateFlags<SaveMeshType>::VertexClearV(m);
  for(FaceIterator fi=m.face.begin(); fi!=m.face.end(); ++fi, ++i) {
	  if((*cb != 0) && i%incr == 0) cb(++cbValue, "Exporting index of verteces");
    for(int j=0; j<3; ++j) {
      int indexOfVertex = (*fi).V(j) - &(m.vert[0]);
	    fprintf(fout,"%i ",indexOfVertex);
	    //if it's the first visit, set visited bit
	    if(!(*fi).V(j)->IsV()) {
		    (*fi).V(j)->SetV();
	    }
	  }
	  //fprintf(fout,"\n");
	  fprintf(fout," ");
  }
  fprintf(fout,"\n]\n");
  qDebug("coords %i",tt.elapsed());
  cbValue = (++step)*cbStep; i=0;

  //third step: vertex coordinates
  fprintf(fout,"\"P\"\n[\n");
  Matrix44f mat = Matrix44f::Identity();
  mat = mat.SetScale(1.0,1.0,1.0);
  incr = m.vn/cbStep;
  for(VertexIterator vi=m.vert.begin(); vi!=m.vert.end(); ++vi, ++i) {
    if((*cb != 0) && i%incr == 0) cb(++cbValue, "Exporting vertex coordinates");
    if(vi->IsV()) {
	    Point3f p = mat * vi->P();
	    fprintf(fout,"%g %g %g ",p[0],p[1],p[2]);
	  }
  }
  fprintf(fout,"\n]\n");
  qDebug("coords %i",tt.elapsed());

  incr = m.fn/cbStep; cbValue = (++step)*cbStep; i=0;
  //fourth step: vertex normal
  if(HasPerVertexNormal(m) && (savemask & Mask::IOM_VERTNORMAL)) {
    fprintf(fout,"\"N\"\n[\n");
    for(FaceIterator fi=m.face.begin(); fi!=m.face.end(); ++fi, ++i) {
	    if((*cb != 0) && i%incr == 0) cb(++cbValue, "Exporting vertex normals");
	    //for each face, foreach vertex write normal
	    for(int j=0; j<3; ++j) {			
        Point3f n = mat * (*fi).V(j)->N(); //transform normal too
        fprintf(fout,"%g %g %g ",n[0],n[1],n[2]);
	    }
	  }
	  fprintf(fout,"\n]\n");
	  qDebug("normal %i",tt.elapsed());	
  }
  cbValue = (++step)*cbStep; i=0;

  //fifth step: vertex color (ignore face color?)
  if(m.HasPerVertexColor() && (savemask & Mask::IOM_VERTCOLOR)) {
    fprintf(fout,"\"Cs\"\n[\n");
	  for(FaceIterator fi=m.face.begin(); fi!=m.face.end(); ++fi, ++i) {
	    if((*cb != 0) && i%incr == 0) cb(++cbValue, "Exporting vertex colors");
	    //for each face, foreach vertex write color
	    for(int j=0; j<3; ++j) {
	      Color4b &c=(*fi).V(j)->C();
    		fprintf(fout,"%g %g %g ",float(c[0])/255,float(c[1])/255,float(c[2])/255);
   	  }
	  }
	  fprintf(fout,"\n]\n");
	  qDebug("color %i",tt.elapsed());
  }
  cbValue = (++step)*cbStep; i=0;

  //sixth step: texture coordinates (for edge)
  if((HasPerVertexTexCoord(m) && (savemask & Mask::IOM_VERTTEXCOORD)) || 
	  (HasPerWedgeTexCoord(m) && (savemask & Mask::IOM_WEDGTEXCOORD))) {
  	fprintf(fout,"\"st\"\n[\n"); i=0;
	  for(FaceIterator fi=m.face.begin(); fi!=m.face.end(); ++fi, ++i) {
      if((*cb != 0) && i%incr == 0) cb(++cbValue, "Exporting vertex/edge texture coordinates");
	    //for each face, foreach vertex write uv coord
	    for(int j=0; j<3; ++j) {
	      fprintf(fout,"%g %g ",(*fi).WT(j).U() , 1.0 - (*fi).WT(j).V()); //v origin axis is up
	    }
	  }
	  fprintf(fout,"\n]\n");
	  qDebug("texcoords %i",tt.elapsed());
  }
  cbValue = (++step)*cbStep; i=0;

  //seventh step: vertex quality
  if(HasPerVertexQuality(m) && (savemask & Mask::IOM_VERTQUALITY)) {
	  fprintf(fout,"\"Q\"\n[\n"); i=0;
	  for(FaceIterator fi=m.face.begin(); fi!=m.face.end(); ++fi, ++i) {
	    if((*cb != 0) && i%incr == 0) cb(++cbValue, "Exporting vertex quality");
	    //for each face, foreach vertex write its quality
	    for(int j=0; j<3; ++j) {
	      fprintf(fout,"%g ",(*fi).V(j)->Q());
  	  }
  	}
  	fprintf(fout,"\n]\n");
  	qDebug("quality %i",tt.elapsed());
  }
  cb(100, "Exporting completed");
  fclose(fout);

  return E_NOERROR;
}

enum RibError {
  E_NOERROR,				// 0
		// Errors of  open(..)
	E_CANTOPEN,				// 1
	E_MAXRIBERRORS
};

static const char *ErrorMsg(int error) {
  static std::vector<std::string> rib_error_msg;
  if(rib_error_msg.empty())
  {
    rib_error_msg.resize(E_MAXRIBERRORS);
    rib_error_msg[E_NOERROR		]="No errors";
	  rib_error_msg[E_CANTOPEN  ]="Can't open file";    
  }

  if(error > E_MAXRIBERRORS || error < 0)
    return "Unknown error";
  else
    return rib_error_msg[error].c_str();
};

 static int GetExportMaskCapability() {
	  int capability = 0;			
	  capability |= vcg::tri::io::Mask::IOM_VERTCOORD    ;
	  //capability |= vcg::tri::io::Mask::IOM_VERTFLAGS    ;
	  capability |= vcg::tri::io::Mask::IOM_VERTCOLOR    ;
	  capability |= vcg::tri::io::Mask::IOM_VERTQUALITY  ;
	  capability |= vcg::tri::io::Mask::IOM_VERTNORMAL   ;
	  //capability |= vcg::tri::io::Mask::IOM_VERTRADIUS   ;
	  capability |= vcg::tri::io::Mask::IOM_VERTTEXCOORD ;
	  //capability |= vcg::tri::io::Mask::IOM_FACEINDEX    ;
	  //capability |= vcg::tri::io::Mask::IOM_FACEFLAGS    ;
	  //capability |= vcg::tri::io::Mask::IOM_FACECOLOR    ;
	  //capability |= vcg::tri::io::Mask::IOM_FACEQUALITY  ;
	  // capability |= vcg::tri::io::Mask::IOM_FACENORMAL   ;
	  capability |= vcg::tri::io::Mask::IOM_WEDGCOLOR    ;
	  capability |= vcg::tri::io::Mask::IOM_WEDGTEXCOORD ;
	  capability |= vcg::tri::io::Mask::IOM_WEDGTEXMULTI ;
    //capability |= vcg::tri::io::Mask::IOM_WEDGNORMAL   ;
	//	capability |= vcg::tri::io::Mask::IOM_CAMERA   ;
	  return capability;
  }


}; // end class



} // end namespace tri
} // end namespace io
} // end namespace vcg
//@}
#endif
