// A module for reading simple obj files with groups.
// Authors: Jens Schwaiger and John Bowman
//
// Here simple means that :
//
// 1) all vertex statements should come before the face statements;
//
// 2) face informations with respect to texture and/or normal vectors are
// ignored;
//
// 3) face statements only contain positive numbers(no relative positions).
//
// The reading process only takes into account lines starting with "v" or
// "f" or "g"(group).

import three;

struct obj {
  surface s;
  material[] surfacepen;
  pen[] meshpen;

  path3[][] read(string datafile, bool verbose=false) {
    file in=input(datafile).word().line();
    triple[] vert;
    path3[][] g;
    g[0]=new path3[] ;
    string[] G;
    void Vertex(real x,real y ,real z) {vert.push((x,y,z));}
    void Face(int[] vertnr, int groupnr) {
      guide3 gh;
      for(int i=0; i < vertnr.length; ++i)
        gh=gh--vert[vertnr[i]-1];
      gh=gh--cycle;
      g[groupnr].push(gh);
    }
    if(verbose) write("Reading data from "+datafile+".");
    int groupnr;
    while(true) {
      string[] str=in;
      if(str.length == 0) break;
      str=sequence(new string(int i) {return split(str[i],"/")[0];},str.length);
      if(str[0] == "g" && str.length > 1) {
        int tst=find(G == str[1]);
        if(tst == -1) {
          G.push(str[1]);
          groupnr=G.length-1;
          g[groupnr]=new path3[] ;
        }
        if(tst > -1) groupnr=tst;
      }
      if(str[0] == "v") Vertex((real) str[1],(real) str[2],(real) str[3]);
      if(str[0] == "f") {
        int[] vertnr;
        for(int i=1; i < str.length; ++i) vertnr[i-1]=(int) str[i];
        Face(vertnr,groupnr);
      }
      if(eof(in)) break;
    }
    close(in);
    if(verbose) {
      write("Number of groups: ",G.length);
      write("Groups and their names:");
      write(G);
      write("Reading done.");
      write("Number of faces contained in the groups: ");
      for(int j=0; j < G.length; ++j)
        write(G[j],": ",(string) g[j].length);
    }
    return g;
  }

  void operator init(path3[][] g, material[] surfacepen, pen[] meshpen) {
    for(int i=0; i < g.length; ++i) {
      path3[] gi=g[i];
      for(int j=0; j < gi.length; ++j) {
        // Treat all faces as planar to avoid subdivision cracks.
        surface sij=surface(gi[j],planar=true);
        s.append(sij);
        this.surfacepen.append(array(sij.s.length,surfacepen[i]));
        this.meshpen.append(array(sij.s.length,meshpen[i]));
      }
    }
  }

  void operator init(string datafile, bool verbose=false,
                     material[] surfacepen, pen[] meshpen=nullpens) {
    operator init(read(datafile,verbose),surfacepen,meshpen);
  }

  void operator init(string datafile, bool verbose=false,
                     material surfacepen, pen meshpen=nullpen) {
    material[] surfacepen={surfacepen};
    pen[] meshpen={meshpen};
    surfacepen.cyclic=true;
    meshpen.cyclic=true;
    operator init(read(datafile,verbose),surfacepen,meshpen);
  }
}

obj operator * (transform3 T, obj o)
{
  obj ot;
  ot.s=T*o.s;
  ot.surfacepen=copy(o.surfacepen);
  ot.meshpen=copy(o.meshpen);
  return ot;
}

void draw(picture pic=currentpicture, obj o, light light=currentlight)
{
  draw(pic,o.s,o.surfacepen,o.meshpen,light);
}
