namespace X3DTK {
namespace MESH {

template<class MData, class VData, class EData, class FData, bool RW>
TemplateMeshSimplifierCoreVisitor<MData, VData, EData, FData, RW>::TemplateMeshSimplifierCoreVisitor()
: CoreVisitor()
{
  define(Recorder<TemplateVertex<MData, VData, EData, FData, RW> >::getEnterFunction(&TemplateMeshSimplifierCoreVisitor::enterVertex));
  define(Recorder<TemplateMesh<MData, VData, EData, FData, RW> >::getLeaveFunction(&TemplateMeshSimplifierCoreVisitor::leaveMesh));
}
template<class MData, class VData, class EData, class FData, bool RW>
void TemplateMeshSimplifierCoreVisitor<MData, VData, EData, FData, RW>::enterVertex(TemplateVertex<MData, VData, EData, FData, RW> *V)
{
  TemplateMeshSimplifierStateVariables<MData, VData, EData, FData, RW> *stateVariables = Singleton<TemplateMeshSimplifierStateVariables<MData, VData, EData, FData, RW> >::getInstance();
  
  if (!stateVariables->getNode(V))
  {
    TemplateVertex<MData, VData, EData, FData, RW> *NV = static_cast<TemplateVertex<MData, VData, EData, FData, RW> *>(stateVariables->getMesh()->getVertex());
    
    for (typename TemplateVertex<MData, VData, EData, FData, RW>::MFVertex::const_iterator v = V->getVertices().begin(); v != V->getVertices().end(); ++v)
    {
      SFTemplateVertex<MData, VData, EData, FData, RW> *n = NV->createVertex();
      stateVariables->addCoupleVertex(*v, n); 
      
      // filling data.
      #ifdef TEMPLATE_SPECIALIZATION_SUPPORTED
      if (VData::template find<MESH::VertexPointData>())
        n->template ogetData<MESH::VertexPointData>().setPoint((*v)->data().getPoint());
      #else
      n->data().setPoint((*v)->data().getPoint());
      #endif
    } 
    
    stateVariables->addNode(V);
  }
}
template<class MData, class VData, class EData, class FData, bool RW>
void TemplateMeshSimplifierCoreVisitor<MData, VData, EData, FData, RW>::leaveMesh(TemplateMesh<MData, VData, EData, FData, RW> *M)
{
  TemplateMeshSimplifierStateVariables<MData, VData, EData, FData, RW> *stateVariables = Singleton<TemplateMeshSimplifierStateVariables<MData, VData, EData, FData, RW> >::getInstance();
  TemplateMesh<MData, VData, EData, FData, RW> *NM = stateVariables->getMesh();
  #ifdef TEMPLATE_SPECIALIZATION_SUPPORTED
  if (MData::template find<MESH::MeshColorData>())  
    NM->template ogetData<MESH::MeshColorData>().setColor((NM->template ogetData<MESH::MeshColorData>().hasColor()) && (M->template ogetData<MESH::MeshColorData>().hasColor()));
  #else
  NM->data().setColor((NM->data().hasColor()) && (M->data().hasColor()));
  #endif
  
  for (typename TemplateMesh<MData, VData, EData, FData, RW>::MFFace::const_iterator f = M->getFaces().begin(); f != M->getFaces().end(); ++f)
  {
    MFInt32 faceCoordIndex;
    typename SFTemplateFace<MData, VData, EData, FData, RW>::MFEdge::const_iterator e = (*f)->getEdges().begin();
    while (e != (*f)->getEdges().end())
    {
      SFTemplateVertex<MData, VData, EData, FData, RW> *v = (*e)->getFromVertex();
      SFTemplateVertex<MData, VData, EData, FData, RW> *nv = stateVariables->getNode(v);

      if (nv != 0)
        faceCoordIndex.push_back(nv->getIndex());
      
      ++e;  
    }
    SFTemplateFace<MData, VData, EData, FData, RW> *nf = NM->createFace(faceCoordIndex);
    
    // filling data.
    #ifdef TEMPLATE_SPECIALIZATION_SUPPORTED
    if ((VData::template find<MESH::VertexNormalData>()) 
     && (FData::template find<MESH::FaceNormalData>()))
      nf->template ogetData<MESH::FaceNormalData>().setNormal((*f)->template ogetData<MESH::FaceNormalData>().getNormal());
    if ((VData::template find<MESH::VertexColorData>())
     && (FData::template find<MESH::FaceColorData>()))    
      nf->template ogetData<MESH::FaceColorData>().setColor((*f)->template ogetData<MESH::FaceColorData>().getColor());
    #else
    nf->data().setNormal((*f)->data().getNormal());
    nf->data().setColor((*f)->data().getColor());
    #endif
    e = (*f)->getEdges().begin();
    while (e != (*f)->getEdges().end())
    {
      SFTemplateVertex<MData, VData, EData, FData, RW> *v = (*e)->getFromVertex();
      SFTemplateVertex<MData, VData, EData, FData, RW> *nv = stateVariables->getNode(v);
      
      #ifdef TEMPLATE_SPECIALIZATION_SUPPORTED
      if ((MData::template find<MESH::MeshNormalData>())
       && (VData::template find<MESH::VertexNormalData>())
       && (FData::template find<MESH::FaceNormalData>()))
      {
        if (!M->template ogetData<MESH::MeshNormalData>().getNormalPerVertex())
          nv->template ogetData<MESH::VertexNormalData>().setNormalOfFace(nf->template ogetData<MESH::FaceNormalData>().getNormal(), nf);
        else
          nv->template ogetData<MESH::VertexNormalData>().setNormalOfFace(v->template ogetData<MESH::VertexNormalData>().getNormalOfFace(*f), nf);
      }
      
      if ((MData::template find<MESH::MeshColorData>())
       && (VData::template find<MESH::VertexColorData>())
       && (FData::template find<MESH::FaceColorData>()))
      { 
        if ((M->template ogetData<MESH::MeshColorData>().hasColor()) && (!M->template ogetData<MESH::MeshColorData>().getColorPerVertex()))
          nv->template ogetData<MESH::VertexColorData>().setColorOfFace(nf->template ogetData<MESH::FaceColorData>().getColor(), nf); 
        if ((M->template ogetData<MESH::MeshColorData>().hasColor()) && (M->template ogetData<MESH::MeshColorData>().getColorPerVertex()))
          nv->template ogetData<MESH::VertexColorData>().setColorOfFace(v->template ogetData<MESH::VertexColorData>().getColorOfFace(*f), nf); 
      }
      
      if (VData::template find<MESH::VertexTexCoordData>())
        nv->template ogetData<MESH::VertexTexCoordData>().setTexCoordOfFace(v->template ogetData<MESH::VertexTexCoordData>().getTexCoordOfFace(*f), nf);  
      #else
      if (!M->data().getNormalPerVertex())
        nv->data().setNormalOfFace(nf->data().getNormal(), nf);
      else
        nv->data().setNormalOfFace(v->data().getNormalOfFace(*f), nf);

      if ((M->data().hasColor()) && (!M->data().getColorPerVertex()))
        nv->data().setColorOfFace(nf->data().getColor(), nf); 
      if ((M->data().hasColor()) && (M->data().getColorPerVertex()))
        nv->data().setColorOfFace(v->data().getColorOfFace(*f), nf); 

      nv->data().setTexCoordOfFace(v->data().getTexCoordOfFace(*f), nf);  
      #endif
      
      ++e;
    }  
  }
}

}
}
