#include <fstream>
#include <osg/Geometry>

using namespace osg;
using std::cin;
using std::cout;
using std::endl;
using std::istream;

#if 0
void set_normal (Vec3f *normal, Vec3f v1, Vec3f v2, Vec3f v3)
{
  normal->set(((v1.y() - v2.y()) * (v2.z() - v3.z())) - ((v1.z() - v2.z()) * (v2.y() - v3.y())),
			  ((v1.z() - v2.z()) * (v2.x() - v3.x())) - ((v1.x() - v2.x()) * (v2.z() - v3.z())),
		      ((v1.x() - v2.x()) * (v2.y() - v3.y())) - ((v1.y() - v2.y()) * (v2.x() - v3.x())));
  normal->normalize();
}
#else
void set_normal (Vec3f *normal, Vec3f v1, Vec3f v2, Vec3f v3)
{
  normal->set(((v1.z() - v2.z()) * (v2.y() - v3.y())) - ((v1.y() - v2.y()) * (v2.z() - v3.z())),
			  ((v1.x() - v2.x()) * (v2.z() - v3.z())) - ((v1.z() - v2.z()) * (v2.x() - v3.x())),
			  ((v1.y() - v2.y()) * (v2.x() - v3.x())) - ((v1.x() - v2.x()) * (v2.y() - v3.y())));
  normal->normalize();
}
#endif

Geometry*
readOFF (istream& in)
{
  char ch;

  in >> ch; if (ch != 'O') return NULL;
  in >> ch; if (ch != 'F') return NULL;
  in >> ch; if (ch != 'F') return NULL;

  Geometry* geometry = new Geometry();
  Vec3Array* vertices = new Vec3Array;
  Vec3Array* normals = new Vec3Array;
  IntArray* quads = new IntArray;
  IntArray* tris = new IntArray;
  IntArray* normal_map = new IntArray;
  unsigned int num_quads, num_tris;

  int num_vertices;
  in >> num_vertices;

  int num_faces;
  in >> num_faces;

  int num_edges;
  in >> num_edges;

  cout << "Read header." << endl;

  for (int i = num_vertices; i > 0; --i)
	{
	  float x, y, z;
	  in >> x >> y >> z;
	  vertices->push_back(Vec3(x, y, z));
	}
  cout << "Read " << num_vertices << " vertices." << endl;

  for (int i = num_faces; i > 0; --i)
	{
	  int num, a, b, c;
	  in >> num >> a >> b >> c;
	  switch (num)
		{
		  case 3:
		    {
			  Vec3f* normal = new Vec3f;
			  Vec3Array& rvertices = *vertices;
			  tris->push_back(a);
			  tris->push_back(b);
			  tris->push_back(c);
			  set_normal(normal, rvertices[a], rvertices[b], rvertices[c]);
			  normals->push_back(*normal);
			  normal_map->push_back(normals->size());
			  break;
			}
		  case 4:
		    {
			  Vec3f* normal1 = new Vec3f;
			  Vec3f* normal2 = new Vec3f;
			  Vec3Array& rvertices = *vertices;
			  quads->push_back(a);
			  quads->push_back(b);
			  quads->push_back(c);
			  set_normal(normal1, rvertices[a], rvertices[b], rvertices[c]);
			  in >> b;
			  quads->push_back(b);
			  set_normal(normal2, rvertices[c], rvertices[b], rvertices[a]);
	          // FIX
			  //normals->push_back((*normal1 + *normal2) / 2.0);
			  normals->push_back(*normal1);
			  normal_map->push_back(normals->size());
			  break;
			}
		}
	}

  num_quads = quads->size();
  num_tris = tris->size();
  cout << "Read " << num_quads + num_tris << " faces." << endl;

  for (unsigned int i = 0; i < num_tris; )
	{
	  quads->push_back(tris->index(i++));
	  quads->push_back(tris->index(i++));
	  quads->push_back(tris->index(i++));
	}

  geometry->setVertexArray(vertices);
  geometry->setVertexIndices(quads);
  geometry->addPrimitiveSet(new DrawArrays(PrimitiveSet::QUADS, 0, num_quads));
  geometry->addPrimitiveSet(new DrawArrays(PrimitiveSet::TRIANGLES, 0, num_tris));
  geometry->setNormalArray(normals);
  geometry->setNormalBinding(Geometry::BIND_PER_PRIMITIVE);
  geometry->setNormalIndices(normal_map);

  return geometry;
}
