/* renderer.C: default XRML node handlers */

#include "renderer.H"

#include <math.h>
#include "Stack.H"
#include "vector.H"
#include "matrix.H"

#include "Anchor.H"
#include "Appearance.H"
#include "AudioClip.H"
#include "Background.H"
#include "Billboard.H"
#include "Box.H"
#include "Collision.H"
#include "Color.H"
#include "ColorInterpolator.H"
#include "Cone.H"
#include "Coordinate.H"
#include "CoordinateInterpolator.H"
#include "Cylinder.H"
#include "CylinderSensor.H"
#include "DirectionalLight.H"
#include "ElevationGrid.H"
#include "Extrusion.H"
#include "Fog.H"
#include "FontStyle.H"
#include "Group.H"
#include "ImageTexture.H"
#include "IndexedFaceSet.H"
#include "IndexedLineSet.H"
#include "Inline.H"
#include "LOD.H"
#include "Material.H"
#include "MovieTexture.H"
#include "NavigationInfo.H"
#include "Normal.H"
#include "NormalInterpolator.H"
#include "OrientationInterpolator.H"
#include "PixelTexture.H"
#include "PlaneSensor.H"
#include "PointLight.H"
#include "PointSet.H"
#include "PositionInterpolator.H"
#include "ProximitySensor.H"
#include "ScalarInterpolator.H"
#include "Script.H"
#include "Shape.H"
#include "Sound.H"
#include "Sphere.H"
#include "SphereSensor.H"
#include "SpotLight.H"
#include "Switch.H"
#include "Text.H"
#include "TextureCoordinate.H"
#include "TextureTransform.H"
#include "TimeSensor.H"
#include "TouchSensor.H"
#include "Transform.H"
#include "Viewpoint.H"
#include "VisibilitySensor.H"
#include "WorldInfo.H"

#ifdef PhBRML
#include "PhBAtmosphere.H"
#include "PhBMonitorCalibration.H"
#endif

namespace xrml {

static stack<Mat4> xf_stack;

void renderer::ignore(SFNode *node)
{
  Info(NULL, "Ignoring %s", node->name());
}

void renderer::begin_frame(world *)
{
  /* silently ignore */
}

void renderer::end_frame(world *)
{
  /* silently ignore */
}


// bindables
void renderer::background(Background *)
{
  // silently ignore
}

void renderer::fog(Fog *)
{
  // silently ignore
}

void renderer::navigationInfo(NavigationInfo *)
{
  // silently ignore
}

void renderer::viewpoint(Viewpoint *)
{
  // silently ignore
}


// shape
void renderer::begin_Shape(Shape *)
{
  /* silently ignore */
}

void renderer::end_Shape(Shape *)
{
  /* silently ignore */
}


// appearance
void renderer::begin_Appearance(Appearance *)
{
  /* silently ignore */
}

void renderer::end_Appearance(Appearance *)
{
  /* silently ignore */
}


// material
void renderer::begin_Material(Material *node)
{
  ignore(node);
}

void renderer::end_Material(Material *)
{
}


// texture nodes
void renderer::begin_Texture(Texture *node)
{
  ignore(node);
}

void renderer::end_Texture(Texture *)
{
}

void renderer::begin_ImageTexture(ImageTexture *node)
{
  //  cerr << __FILE__ << ":" << __LINE__ << ": " << node->Width << "x" << node->Height << "x" << node->Channels << " texture.\n";
  begin_Texture(node);
}

void renderer::end_ImageTexture(ImageTexture *node)
{
  end_Texture(node);
}

void renderer::begin_MovieTexture(MovieTexture *node)
{
  begin_Texture(node);
}

void renderer::end_MovieTexture(MovieTexture *node)
{
  end_Texture(node);
}

void renderer::begin_PixelTexture(PixelTexture *node)
{
  begin_Texture(node);
}

void renderer::end_PixelTexture(PixelTexture *node)
{
  end_Texture(node);
}


// texture transform
void renderer::begin_TextureTransform(TextureTransform *node)
{
  ignore(node);
}

void renderer::end_TextureTransform(TextureTransform *)
{
}


// geometry nodes
void renderer::box(Box *node)
{
  geometry(node);
}

void renderer::cone(Cone *node)
{
  geometry(node);
}

void renderer::cylinder(Cylinder *node)
{
  geometry(node);
}

void renderer::elevationGrid(ElevationGrid *node)
{
  geometry(node);
}

void renderer::extrusion(Extrusion *node)
{
  geometry(node);
}

void renderer::indexedFaceSet(IndexedFaceSet *node)
{
  geometry(node);
}

void renderer::sphere(Sphere *node)
{
  geometry(node);
}

void renderer::text(Text *node)
{
  geometry(node);
}

void renderer::indexedLineSet(IndexedLineSet *)
{
  /* silently ignore */
}

void renderer::begin_points(class PointSet*)
{
  // ignore
}

void renderer::point_color(const SFColor&)
{
  // ignore
}

void renderer::point_normal(const SFVec3f&)
{
  // ignore
}

void renderer::point_texCoord(const SFVec2f&)
{
  // ignore
}

void renderer::point_coord(const SFVec3f&)
{
  // ignore
}

void renderer::end_points(class PointSet*)
{
  // ignore
}

void renderer::pointSet(PointSet *ps)
{
  ps->discretize();
  if (!ps->co)
    return;

  begin_points(ps);
  for (int i=0; i<ps->co->size; i++) {
    if (ps->col && (ps->col->size > i)) 
      point_color((*ps->col)[i]);
    if (ps->norm && (ps->norm->size > i)) 
      point_normal((*ps->norm)[i]);
    if (ps->texco && (ps->texco->size > i)) 
      point_texCoord((*ps->texco)[i]);
    point_coord((*ps->co)[i]);
  }
  end_points(ps);
}

// grouping nodes
void renderer::begin_Grouping(Grouping *node)
{
  ignore(node);
}

void renderer::end_Grouping(Grouping *)
{
}

void renderer::begin_Anchor(Anchor *node)
{
  begin_Grouping(node);
}

void renderer::end_Anchor(Anchor *node)
{
  end_Grouping(node);
}

void renderer::begin_Collision(Collision *node)
{
  begin_Grouping(node);
}

void renderer::end_Collision(Collision *node)
{
  end_Grouping(node);
}

void renderer::begin_Group(Group *node)
{
  begin_Grouping(node);
}

void renderer::end_Group(Group *node)
{
  end_Grouping(node);
}

void renderer::begin_Inline(Inline *node)
{
  begin_Grouping(node);
}

void renderer::end_Inline(Inline *node)
{
  end_Grouping(node);
}

void renderer::begin_LOD(LOD *node)
{
  begin_Grouping(node);

  Warning("renderer::begin_LOD()", "level selection not yet implemented, chosing level 0");
  node->whichLevel = 0;
}

void renderer::end_LOD(LOD *node)
{
  end_Grouping(node);
}

void renderer::begin_Switch(Switch *node)
{
  begin_Grouping(node);
}

void renderer::end_Switch(Switch *node)
{
  end_Grouping(node);
}


// grouping nodes that transform their children
void renderer::begin_Transforming(Transforming *node)
{
  xf_stack.push(xf);
  xf = node->xf * xf;
  xf_stack.push(inverse_xf);
  inverse_xf = inverse_xf * node->inverse_xf;

  begin_Grouping(node);
}

void renderer::end_Transforming(Transforming *node)
{
  end_Grouping(node);

  inverse_xf = xf_stack.pop();
  xf = xf_stack.pop();
}

void renderer::begin_Billboard(Billboard *node)
{
  Warning("renderer::begin_Billboard", "transform not yet implemented");
  node->xf = node->inverse_xf = Mat4();

  begin_Transforming(node);
}

void renderer::end_Billboard(Billboard *node)
{
  end_Transforming(node);
}

void renderer::begin_Transform(Transform *node)
{
  node->work_out();		// work out transform and its inverse
  node->work_out_inverse();

  begin_Transforming(node);
}

void renderer::end_Transform(Transform *node)
{
  end_Transforming(node);
}


// light sources
void renderer::begin_LightSource(class LightSource *node)
{
  ignore(node);
}

void renderer::end_LightSource(class LightSource *)
{
  // silently ignore
}

void renderer::begin_DirectionalLight(DirectionalLight *node)
{
  begin_LightSource(node);
}

void renderer::end_DirectionalLight(DirectionalLight *node)
{
  end_LightSource(node);
}

void renderer::begin_PointLight(PointLight *node)
{
  begin_LightSource(node);
}

void renderer::end_PointLight(PointLight *node)
{
  end_LightSource(node);
}

void renderer::begin_SpotLight(SpotLight *node)
{
  begin_LightSource(node);
}

void renderer::end_SpotLight(SpotLight *node)
{
  end_LightSource(node);
}


// sensors
void renderer::cylinderSensor(CylinderSensor *node)
{
  ignore(node);
}

void renderer::planeSensor(PlaneSensor *node)
{
  ignore(node);
}

void renderer::proximitySensor(ProximitySensor *node)
{
  ignore(node);
}

void renderer::sphereSensor(SphereSensor *node)
{
  ignore(node);
}

void renderer::timeSensor(TimeSensor *sensor)
{
  bool should_be_active = sensor->should_be_active();
  if (sensor->isActive && should_be_active == false)
    sensor->deactivate();
  else if (!sensor->isActive && should_be_active == true)
    sensor->activate();
  else if (sensor->isActive)
    sensor->sample();
}

void renderer::touchSensor(TouchSensor *node)
{
  ignore(node);
}

void renderer::visibilitySensor(VisibilitySensor *node)
{
  ignore(node);
}



// audio
void renderer::sound(Sound *node)
{
  ignore(node);
}


// WorldInfo
void renderer::worldInfo(WorldInfo *node)
{
  ignore(node);
}


void renderer::begin_faces(Geometry *)
{
}

void renderer::begin_face(int, int)
{
}

void renderer::face_normal(int, const SFVec3f&)
{
}

void renderer::face_color(int, const SFColor&)
{
}

void renderer::vertex_coord(int, const SFVec3f&)
{
}

void renderer::vertex_texCoord(int, const SFVec2f&)
{
}

void renderer::vertex_normal(int, const SFVec3f&)
{
}

void renderer::vertex_color(int, const SFColor&)
{
}

void renderer::end_face(int, int)
{
}

void renderer::end_faces(Geometry *)
{
}

// TODO: the default geometry callback function needs to be
// completely rewritten!!!

struct DVec3 {
  double x, y, z;
  DVec3(double xx =0., double yy =0., double zz =0.) { x=xx; y=yy; z=zz; }
};

struct vertshare {
  array<int> faces;	// indices of faces sharing the vertex
  friend ostream& operator<<(ostream& s, vertshare& v) { return s << v.faces; }
};

static SFVec3f compute_normal(Geometry *f, int facenr, const array<int>& vidx)
{
  if (vidx.size < 3) {
    Error(NULL, "%s: face %d has less than three vertices", f->name(), facenr);
    return SFVec3f(0,0,0);
  }

  DVec3 n(0,0,0);
  SFVec3f& o = (*f->co)[0];
  SFVec3f& p = (*f->co)[vidx[vidx.size-1]];
  DVec3 prev, cur(p.x - o.x, p.y - o.y, p.z - o.z);
  for (int j=0; j<vidx.size; j++) {
    prev = cur;
    SFVec3f& p = (*f->co)[vidx[j]];
    cur = DVec3(p.x - o.x, p.y - o.y, p.z - o.z);

    n.x += (prev.y - cur.y) * (prev.z + cur.z);
    n.y += (prev.z - cur.z) * (prev.x + cur.x);
    n.z += (prev.x - cur.x) * (prev.y + cur.y);
  }

  double norm = sqrt(n.x*n.x + n.y*n.y + n.z*n.z);
  if (norm < 1e-6) {
    Error(NULL, "%s: face %d is degenerate (or very nearly so)", f->name(), facenr);
    return SFVec3f(0,0,0);
  }
  return SFVec3f(n.x/norm, n.y/norm, n.z/norm);
}

static SFVec3f gen_normal(Geometry *f, int vertnr, int facenr, const MFVec3f& fnorm, const array<vertshare>& vert)
{
  SFVec3f& nf = fnorm[facenr];
  SFVec3f vnorm = nf;
  if (f->creaseAngle > 0.) {	// otherwise no vertex sharing
    int ncnt = 1;
    for (int j=0; j<vert[vertnr].faces.size; j++) {
      int k = vert[vertnr].faces[j];
      SFVec3f& n = fnorm[k];
      if (k != facenr && cos(f->creaseAngle) < n.x*nf.x + n.y*nf.y + n.z*nf.z) {
	vnorm.x += n.x;
	vnorm.y += n.y;
	vnorm.z += n.z;
	ncnt++;
      }
    }
    if (ncnt > 1) {
      double norm = sqrt(vnorm.x*vnorm.x + vnorm.y*vnorm.y + vnorm.z*vnorm.z);
      vnorm.x /= norm;
      vnorm.y /= norm;
      vnorm.z /= norm;
    }
  }
  return vnorm;
}

static int get_normal(Geometry *f, MFInt32 *idx, int i)
{
  int k = i;
  
  if (idx) {
    if (idx->size <= i) {
      Error(NULL, "%s: normalIndex array too short (no %dth index)", f->name(), i);
      return -1;
    }
    k = (*idx)[i];
  }

  if (k < 0 || f->norm->size <= k) { 
    Error(NULL, "%s: normal index %d out of range", f->name(), k);
    return -1;
  }

  return k;
}

static int get_color(Geometry *f, MFInt32 *idx, int i)
{
  int k = i;
  
  if (idx) {
    if (idx->size <= i) {
      Error(NULL, "%s: colorIndex array too short (no %dth index)", f->name(), i);
      return -1;
    }
    k = (*idx)[i];
  }

  if (k < 0 || f->col->size <= k) {
    Error(NULL, "%s: color index %d out of range", f->name(), k);
    return -1;
  }

  return k;
}

static SFVec2f gen_texCoord(Geometry *f, int vertnr, int sidx, int tidx, const Vec3& min, float side)
{
  SFVec3f& v = (*f->co)[vertnr];
  Vec3 d = Vec3(v.x-min.x, v.y-min.y, v.z-min.z);
  SFVec2f t;
  t.s = (sidx==1 ? d.x : (sidx==2 ? d.y : d.z)) / side;
  t.t = (tidx==1 ? d.x : (tidx==2 ? d.y : d.z)) / side;
  return t;
}

static int get_texCoord(Geometry *f, MFInt32 *idx, int i)
{
  int k = i;
  
  if (idx) {
    if (idx->size <= i) {
      Error(NULL, "%s: texCoordIndex array too short (no %dth index)", f->name(), i);
      return -1;
    }
    k = (*idx)[i];
  }

  if (k < 0 || f->texco->size <= k) {
    Error(NULL, "%s: texCoord index %d out of range", f->name(), k);
    return -1;
  }

  return k;
}

void renderer::geometry(Geometry *f)
{
  int newface, facenr, k;

  f->discretize();

  if (!f->co || f->coordIndex.size <= 0) {
    Error(NULL, "%s: no coordinates", f->name());
    return;
  }

  // the following is computed when f->norm is nil (and only
  // needed in that case further on)
  array<vertshare> vert; // vertex sharing information for normal generation
  MFVec3f fnorm;	 // automatically generated normals

  if (!f->norm && normals_required) {
    array<int> vidx(10);
    array<int> fverts;	 // fverts[i] = nr of vertices in face i.
    // count the number of faces in the geometry
    facenr = 0; newface = TRUE;
    for (k=0; k<f->coordIndex.size; k++) {
      if (f->coordIndex[k] < 0) {
	facenr++; 
	newface = TRUE;
      } else
	newface = FALSE;
    }
    if (!newface)
      facenr++;

    // no normals specified, so they must be generated.
    fverts.grow(facenr);
    fnorm.grow(facenr);
    vert.grow(f->co->size);

    // compute face normals and vertex sharing information
    newface = TRUE;
    facenr = 0;
    for (k=0; k<f->coordIndex.size; k++) {	// process faces
      int i = f->coordIndex[k];
      if (i < 0) {
	// fnorm[facenr] will be normal of face facenr.
	fnorm[facenr] = compute_normal(f, facenr, vidx);
	fverts[facenr] = vidx.size;

	facenr++;
	newface = TRUE;
      } else {
	if (newface) {
	  newface = FALSE;
	  vidx.size = 0;
	}
	if (i >= f->co->size) {
	  Error(NULL, "%s: coordinate index %d out of range", f->name(), i);
	} else {
	  vidx.append(i);
	  if (f->creaseAngle > 0.) vert[i].faces.append(facenr);
	}
      }
    }
    if (!newface) {
      fnorm[facenr] = compute_normal(f, facenr, vidx);
      fverts[facenr] = vidx.size;
    }
  }

  Vec3 min(HUGE, HUGE, HUGE);
  int sidx=0, tidx=0;
  float side = 1;

  if (!f->texco && texcoords_required) {
    // compute minimum x,y,z, longest side of bounding box and indices sidx and
    // tidx relating texture coordinates s and t to 3D world coordinates x, y or z.
    Vec3 size(0.,0.,0.);
    Vec3 max(-HUGE, -HUGE, -HUGE);
    for (int i=0; i<f->co->size; i++) {
      SFVec3f& P = (*f->co)[i];
      Vec3 p(P.x, P.y, P.z);
      min <<= p;
      max >>= p;
    }
    size = max-min;

    side = size.x; sidx = 1;
    if (size.y > side) { side = size.y; sidx = 2; }
    if (size.z > side) { side = size.z; sidx = 3; }

    float mmin = size.x; tidx = 1;
    if (size.y <= mmin) { mmin = size.y; tidx = 2; }
    if (size.z <= mmin) { mmin = size.z; tidx = 3; }

    tidx = 6 - tidx - sidx;
  }

  begin_faces(f);

  newface = TRUE;
  facenr = 0;
  int nrverts = 0;
  for (k=0; k<f->coordIndex.size; k++) {	// output faces
    int i = f->coordIndex[k];
    if (i < 0) {         // end of face marker
      if (nrverts < 3) {
	Warning(NULL, "%s: face %d has less than 3 vertices.\n", f->name(), facenr);
      }
      end_face(facenr, nrverts);

      newface = TRUE;
      facenr++;
    } else {
      if (newface) {
	// count nr of vertices in face
	nrverts = 1;
	for (int l=k+1; l<f->coordIndex.size && f->coordIndex[l]!=-1; l++) {
	  nrverts++;
	}
	begin_face(facenr, nrverts);

	if (normals_required) {
	if (f->norm && !f->normalPerVertex) {	// per face normal
	  int idx;
	  if (f->normalIndex.size > 0)
	    idx = get_normal(f, &f->normalIndex, facenr);
	  else
	    idx = get_normal(f, 0, facenr);
	  if (idx>=0) face_normal(idx, (*f->norm)[idx]);
	}
	}

	if (colors_required) {
	if (f->col && !f->colorPerVertex) {	// per face color
	  int idx;
	  if (f->colorIndex.size > 0)
	    idx = get_color(f, &f->colorIndex, facenr);
	  else
	    idx = get_color(f, 0, facenr);
	  if (idx>=0) face_color(idx, (*f->col)[idx]);
	}
	}

	newface = FALSE;
      }

      if (i >= f->co->size) {
	Error(NULL, "%s: coordinate index %d out of range", f->name(), i);
      } else {
	if (normals_required) {  // per vertex normals
	  if (!f->norm) {
	    SFVec3f n;
	    n = gen_normal(f, i, facenr, fnorm, vert);
	    vertex_normal(-1, n);
	  } else if (f->normalPerVertex) {	// set vertex normal
	    int idx;
	    if (f->normalIndex.size > 0)
	      idx = get_normal(f, &f->normalIndex, k);
	    else
	      idx = get_normal(f, &f->coordIndex, k);
	    if (idx>=0) vertex_normal(idx, (*f->norm)[idx]);
	  }
	}

	if (colors_required) {
	  if (f->col && f->colorPerVertex) {	// set vertex color
	    int idx;
	    if (f->colorIndex.size > 0)
	      idx = get_color(f, &f->colorIndex, k);
	    else
	      idx = get_color(f, &f->coordIndex, k);
	    if (idx>=0) vertex_color(idx, (*f->col)[idx]);
	  }
	}

	if (texcoords_required) {
	  // per-vertex texture coordinates
	  if (f->texco) {
	    int idx;
	    if (f->texCoordIndex.size > 0)
	      idx = get_texCoord(f, &f->texCoordIndex, k);
	    else
	      idx = get_texCoord(f, &f->coordIndex, k);
	    if (idx>=0) vertex_texCoord(idx, (*f->texco)[idx]);
	  } else {
	    SFVec2f t;
	    t = gen_texCoord(f, i, sidx, tidx, min, side);
	    vertex_texCoord(-1, t);
	  }
	}

	vertex_coord(i, (*f->co)[i]);
      }
    }
  }
  if (!newface) {
    // ending coordIndex -1 is not required.
    if (nrverts < 3) {
      Warning(NULL, "%s: face %d has less than 3 vertices.\n", f->name(), facenr);
    }
    end_face(facenr, nrverts);
  }

  end_faces(f);
}

} // namespace xrml

