namespace X3DTK {
namespace MESH {

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

template<class MData, class VData, class EData, class FData, bool RW>
bool TemplateNormalsFlipperCoreVisitor<MData, VData, EData, FData, RW>::walkOnShape(Shape *, SFNode child)
{
  TemplateNormalsFlipperStateVariables<MData, VData, EData, FData, RW> *stateVariables = Singleton<TemplateNormalsFlipperStateVariables<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 TemplateNormalsFlipperCoreVisitor<MData, VData, EData, FData, RW>::enterVertex(TemplateVertex<MData, VData, EData, FData, RW> *V)
{
  TemplateNormalsFlipperStateVariables<MData, VData, EData, FData, RW> *stateVariables = Singleton<TemplateNormalsFlipperStateVariables<MData, VData, EData, FData, RW> >::getInstance();
  if (!stateVariables->getNode(V))
  { 
    // computing the list of Mesh parents of V.
    MFNode parentList = V->getParentList();
    std::list<TemplateMesh<MData, VData, EData, FData, RW> *> meshList;
    for (MFNode::const_iterator it = parentList.begin(); it != parentList.end(); ++it)
    {
      if (dynamic_cast<TemplateMesh<MData, VData, EData, FData, RW> *>(*it) != 0)
        meshList.push_back(static_cast<TemplateMesh<MData, VData, EData, FData, RW> *>(*it));
    }
    
    // defining structures.
    typename std::vector<SFTemplateFace<MData, VData, EData, FData, RW> *> stack;
    unsigned int stackSize = 0;
    for (typename std::list<TemplateMesh<MData, VData, EData, FData, RW> *>::const_iterator it = meshList.begin(); it != meshList.end(); ++it)
      stackSize += (*it)->getFaces().size();
    stack.reserve(stackSize);
    
    typename std::list<TemplateMesh<MData, VData, EData, FData, RW> *>::const_iterator itMesh = meshList.begin();
    if (itMesh == meshList.end()) return;
    
    typename TemplateMesh<MData, VData, EData, FData, RW>::MFFace::const_iterator itFace = (*itMesh)->getFaces().begin();
    
    while (itMesh != meshList.end())
    {
      std::map<SFTemplateFace<MData, VData, EData, FData, RW> *, bool> faceMap;
      stack.push_back(*itFace);
      typename std::vector<SFTemplateFace<MData, VData, EData, FData, RW> *>::const_iterator f = stack.end();
      --f;
      faceMap[*f] = true;
    
    // initializing the algorithm by the first face. Precondition all faces can be accessed
    // by neighboorhood of the face.
    
      while (f != stack.end())
      {
        bool current = faceMap[*f];

        // searching the list of faces around f.
        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)
        {
          const typename SFTemplateEdge<MData, VData, EData, FData, RW>::MFFace &mlf = (*e)->getLeftFaces();
          const typename SFTemplateEdge<MData, VData, EData, FData, RW>::MFFace &mrf = (*e)->getRightFaces();
        
          for (typename SFTemplateEdge<MData, VData, EData, FData, RW>::MFFace::const_iterator nf = mlf.begin(); nf != mlf.end(); ++nf)
          {
            if (faceMap.find(*nf) == faceMap.end())
            {
              faceMap[*nf] = !current;
              stack.push_back(*nf);
            }
          }
        
          for (typename SFTemplateEdge<MData, VData, EData, FData, RW>::MFFace::const_iterator nf = mrf.begin(); nf != mrf.end(); ++nf)
          {
            if (faceMap.find(*nf) == faceMap.end())
            {
              faceMap[*nf] = current;
              stack.push_back(*nf);
            }
          }
        }
        ++f;
      }
    
      // Counting the number of direct faces against indirect faces.
      unsigned int direct = 0;
      for (typename std::map<SFTemplateFace<MData, VData, EData, FData, RW> *, bool>::const_iterator it = faceMap.begin(); it != faceMap.end(); ++it)
      {
        if ((*it).second)
          ++direct;
      }
    
      unsigned int nfaces = 0;
      if (direct < faceMap.size() - direct)
        nfaces = direct;
      else
        nfaces = faceMap.size() - direct;
        
      std::cout << "reversing " << nfaces << "/" << faceMap.size() << " faces" << std::endl;
    
      for (typename std::map<SFTemplateFace<MData, VData, EData, FData, RW> *, bool>::const_iterator pf = faceMap.begin(); pf != faceMap.end(); ++pf)
        if ((direct > (faceMap.size() >> 1)) != ((*pf).second)) 
        {
          SFTemplateFace<MData, VData, EData, FData, RW> *f = (*pf).first;
          f->reverse();
          // reversing the normals of the face.
          #ifdef TEMPLATE_SPECIALIZATION_SUPPORTED
          if (FData::template find<MESH::FaceNormalData>())
          {
            const SFVec3f &n = f->template ogetData<MESH::FaceNormalData>().getNormal();
            f->template ogetData<MESH::FaceNormalData>().setNormal(-n);
          }  
          
          if (VData::template find<MESH::VertexNormalData>())
          {
            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();
              const SFVec3f &n = v->template ogetData<MESH::VertexNormalData>().getNormalOfFace(f);
              v->template ogetData<MESH::VertexNormalData>().setNormalOfFace(-n, f);
            }
          }
          #else
          const SFVec3f &n = f->data().getNormal();
          f->data().setNormal(-n);
                    
          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();
            const SFVec3f &n = v->data().getNormalOfFace(f);
            v->data().setNormalOfFace(-n, f);
          }
          #endif  
        }
      
      // finding the next face.
      while ((itMesh != meshList.end()) && (std::find(stack.begin(), stack.end(), *itFace) != stack.end()))
      {
        ++itFace;
        if (itFace == (*itMesh)->getFaces().end())
        {
          ++itMesh;
          if (itMesh != meshList.end())
            itFace = (*itMesh)->getFaces().begin();
        }    
      }  
    }
    stateVariables->addNode(V);  
  }
}

}
}  
