#include <bfastVector.H>
#include <bfastMatrix.H>
#include <vector>
#include <bfastUtil.H>


class SimpleTri {
public:
  int indices[3];
  inline int &operator[](const unsigned int &i) { return indices[i];};
  inline int operator[](const unsigned int &i) const { return indices[i];};
  inline void set(int x, int y, int z) {indices[0] = x; indices[1] = y; indices[2] = z;};
  inline SimpleTri(int x, int y, int z) {indices[0] = x; indices[1] = y; indices[2] = z;};
  inline SimpleTri() {};
};

class SimpleEdge {
public:
  int indices[2];
  inline int &operator[](const unsigned int &i) { return indices[i];};
  inline int operator[](const unsigned int &i) const { return indices[i];};
  inline SimpleEdge(int a, int b) {indices[0] = a; indices[1] = b;};
  SimpleEdge(){};
};

struct eqSimpleTri {
  bool operator()(const SimpleTri &t1, const SimpleTri &t2) const {
    return ((t1[0] == t2[0] && t1[1] == t2[1] && t1[2] == t2[2]) ||
	    (t1[1] == t2[0] && t1[0] == t2[1] && t1[2] == t2[2]) ||
	    (t1[2] == t2[0] && t1[1] == t2[1] && t1[0] == t2[2]) ||
	    (t1[0] == t2[0] && t1[2] == t2[1] && t1[1] == t2[2]) ||
	    (t1[1] == t2[0] && t1[2] == t2[1] && t1[0] == t2[2]) ||
	    (t1[2] == t2[0] && t1[0] == t2[1] && t1[1] == t2[2]));
  }
};

struct hashSimpleTri {
  size_t operator()(const SimpleTri& t) const {
    return (t[0] ^ t[1] ^ t[2]);
  }
};

typedef HASH_MAP<SimpleTri, SimpleEdge, hashSimpleTri, eqSimpleTri> TriToTetMap;

inline void updateTriToTetMap(TriToTetMap &triToTetMap, int n, int x, int y, int z) {
  SimpleTri tri(x, y, z);
  if (triToTetMap.count(tri) == 0) {
    SimpleEdge e(n, -1);
    triToTetMap.insert(TriToTetMap::value_type(tri,e));
  } else {
    triToTetMap.find(tri)->second[1] = n;
  }
}


bool readInputFile(const char *fname, std::vector<SimpleNode> &nodes,
		   std::vector<SimpleTet> &tets) {
  char ch;
  BfastVector3 lc(BFAST_REAL_MAX), uc(-BFAST_REAL_MAX);
  SimpleNode n;
  SimpleTet t;
  
  std::ifstream in(fname, std::ios::in);
  while (in>>ch) {
    if (ch == 'v') {
      in>>n.pos[0]>>n.pos[1]>>n.pos[2];
      nodes.push_back(n);
      continue;
    }
    if (ch == 't') {
      in>>t[0]>>t[1]>>t[2]>>t[3];
      tets.push_back(t);
      continue;
    }
  }
  in.close();
  return true;
}

bool extractSurface (const std::vector<SimpleNode> &nodes,
		     const std::vector<SimpleTet> &tets,
		     std::vector<SimpleTri> &tris) {
  TriToTetMap triToTetMap;
  unsigned int i;
  std::vector<SimpleTet>::iterator t;

  for (t=tets.begin(), i=0; t!=tets.end(); t++, i++) {
    updateTriToTetMap(triToTetMap, i, (*t)[0], (*t)[2], (*t)[1]);
    updateTriToTetMap(triToTetMap, i, (*t)[3], (*t)[0], (*t)[1]);
    updateTriToTetMap(triToTetMap, i, (*t)[3], (*t)[2], (*t)[0]);
    updateTriToTetMap(triToTetMap, i, (*t)[2], (*t)[3], (*t)[1]);
  }
  
  for (TriToTetMap::const_iterator m=triToTetMap.begin(); m!=triToTetMap.end(); m++) {
    if (m->second[1] == -1) {
      tris.push_back(m->first);
    }
  }
  return true;
}
