namespace X3DTK {
namespace MESH {

template<class MData, class VData, class EData, class FData, bool RW>
TemplateNormalsUpdaterFacesCoreVisitor<MData, VData, EData, FData, RW>::TemplateNormalsUpdaterFacesCoreVisitor()
{
  define(Recorder<TemplateMesh<MData, VData, EData, FData, RW> >::getEnterFunction(&TemplateNormalsUpdaterFacesCoreVisitor::enterMesh));
  define(Recorder<Shape>::getWalkOnFunction(&TemplateNormalsUpdaterFacesCoreVisitor::walkOnShape));
}

template<class MData, class VData, class EData, class FData, bool RW>
bool TemplateNormalsUpdaterFacesCoreVisitor<MData, VData, EData, FData, RW>::walkOnShape(Shape *, SFNode child)
{
  TemplateNormalsUpdaterStateVariables<MData, VData, EData, FData, RW> *stateVariables = Singleton<TemplateNormalsUpdaterStateVariables<MData, VData, EData, FData, RW> >::getInstance();
  MFNode selectedNodes = stateVariables->getSelectedNodes();
  return (selectedNodes.empty() || (std::find(selectedNodes.begin(), selectedNodes.end(), child) != selectedNodes.end()));
}

template<class MData, class VData, class EData, class FData, bool RW>
void TemplateNormalsUpdaterFacesCoreVisitor<MData, VData, EData, FData, RW>::enterMesh(TemplateMesh<MData, VData, EData, FData, RW> *M)
{
  #ifdef TEMPLATE_SPECIALIZATION_SUPPORTED
  if (VData::template find<MESH::VertexPointData>() 
   && FData::template find<MESH::FaceNormalData>())
  #endif 
  { 
    // Compute all faces normals 
    const typename TemplateMesh<MData, VData, EData, FData, RW>::MFFace &faces = M->getFaces();
    for (typename TemplateMesh<MData, VData, EData, FData, RW>::MFFace::const_iterator f = faces.begin(), end = faces.end() ; f != end; ++f)
    {
      SFVec3f normal;
      const typename SFTemplateFace<MData, VData, EData, FData, RW>::MFEdge &edges = (*f)->getEdges();
      typename SFTemplateFace<MData, VData, EData, FData, RW>::MFEdge::const_iterator e = edges.begin();
      // Choose a pivot point
      #ifdef TEMPLATE_SPECIALIZATION_SUPPORTED
      const SFPoint3f &firstPoint = (*e)->getFromVertex()->template ogetData<MESH::VertexPointData>().getPoint();
      ++e;
      SFPoint3f secondPoint = (*e)->getFromVertex()->template ogetData<MESH::VertexPointData>().getPoint();
      #else
      const SFPoint3f &firstPoint = (*e)->getFromVertex()->data().getPoint();
      ++e;
      SFPoint3f secondPoint = (*e)->getFromVertex()->data().getPoint();
      #endif  

      ++e;
      for (typename SFTemplateFace<MData, VData, EData, FData, RW>::MFEdge::const_iterator end = edges.end() ; e != end; ++e)
      {
        #ifdef TEMPLATE_SPECIALIZATION_SUPPORTED
        SFPoint3f thirdPoint = (*e)->getFromVertex()->template ogetData<MESH::VertexPointData>().getPoint();
        #else
        SFPoint3f thirdPoint = (*e)->getFromVertex()->data().getPoint();
        #endif
        normal = normal + crossprod(secondPoint - firstPoint, thirdPoint - firstPoint);
        secondPoint = thirdPoint;
      }

      // Update face normal (if face does not already has a normal)
      #ifdef TEMPLATE_SPECIALIZATION_SUPPORTED
      (*f)->template ogetData<MESH::FaceNormalData>().setNormal(normal.normalize());
      #else
      (*f)->data().setNormal(normal.normalize());
      #endif
    }
  }
  
  #ifdef TEMPLATE_SPECIALIZATION_SUPPORTED
  if (MData::template find<MESH::MeshNormalData>())
    M->template ogetData<MESH::MeshNormalData>().setNormal(true);
  #else
    M->data().setNormal(true);
  #endif   
}

}
}
