////////////////////////////////////////////////////////////////////////////////
//                            X3D_MeshBuilderGeometry3DVisitor.inl            //
////////////////////////////////////////////////////////////////////////////////

namespace X3DTK {
namespace X3D {

template<class MData, class VData, class EData, class FData, bool RW>
TemplateMeshBuilderGeometry3DVisitor<MData, VData, EData, FData, RW>::TemplateMeshBuilderGeometry3DVisitor()
: Geometry3DVisitor()
{
  define(Recorder<IndexedFaceSet>::getEnterFunction(&TemplateMeshBuilderGeometry3DVisitor::enterIndexedFaceSet));
  define(Recorder<IndexedFaceSet>::getLeaveFunction(&TemplateMeshBuilderGeometry3DVisitor::leaveIndexedFaceSet));
}

template<class MData, class VData, class EData, class FData, bool RW>
void TemplateMeshBuilderGeometry3DVisitor<MData, VData, EData, FData, RW>::enterIndexedFaceSet(IndexedFaceSet *I)
{
  TemplateMeshBuilderStateVariables<MData, VData, EData, FData, RW> *stateVariables = Singleton<TemplateMeshBuilderStateVariables<MData, VData, EData, FData, RW> >::getInstance();
  MESH::TemplateMesh<MData, VData, EData, FData, RW> *TM = dynamic_cast<MESH::TemplateMesh<MData, VData, EData, FData, RW> *>(stateVariables->getNode(I));
  if (TM == 0)
  {
    TM = new MESH::TemplateMesh<MData, VData, EData, FData, RW>();
    TM->setName(I->getName());
    
    // normals
    #ifdef TEMPLATE_SPECIALIZATION_SUPPORTED
    if (MData::template find<MESH::MeshNormalData>())
    {
      TM->template ogetData<MESH::MeshNormalData>().setNormal(I->getNormal() != 0);
      TM->template ogetData<MESH::MeshNormalData>().setNormalPerVertex(I->getNormalPerVertex());
      TM->template ogetData<MESH::MeshNormalData>().setCreaseAngle(I->getCreaseAngle());
    }
    
    // colors
    if (MData::template find<MESH::MeshColorData>())
    {
      TM->template ogetData<MESH::MeshColorData>().setColor(I->getColor() != 0);
      TM->template ogetData<MESH::MeshColorData>().setColorPerVertex(I->getColorPerVertex());
      TM->template ogetData<MESH::MeshColorData>().setRGBA(dynamic_cast<X3D::ColorRGBA *>(I->getColor()) != 0);
    }
    
    // texCoords
    if (MData::template find<MESH::MeshTexCoordData>())
    {
      TM->template ogetData<MESH::MeshTexCoordData>().setTexCoord(I->getTexCoord() != 0);
    }
    
    // Solid
    if (MData::template find<MESH::MeshSolidData>())
    {
      TM->template ogetData<MESH::MeshSolidData>().setSolid(I->getSolid() != 0);
    }
    #else
    TM->data().setNormal(I->getNormal() != 0);
    TM->data().setNormalPerVertex(I->getNormalPerVertex());
    TM->data().setCreaseAngle(I->getCreaseAngle());

    // colors
    TM->data().setColor(I->getColor() != 0);
    TM->data().setColorPerVertex(I->getColorPerVertex());
    TM->data().setRGBA(dynamic_cast<X3D::ColorRGBA *>(I->getColor()) != 0);
    
    // texCoords
    TM->data().setTexCoord(I->getTexCoord() != 0);
    // Solid
    TM->data().setSolid(I->getSolid() != 0);
    #endif
    ////////////////////////////////////////////////////////////////////////////
    // Coordinate
    
    Coordinate *C = dynamic_cast<Coordinate *>(I->getCoord());
  
    MESH::TemplateVertex<MData, VData, EData, FData, RW> *MC = dynamic_cast<MESH::TemplateVertex<MData, VData, EData, FData, RW> *>(stateVariables->getNode(C));
    if (MC == 0)
    {
      MC = new MESH::TemplateVertex<MData, VData, EData, FData, RW>();
      MC->setName(C->getName());
      stateVariables->addCoupleNode(C, MC);

      // Filling the Vertices.
      const MFVec3f &point = C->getPoint();

      for (MFVec3f::const_iterator it = point.begin(); it != point.end(); ++it)
      {
        MESH::SFTemplateVertex<MData, VData, EData, FData, RW> *V = MC->createVertex();
        #ifdef TEMPLATE_SPECIALIZATION_SUPPORTED
        if (VData::template find<MESH::VertexPointData>())
          V->template ogetData<MESH::VertexPointData>().setPoint(SFPoint3f(*it));
        #else 
        V->data().setPoint(SFPoint3f(*it)); 
        #endif
      }  
    }
    TM->setVertex(MC);
    
    ////////////////////////////////////////////////////////////////////////////
    // Creating faces
        
    // future optim? reserve the vertices and edges.
    SFBool ccw = I->getCcw();
  
    //iterating the faces
    const MFInt32 &coordIndex = I->getCoordIndex();
    MFInt32::const_iterator itCoordIndex = coordIndex.begin();
  
    while (itCoordIndex != coordIndex.end())
    {
      MFInt32 faceCoordIndex;
      while (*itCoordIndex != -1)
      {
        faceCoordIndex.push_back(*itCoordIndex);
        ++itCoordIndex;
      }
      TM->createFace(faceCoordIndex, ccw);
      
      ++itCoordIndex;
    }
    
    ////////////////////////////////////////////////////////////////////////////
    // Other fields
    
    ////////////////// arrays //////////////////////////////////////////////////////////
  
    MFVec3f pointArray;
    MFVec3f normalArray;
    MFColorRGBA colorArray;
    MFVec2f texCoordArray;

    X3D::Normal *NormalNode = dynamic_cast<X3D::Normal *>(I->getNormal());
    if (NormalNode != 0)
      normalArray = NormalNode->getVector();
  
    if (I->getColor() != 0)
    {
      if (I->getColor()->getTypeName() == "ColorRGBA")
        colorArray = dynamic_cast<X3D::ColorRGBA *>(I->getColor())->getColor();
      else  
        colorArray = dynamic_cast<X3D::Color *>(I->getColor())->getColor();
    }
  
    X3D::TextureCoordinate *TexCoordNode = dynamic_cast<X3D::TextureCoordinate *>(I->getTexCoord());
    if (TexCoordNode != 0)
      texCoordArray = TexCoordNode->getPoint();
  
    bool hasNormal = !normalArray.empty();
    bool hasColor = !colorArray.empty();
    bool hasTexCoord = !texCoordArray.empty();
    
    ////////////////// indexes /////////////////////////////////////////////////////////
 
    const MFInt32 &colorIndex = I->getColorIndex();
    const MFInt32 &normalIndex = I->getNormalIndex();
    const MFInt32 &texCoordIndex = I->getTexCoordIndex();

    bool hasColorIndex = !colorIndex.empty();
    bool hasNormalIndex = !normalIndex.empty();
    bool hasTexCoordIndex = !texCoordIndex.empty();
    
    ////////////////// iterators ///////////////////////////////////////////////////////
    
    itCoordIndex = coordIndex.begin();
    MFInt32::const_iterator itColorIndex;
    MFInt32::const_iterator itNormalIndex;
    MFInt32::const_iterator itTexCoordIndex;
    unsigned int itFaceIndex = 0; 
    
    if (hasColorIndex)
      itColorIndex = colorIndex.begin();
    else
      itColorIndex = coordIndex.begin();
  
    if (hasNormalIndex)
      itNormalIndex = normalIndex.begin();  
    else
      itNormalIndex = coordIndex.begin();
  
    if (hasTexCoordIndex)
      itTexCoordIndex = texCoordIndex.begin();
    else
      itTexCoordIndex = coordIndex.begin();
    
    typename MESH::TemplateMesh<MData, VData, EData, FData, RW>::MFFace::const_iterator itFace = TM->getFaces().begin(); 
    
    ////////////////// loop ////////////////////////////////////////////////////////////
  
    SFBool normalPerVertex = I->getNormalPerVertex();
    SFBool colorPerVertex = I->getColorPerVertex();
        
    while (itCoordIndex != coordIndex.end())
    {
      while ((*itCoordIndex != -1) && (itCoordIndex != coordIndex.end()))
      {
        MESH::SFTemplateVertex<MData, VData, EData, FData, RW> *V = TM->getVertices()[*itCoordIndex];
     
        #ifdef TEMPLATE_SPECIALIZATION_SUPPORTED
        if (VData::template find<MESH::VertexColorData>() && hasColor)
          if (colorPerVertex)
            V->template ogetData<MESH::VertexColorData>().setColorOfFace(colorArray[*itColorIndex], *itFace);
          else
            (*itFace)->template ogetData<MESH::FaceColorData>().setColor(colorArray[(hasColorIndex ? *itColorIndex : itFaceIndex)]);  
              
        if (VData::template find<MESH::VertexNormalData>() && hasNormal)
          if (normalPerVertex)
            V->template ogetData<MESH::VertexNormalData>().setNormalOfFace(normalArray[*itNormalIndex], *itFace);
          else
            (*itFace)->template ogetData<MESH::FaceNormalData>().setNormal(normalArray[(hasNormalIndex ? *itNormalIndex : itFaceIndex)]);
            
        if (VData::template find<MESH::VertexTexCoordData>() && hasTexCoord)
          V->template ogetData<MESH::VertexTexCoordData>().setTexCoordOfFace(SFPoint2f(texCoordArray[*itTexCoordIndex]), *itFace);  
        #else
        if (hasColor)
          if (colorPerVertex)
            V->data().setColorOfFace(colorArray[*itColorIndex], *itFace);
          else
            (*itFace)->data().setColor(colorArray[(hasColorIndex ? *itColorIndex : itFaceIndex)]);  
        
        if (hasNormal)
          if (normalPerVertex)
            V->data().setNormalOfFace(normalArray[*itNormalIndex], *itFace);
          else
            (*itFace)->data().setNormal(normalArray[(hasNormalIndex ? *itNormalIndex : itFaceIndex)]);
            
        if (hasTexCoord)    
          V->data().setTexCoordOfFace(SFPoint2f(texCoordArray[*itTexCoordIndex]), *itFace);          
        #endif
        
        ++itCoordIndex;
        if (colorPerVertex)
          ++itColorIndex;     
        if (normalPerVertex)
          ++itNormalIndex;      
        ++itTexCoordIndex;
      }
    
      ++itCoordIndex;
      ++itColorIndex;
      ++itNormalIndex;
      ++itTexCoordIndex;
    
      ++itFace;
      ++itFaceIndex;
    }
    
    stateVariables->addCoupleNode(I, TM);
  }
  stateVariables->pushNode(TM);
}

template<class MData, class VData, class EData, class FData, bool RW>
void TemplateMeshBuilderGeometry3DVisitor<MData, VData, EData, FData, RW>::leaveIndexedFaceSet(IndexedFaceSet *)
{
  TemplateMeshBuilderStateVariables<MData, VData, EData, FData, RW> *stateVariables = Singleton<TemplateMeshBuilderStateVariables<MData, VData, EData, FData, RW> >::getInstance();
  stateVariables->popNode();
}

}
}  
