namespace X3DTK {
namespace MESH {

template<class MData, class VData, class EData, class FData, bool RW>
TemplateX3DBuilderCoreVisitor<MData, VData, EData, FData, RW>::TemplateX3DBuilderCoreVisitor()
{
  define(Recorder<Scene>::getEnterFunction(&TemplateX3DBuilderCoreVisitor::enterScene));
  define(Recorder<Transform>::getEnterFunction(&TemplateX3DBuilderCoreVisitor::enterTransform));
  define(Recorder<Switch>::getEnterFunction(&TemplateX3DBuilderCoreVisitor::enterSwitch));
  define(Recorder<Shape>::getEnterFunction(&TemplateX3DBuilderCoreVisitor::enterShape));
  define(Recorder<TemplateMesh<MData, VData, EData, FData, RW> >::getEnterFunction(&TemplateX3DBuilderCoreVisitor::enterMesh));
  define(Recorder<TemplateVertex<MData, VData, EData, FData, RW> >::getEnterFunction(&TemplateX3DBuilderCoreVisitor::enterVertex));
  define(Recorder<X3DNode>::getLeaveFunction(&TemplateX3DBuilderCoreVisitor::leaveX3DNode));
}

template<class MData, class VData, class EData, class FData, bool RW>
void TemplateX3DBuilderCoreVisitor<MData, VData, EData, FData, RW>::enterScene(Scene *S)
{
  TemplateX3DBuilderStateVariables<MData, VData, EData, FData, RW> *stateVariables = Singleton<TemplateX3DBuilderStateVariables<MData, VData, EData, FData, RW> >::getInstance();
  X3D::Scene *NS = dynamic_cast<X3D::Scene *>(stateVariables->getNode(S));
  if (NS == 0)
  {
    NS = new X3D::Scene();
    NS->setFileName(S->getFileName());
    NS->getProcessHistory() = S->getProcessHistory();
    
    // Updating Scene.
    MFNode x3dChildren = S->getX3DChildren();
    for (MFNode::const_iterator it = x3dChildren.begin(); it != x3dChildren.end(); ++it)
      NS->setChild(stateVariables->clone(*it));
  
    stateVariables->addCoupleNode(S, NS);
  }
  stateVariables->pushNode(NS); 
}

template<class MData, class VData, class EData, class FData, bool RW>
void TemplateX3DBuilderCoreVisitor<MData, VData, EData, FData, RW>::enterTransform(Transform *T)
{
  TemplateX3DBuilderStateVariables<MData, VData, EData, FData, RW> *stateVariables = Singleton<TemplateX3DBuilderStateVariables<MData, VData, EData, FData, RW> >::getInstance();
  X3D::Transform *NT = dynamic_cast<X3D::Transform *>(stateVariables->getNode(T));
  if (NT == 0)
  {
    NT = new X3D::Transform();
    NT->setName(T->getName());
    
    // Updating Transform.
    NT->setCenter(T->getCenter());
    NT->setRotation(T->getRotation());
    NT->setScale(T->getScale());
    NT->setScaleOrientation(T->getScaleOrientation());
    NT->setTranslation(T->getTranslation());
  
    MFNode x3dChildren = T->getX3DChildren();
    for (MFNode::const_iterator it = x3dChildren.begin(); it != x3dChildren.end(); ++it)
      NT->setChild(stateVariables->clone(*it));
  
    stateVariables->addCoupleNode(T, NT);
  }
  stateVariables->pushNode(NT); 
}

template<class MData, class VData, class EData, class FData, bool RW>
void TemplateX3DBuilderCoreVisitor<MData, VData, EData, FData, RW>::enterSwitch(Switch *S)
{
  TemplateX3DBuilderStateVariables<MData, VData, EData, FData, RW> *stateVariables = Singleton<TemplateX3DBuilderStateVariables<MData, VData, EData, FData, RW> >::getInstance();
  X3D::Switch *NS = dynamic_cast<X3D::Switch *>(stateVariables->getNode(S));
  if (NS == 0)
  {
    NS = new X3D::Switch();
    NS->setName(S->getName());
    
    // Updating Switch.
    NS->setWhichChoice(S->getWhichChoice());
  
    MFNode x3dChildren = S->getX3DChildren();
    for (MFNode::const_iterator it = x3dChildren.begin(); it != x3dChildren.end(); ++it)
      NS->setChild(stateVariables->clone(*it));
  
    stateVariables->addCoupleNode(S, NS);
  }
  stateVariables->pushNode(NS); 
}

template<class MData, class VData, class EData, class FData, bool RW>
void TemplateX3DBuilderCoreVisitor<MData, VData, EData, FData, RW>::enterShape(Shape *S)
{
  TemplateX3DBuilderStateVariables<MData, VData, EData, FData, RW> *stateVariables = Singleton<TemplateX3DBuilderStateVariables<MData, VData, EData, FData, RW> >::getInstance();
  X3D::Shape *NS = dynamic_cast<X3D::Shape *>(stateVariables->getNode(S));
  if (NS == 0)
  {
    NS = new X3D::Shape();
    NS->setName(S->getName());

    NS->setChild(stateVariables->clone(S->getAppearance()));
    
    stateVariables->addCoupleNode(S, NS);
  }
  stateVariables->pushNode(NS);   
}

template<class MData, class VData, class EData, class FData, bool RW>
void TemplateX3DBuilderCoreVisitor<MData, VData, EData, FData, RW>::enterMesh(TemplateMesh<MData, VData, EData, FData, RW> *M)
{
  TemplateX3DBuilderStateVariables<MData, VData, EData, FData, RW> *stateVariables = Singleton<TemplateX3DBuilderStateVariables<MData, VData, EData, FData, RW> >::getInstance();
  X3D::IndexedFaceSet *NI = dynamic_cast<X3D::IndexedFaceSet *>(stateVariables->getNode(M));
  if (NI == 0)
  {
    NI = new X3D::IndexedFaceSet();
    NI->setName(M->getName());
  
    // Updating IndexedFaceSet.
    // setting the parameters.
    NI->setCcw(true);
    
    #ifdef TEMPLATE_SPECIALIZATION_SUPPORTED
    if (MData::template find<MESH::MeshNormalData>())
    {
      NI->setNormalPerVertex(M->template ogetData<MESH::MeshNormalData>().getNormalPerVertex());
      NI->setCreaseAngle(M->template ogetData<MESH::MeshNormalData>().getCreaseAngle());
    }
    
    if (MData::template find<MESH::MeshColorData>())      
      NI->setColorPerVertex(M->template ogetData<MESH::MeshColorData>().getColorPerVertex());
    
    if (MData::template find<MESH::MeshSolidData>())      
      NI->setSolid(M->template ogetData<MESH::MeshSolidData>().isSolid());
    #else
    NI->setNormalPerVertex(M->data().getNormalPerVertex());
    NI->setCreaseAngle(M->data().getCreaseAngle());
    NI->setColorPerVertex(M->data().getColorPerVertex());
    NI->setSolid(M->data().isSolid());
    #endif  
    // Faces
    MFInt32 coordIndexArray;
    const typename TemplateMesh<MData, VData, EData, FData, RW>::MFFace &mf = M->getFaces(); 
  
    // Filling the coordIndex.
    for (typename TemplateMesh<MData, VData, EData, FData, RW>::MFFace::const_iterator itFace = mf.begin(); itFace != mf.end(); ++itFace)  
    {
      const typename SFTemplateFace<MData, VData, EData, FData, RW>::MFEdge &me = (*itFace)->getEdges();
      for (typename SFTemplateFace<MData, VData, EData, FData, RW>::MFEdge::const_iterator itEdge = me.begin(); itEdge != me.end(); ++itEdge)
        coordIndexArray.push_back((*itEdge)->getFromVertex()->getIndex());
      coordIndexArray.push_back(-1);
    }

    NI->setCoordIndex(coordIndexArray);
  
    ///////////////////////////////////////////////////////////////////////////////////
    // normals
    
    #ifdef TEMPLATE_SPECIALIZATION_SUPPORTED
    if (MData::template find<MESH::MeshNormalData>()
     && M->template ogetData<MESH::MeshNormalData>().hasNormal())
    #endif 
    {
      X3D::Normal *N = new X3D::Normal();
      NI->setChild(N);
    
      MFVec3f normalArray;
      
      #ifdef TEMPLATE_SPECIALIZATION_SUPPORTED
      if (VData::template find<MESH::VertexNormalData>()
       && M->template ogetData<MESH::MeshNormalData>().getNormalPerVertex())
      #endif
      {
        // iterating the faces to create the normalIndexArray.
        MFInt32 normalIndexArray;
        
        // Using a temporary map to keep the computed normals and find faster whether the
        // normal is in the NormalArray or not.
        
        std::map<SFTemplateVertex<MData, VData, EData, FData, RW> *, std::list<unsigned int> > _vertexMap; 
    
        for (typename TemplateMesh<MData, VData, EData, FData, RW>::MFFace::const_iterator itFace = mf.begin(); itFace != mf.end(); ++itFace)  
        {
          const typename SFTemplateFace<MData, VData, EData, FData, RW>::MFEdge &me = (*itFace)->getEdges();
          
          for (typename SFTemplateFace<MData, VData, EData, FData, RW>::MFEdge::const_iterator itEdge = me.begin(); itEdge != me.end(); ++itEdge)
          {
            SFTemplateVertex<MData, VData, EData, FData, RW> *V = (*itEdge)->getFromVertex();
            #ifdef TEMPLATE_SPECIALIZATION_SUPPORTED
            const SFVec3f &normal = V->template ogetData<MESH::VertexNormalData>().getNormalOfFace(*itFace);
            #else
            const SFVec3f &normal = V->data().getNormalOfFace(*itFace);
            #endif
            // finding the normal among the list of normals for V, already present in NormalArray
            std::list<unsigned int>::const_iterator itNormalIndex = _vertexMap[V].begin();
            while (itNormalIndex != _vertexMap[V].end())
            {
              if (normalArray[*itNormalIndex] == normal)
              {
                normalIndexArray.push_back(*itNormalIndex);
                break;
              }
              ++itNormalIndex;
            }
            
            // if not found, adding it to NormalArray
            if (itNormalIndex == _vertexMap[V].end())
            {
              _vertexMap[V].push_back(normalArray.size());
              normalIndexArray.push_back(normalArray.size());
              normalArray.push_back(normal);
            }
          }  
          normalIndexArray.push_back(-1);
        }
        NI->setNormalIndex(normalIndexArray);
      }
      
      #ifdef TEMPLATE_SPECIALIZATION_SUPPORTED     
      if (FData::template find<MESH::FaceNormalData>()
       && !M->template ogetData<MESH::MeshNormalData>().getNormalPerVertex())
      #endif
      {
        for (typename TemplateMesh<MData, VData, EData, FData, RW>::MFFace::const_iterator itFace = mf.begin(); itFace != mf.end(); ++itFace)  
          #ifdef TEMPLATE_SPECIALIZATION_SUPPORTED
          normalArray.push_back((*itFace)->template ogetData<MESH::FaceNormalData>().getNormal());
          #else
          normalArray.push_back((*itFace)->data().getNormal());
          #endif
      }
      N->setVector(normalArray);
    }
  
    //////////////////////////////////////////////////////////////////////////////////// 
    // colors

    #ifdef TEMPLATE_SPECIALIZATION_SUPPORTED
    if (MData::template find<MESH::MeshColorData>()
     && M->template ogetData<MESH::MeshColorData>().hasColor()
     && M->template ogetData<MESH::MeshColorData>().getRGBA())
    #endif
    {
      // creating a new Node.
      X3D::ColorRGBA *C = new X3D::ColorRGBA();
      NI->setChild(C);

      MFColorRGBA colorArray;

      #ifdef TEMPLATE_SPECIALIZATION_SUPPORTED
      if (VData::template find<MESH::VertexColorData>()
       && M->template ogetData<MESH::MeshColorData>().getColorPerVertex())
      #endif 
      {
        // iterating the faces to create the colorIndexArray.
        MFInt32 colorIndexArray;
    
        // Using a temporary map to keep the computed colors and find faster whether the
        // color is in the ColorArray or not.
        
        std::map<SFTemplateVertex<MData, VData, EData, FData, RW> *, std::list<unsigned int> > _vertexMap; 
            
        for (typename TemplateMesh<MData, VData, EData, FData, RW>::MFFace::const_iterator itFace = mf.begin(); itFace != mf.end(); ++itFace)  
        {
          const typename SFTemplateFace<MData, VData, EData, FData, RW>::MFEdge &me = (*itFace)->getEdges();
          for (typename SFTemplateFace<MData, VData, EData, FData, RW>::MFEdge::const_iterator itEdge = me.begin(); itEdge != me.end(); ++itEdge)
          {
            SFTemplateVertex<MData, VData, EData, FData, RW> *V = (*itEdge)->getFromVertex();
            #ifdef TEMPLATE_SPECIALIZATION_SUPPORTED
            const SFColorRGBA &color = V->template ogetData<MESH::VertexColorData>().getColorOfFace(*itFace);
            #else
            const SFColorRGBA &color = V->data().getColorOfFace(*itFace);
            #endif

            // finding the color among the list of colors for V, already present in ColorArray
            std::list<unsigned int>::const_iterator itColorIndex = _vertexMap[V].begin();
            while (itColorIndex != _vertexMap[V].end())
            {
              if (colorArray[*itColorIndex] == color)
              {
                colorIndexArray.push_back(*itColorIndex);
                break;
              }
              ++itColorIndex;
            }
            
            // if not found, adding it to NormalArray
            if (itColorIndex == _vertexMap[V].end())
            {
              _vertexMap[V].push_back(colorArray.size());
              colorIndexArray.push_back(colorArray.size());
              colorArray.push_back(color);
            }
          }  
          colorIndexArray.push_back(-1);
        }
        NI->setColorIndex(colorIndexArray);
      }
      
      #ifdef TEMPLATE_SPECIALIZATION_SUPPORTED
      if (FData::template find<MESH::FaceColorData>()
       && !M->template ogetData<MESH::MeshColorData>().getColorPerVertex())
      #endif 
      {
        for (typename TemplateMesh<MData, VData, EData, FData, RW>::MFFace::const_iterator itFace = mf.begin(); itFace != mf.end(); ++itFace)  
          #ifdef TEMPLATE_SPECIALIZATION_SUPPORTED
          colorArray.push_back((*itFace)->template ogetData<MESH::FaceColorData>().getColor());
          #else
          colorArray.push_back((*itFace)->data().getColor());
          #endif
      }
      C->setColor(colorArray);
    }

    #ifdef TEMPLATE_SPECIALIZATION_SUPPORTED
    if (MData::template find<MESH::MeshColorData>()
     && M->template ogetData<MESH::MeshColorData>().hasColor()
     && !M->template ogetData<MESH::MeshColorData>().getRGBA())
    #endif 
    {
      // creating a new Node.
      X3D::Color *C = new X3D::Color();
      NI->setChild(C);

      MFColor colorArray;

      #ifdef TEMPLATE_SPECIALIZATION_SUPPORTED
      if (VData::template find<MESH::VertexColorData>()
       && M->template ogetData<MESH::MeshColorData>().getColorPerVertex())
      #endif 
      {
        // iterating the faces to create the colorIndexArray.
        MFInt32 colorIndexArray;
    
        // Using a temporary map to keep the computed colors and find faster whether the
        // color is in the ColorArray or not.
        
        std::map<SFTemplateVertex<MData, VData, EData, FData, RW> *, std::list<unsigned int> > _vertexMap; 
            
        for (typename TemplateMesh<MData, VData, EData, FData, RW>::MFFace::const_iterator itFace = mf.begin(); itFace != mf.end(); ++itFace)  
        {
          const typename SFTemplateFace<MData, VData, EData, FData, RW>::MFEdge &me = (*itFace)->getEdges();
          for (typename SFTemplateFace<MData, VData, EData, FData, RW>::MFEdge::const_iterator itEdge = me.begin(); itEdge != me.end(); ++itEdge)
          {
            SFTemplateVertex<MData, VData, EData, FData, RW> *V = (*itEdge)->getFromVertex();

            #ifdef TEMPLATE_SPECIALIZATION_SUPPORTED
            const SFColorRGBA &colorRGBA = V->template ogetData<MESH::VertexColorData>().getColorOfFace(*itFace);
            #else
            const SFColorRGBA &colorRGBA = V->data().getColorOfFace(*itFace);
            #endif
            SFColor color(colorRGBA.r, colorRGBA.g, colorRGBA.b);

            // finding the color among the list of colors for V, already present in ColorArray
            std::list<unsigned int>::const_iterator itColorIndex = _vertexMap[V].begin();
            while (itColorIndex != _vertexMap[V].end())
            {
              if (colorArray[*itColorIndex] == color)
              {
                colorIndexArray.push_back(*itColorIndex);
                break;
              }
              ++itColorIndex;
            }
            
            // if not found, adding it to NormalArray
            if (itColorIndex == _vertexMap[V].end())
            {
              _vertexMap[V].push_back(colorArray.size());
              colorIndexArray.push_back(colorArray.size());
              colorArray.push_back(color);
            }
          }  
          colorIndexArray.push_back(-1);
        }
        NI->setColorIndex(colorIndexArray);
      }
      
      #ifdef TEMPLATE_SPECIALIZATION_SUPPORTED
      if (FData::template find<MESH::FaceColorData>()
       && !M->template ogetData<MESH::MeshColorData>().getColorPerVertex())
      #endif 
      {
        for (typename TemplateMesh<MData, VData, EData, FData, RW>::MFFace::const_iterator itFace = mf.begin(); itFace != mf.end(); ++itFace)  
        {
          #ifdef TEMPLATE_SPECIALIZATION_SUPPORTED
          const SFColorRGBA &colorRGBA = (*itFace)->template ogetData<MESH::FaceColorData>().getColor();
          #else
          const SFColorRGBA &colorRGBA = (*itFace)->data().getColor();
          #endif
          SFColor color(colorRGBA.r, colorRGBA.g, colorRGBA.b);
          colorArray.push_back(color);
        }  
      }
      C->setColor(colorArray);
    }


    //////////////////////////////////////////////////////////////////////////////////// 
    // texCoords

    #ifdef TEMPLATE_SPECIALIZATION_SUPPORTED
    if (MData::template find<MESH::MeshTexCoordData>()
     && VData::template find<MESH::VertexTexCoordData>()
     && M->template ogetData<MESH::MeshTexCoordData>().hasTexCoord())
    #endif 
    {
      X3D::TextureCoordinate *T = new X3D::TextureCoordinate();
      NI->setChild(T);
  
      MFVec2f texCoordArray;
      // iterating the faces to create the normalIndexArray.
      MFInt32 texCoordIndexArray;
  
      // Using a temporary map to keep the computed texCoords and find faster whether the
      // texCoord is in the texCoordArray or not.
        
      std::map<SFTemplateVertex<MData, VData, EData, FData, RW> *, std::list<unsigned int> > _vertexMap; 
  
      for (typename TemplateMesh<MData, VData, EData, FData, RW>::MFFace::const_iterator itFace = mf.begin(); itFace != mf.end(); ++itFace)  
      {
        const typename SFTemplateFace<MData, VData, EData, FData, RW>::MFEdge &me = (*itFace)->getEdges();
        for (typename SFTemplateFace<MData, VData, EData, FData, RW>::MFEdge::const_iterator itEdge = me.begin(); itEdge != me.end(); ++itEdge)
        {
          SFTemplateVertex<MData, VData, EData, FData, RW> *V = (*itEdge)->getFromVertex();
          #ifdef TEMPLATE_SPECIALIZATION_SUPPORTED
          const SFPoint2f &texCoord = V->template ogetData<MESH::VertexTexCoordData>().getTexCoordOfFace(*itFace);
          #else
          const SFPoint2f &texCoord = V->data().getTexCoordOfFace(*itFace);
          #endif
          // finding the color among the list of colors for V, already present in TexCoordArray
          std::list<unsigned int>::const_iterator itTexCoordIndex = _vertexMap[V].begin();
          while (itTexCoordIndex != _vertexMap[V].end())
          {
            if (texCoordArray[*itTexCoordIndex] == SFVec2f(texCoord))
            {
              texCoordIndexArray.push_back(*itTexCoordIndex);
              break;
            }
            ++itTexCoordIndex;
          }
            
          // if not found, adding it to NormalArray
          if (itTexCoordIndex == _vertexMap[V].end())
          {
            _vertexMap[V].push_back(texCoordArray.size());
            texCoordIndexArray.push_back(texCoordArray.size());
            texCoordArray.push_back(SFVec2f(texCoord));
          }
        }  
        texCoordIndexArray.push_back(-1);
      }
      NI->setTexCoordIndex(texCoordIndexArray);
      T->setPoint(texCoordArray);
    }
    stateVariables->addCoupleNode(M, NI);
  }
  stateVariables->pushNode(NI);
}

template<class MData, class VData, class EData, class FData, bool RW>
void TemplateX3DBuilderCoreVisitor<MData, VData, EData, FData, RW>::enterVertex(TemplateVertex<MData, VData, EData, FData, RW> *V)
{
  TemplateX3DBuilderStateVariables<MData, VData, EData, FData, RW> *stateVariables = Singleton<TemplateX3DBuilderStateVariables<MData, VData, EData, FData, RW> >::getInstance();
  X3D::Coordinate *NC = dynamic_cast<X3D::Coordinate *>(stateVariables->getNode(V));
  if (NC == 0)
  {
    NC = new X3D::Coordinate();
    NC->setName(V->getName());
    
    #ifdef TEMPLATE_SPECIALIZATION_SUPPORTED
    if (VData::template find<MESH::VertexPointData>())
    #endif
    {
      // Updating Coordinate.
      const typename TemplateVertex<MData, VData, EData, FData, RW>::MFVertex &mv = V->getVertices();
      MFVec3f point;
    
      for (typename TemplateVertex<MData, VData, EData, FData, RW>::MFVertex::const_iterator it = mv.begin(); it != mv.end(); ++it)
         #ifdef TEMPLATE_SPECIALIZATION_SUPPORTED
         point.push_back(SFVec3f((*it)->template ogetData<MESH::VertexPointData>().getPoint()));
         #else
         point.push_back(SFVec3f((*it)->data().getPoint()));
         #endif
    
      NC->setPoint(point);
    }
    
    stateVariables->addCoupleNode(V, NC);
  }
  stateVariables->pushNode(NC); 
}

template<class MData, class VData, class EData, class FData, bool RW>
void TemplateX3DBuilderCoreVisitor<MData, VData, EData, FData, RW>::leaveX3DNode(X3DNode *)
{
  TemplateX3DBuilderStateVariables<MData, VData, EData, FData, RW> *stateVariables = Singleton<TemplateX3DBuilderStateVariables<MData, VData, EData, FData, RW> >::getInstance();
  stateVariables->popNode();
}

}
}
