namespace X3DTK {
namespace MESH {

template<class MData, class VData, class EData, class FData, bool RW>
TemplateNormalsInverterCoreVisitor<MData, VData, EData, FData, RW>::TemplateNormalsInverterCoreVisitor()
{
  // Enter and leave functions.
  define(Recorder<TemplateMesh<MData, VData, EData, FData, RW> >::getEnterFunction(&TemplateNormalsInverterCoreVisitor::enterMesh));
  define(Recorder<Shape>::getWalkOnFunction(&TemplateNormalsInverterCoreVisitor::walkOnShape));
}

template<class MData, class VData, class EData, class FData, bool RW>
bool TemplateNormalsInverterCoreVisitor<MData, VData, EData, FData, RW>::walkOnShape(Shape *, SFNode child)
{
  TemplateNormalsInverterStateVariables<MData, VData, EData, FData, RW> *stateVariables = Singleton<TemplateNormalsInverterStateVariables<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 TemplateNormalsInverterCoreVisitor<MData, VData, EData, FData, RW>::enterMesh(TemplateMesh<MData, VData, EData, FData, RW> *M)
{
  TemplateNormalsInverterStateVariables<MData, VData, EData, FData, RW> *stateVariables = Singleton<TemplateNormalsInverterStateVariables<MData, VData, EData, FData, RW> >::getInstance();
  if (!stateVariables->getNode(M))
  { 
    const typename TemplateMesh<MData, VData, EData, FData, RW>::MFFace &mf = M->getFaces();
    for (typename TemplateMesh<MData, VData, EData, FData, RW>::MFFace::const_iterator pf = mf.begin(); pf != mf.end(); ++pf)
    {
      SFTemplateFace<MData, VData, EData, FData, RW> *f = *pf;
      f->reverse();
      // reversing the normals of the face.
      #ifdef TEMPLATE_SPECIALIZATION_SUPPORTED
      if (FData::template find<MESH::FaceNormalData>())
      #endif
      {
        #ifdef TEMPLATE_SPECIALIZATION_SUPPORTED
        const SFVec3f &n = f->template ogetData<MESH::FaceNormalData>().getNormal();
        f->template ogetData<MESH::FaceNormalData>().setNormal(-n);
        #else
        const SFVec3f &n = f->data().getNormal();
        f->data().setNormal(-n);
        #endif
      }  
      
      #ifdef TEMPLATE_SPECIALIZATION_SUPPORTED
      if (VData::template find<MESH::VertexNormalData>())
      #endif
      {
        const typename SFTemplateFace<MData, VData, EData, FData, RW>::MFEdge &me = f->getEdges();
        for (typename SFTemplateFace<MData, VData, EData, FData, RW>::MFEdge::const_iterator e = me.begin(); e != me.end(); ++e)
        {
          SFTemplateVertex<MData, VData, EData, FData, RW> *v = (*e)->getFromVertex();
          #ifdef TEMPLATE_SPECIALIZATION_SUPPORTED
          const SFVec3f &n = v->template ogetData<MESH::VertexNormalData>().getNormalOfFace(f);
          v->template ogetData<MESH::VertexNormalData>().setNormalOfFace(-n, f);
          #else
          const SFVec3f &n = v->data().getNormalOfFace(f);
          v->data().setNormalOfFace(-n, f);
          #endif
        }
      }  
    }  
    
    stateVariables->addNode(M);  
  }
}

}
}  
