namespace X3DTK {
namespace MESH {

template<class MData, class VData, class EData, class FData, bool RW>
TemplateEdgesDrawerCoreVisitor<MData, VData, EData, FData, RW>::TemplateEdgesDrawerCoreVisitor()
{
  // Enter and leave functions.
  define(Recorder<Transform>::getEnterFunction(&TemplateEdgesDrawerCoreVisitor::enterTransform));
  define(Recorder<TemplateVertex<MData, VData, EData, FData, RW> >::getEnterFunction(&TemplateEdgesDrawerCoreVisitor::enterVertex));
  define(Recorder<Transform>::getLeaveFunction(&TemplateEdgesDrawerCoreVisitor::leaveTransform));
}

template<class MData, class VData, class EData, class FData, bool RW>
void TemplateEdgesDrawerCoreVisitor<MData, VData, EData, FData, RW>::enterVertex(TemplateVertex<MData, VData, EData, FData, RW> *V)
{
  TemplateEdgesDrawerStateVariables<MData, VData, EData, FData, RW> *stateVariables = Singleton<TemplateEdgesDrawerStateVariables<MData, VData, EData, FData, RW> >::getInstance();
  SFMatrix34f transform = stateVariables->getCurrentTransform();
  
  if (!stateVariables->getCouple(V, transform))
  {
    stateVariables->storeVertex(V);
    int meshId = stateVariables->getCurrentMeshId();

    if (stateVariables->getSelectionMode()) 
      glPushName(meshId);

    glColor3f(1.0f, 0.9f, 0.8f);
    glLineWidth(1.0f);
    glDisable(GL_LIGHTING);  

    const typename TemplateVertex<MData, VData, EData, FData, RW>::MFEdge &edges = V->getEdges();

    int index = 0;
    for (typename TemplateVertex<MData, VData, EData, FData, RW>::MFEdge::const_iterator e = edges.begin(), end = edges.end() ; e != end; ++e)
    {
      if (stateVariables->getSelectionMode())
        glPushName(index++);

      if ((*e)->isBoundary())
        glColor3f(0.3f, 0.3f, 0.9f);
      else
      {
        if ((*e)->isNonManifold())
          glColor3f(0.3f, 0.9f, 0.3f);
        else
          glColor3f(0.7f, 0.7f, 0.7f);
      }  

      glBegin(GL_LINES);
      #ifdef TEMPLATE_SPECIALIZATION_SUPPORTED
      SFPoint3f point = (*e)->getFromVertex()->template ogetData<MESH::VertexPointData>().getPoint();
      #else
      SFPoint3f point = (*e)->getFromVertex()->data().getPoint();
      #endif
      glVertex3fv(point.f_data());
      #ifdef TEMPLATE_SPECIALIZATION_SUPPORTED
      point = (*e)->getToVertex()->template ogetData<MESH::VertexPointData>().getPoint();
      #else
      point = (*e)->getToVertex()->data().getPoint();
      #endif
      glVertex3fv(point.f_data());
      glEnd();
    
      if ((*e)->isNonManifold())
      {
        glEnable(GL_LIGHTING);      
        glColor3f(0.7f, 1.0f, 0.7f);
        const typename SFTemplateEdge<MData, VData, EData, FData, RW>::MFFace &lfaces = (*e)->getLeftFaces();
        for (typename SFTemplateEdge<MData, VData, EData, FData, RW>::MFFace::const_iterator f = lfaces.begin(), end = lfaces.end() ; f != end; ++f)
        {
          glBegin(GL_POLYGON);
          #ifdef TEMPLATE_SPECIALIZATION_SUPPORTED
          SFVec3f normal = (*f)->template ogetData<MESH::FaceNormalData>().getNormal();
          #else
          SFVec3f normal = (*f)->data().getNormal();
          #endif
          const typename SFTemplateFace<MData, VData, EData, FData, RW>::MFEdge &edges = (*f)->getEdges();
          for (typename SFTemplateFace<MData, VData, EData, FData, RW>::MFEdge::const_iterator ep = edges.begin(), end = edges.end() ; ep != end; ++ep)
          {
            #ifdef TEMPLATE_SPECIALIZATION_SUPPORTED
            SFPoint3f point = (*ep)->getFromVertex()->template ogetData<MESH::VertexPointData>().getPoint();
            #else
            SFPoint3f point = (*ep)->getFromVertex()->data().getPoint();
            #endif
            glNormal3fv(normal.f_data());
            glVertex3fv(point.f_data());
          }
          glEnd();
        }
        glEnable(GL_LIGHTING);      
        glColor3f(0.2f, 0.6f, 0.2f);
        const typename SFTemplateEdge<MData, VData, EData, FData, RW>::MFFace &rfaces = (*e)->getRightFaces();
        for (typename SFTemplateEdge<MData, VData, EData, FData, RW>::MFFace::const_iterator f = rfaces.begin(), end = rfaces.end() ; f != end; ++f)
        {
          glBegin(GL_POLYGON);
          #ifdef TEMPLATE_SPECIALIZATION_SUPPORTED
          SFVec3f normal = (*f)->template ogetData<MESH::FaceNormalData>().getNormal();
          #else
          SFVec3f normal = (*f)->data().getNormal();
          #endif
          const typename SFTemplateFace<MData, VData, EData, FData, RW>::MFEdge &edges = (*f)->getEdges();
          for (typename SFTemplateFace<MData, VData, EData, FData, RW>::MFEdge::const_iterator ep = edges.begin(), end = edges.end() ; ep != end; ++ep)
          {
            #ifdef TEMPLATE_SPECIALIZATION_SUPPORTED
            SFPoint3f point = (*ep)->getFromVertex()->template ogetData<MESH::VertexPointData>().getPoint();
            #else
            SFPoint3f point = (*ep)->getFromVertex()->data().getPoint();
            #endif
            glNormal3fv(normal.f_data());
            glVertex3fv(point.f_data());
          }
          glEnd();
        }
        glDisable(GL_LIGHTING);        
      }
          
      if (stateVariables->getSelectionMode())
        glPopName();
    }
    if (stateVariables->getSelectionMode())
      glPopName();
  
    stateVariables->addCouple(V, transform);
  }  
}

template<class MData, class VData, class EData, class FData, bool RW>
void TemplateEdgesDrawerCoreVisitor<MData, VData, EData, FData, RW>::enterTransform(Transform *T)
{
  TemplateEdgesDrawerStateVariables<MData, VData, EData, FData, RW> *stateVariables = Singleton<TemplateEdgesDrawerStateVariables<MData, VData, EData, FData, RW> >::getInstance();
  glMatrixMode(GL_MODELVIEW);
  stateVariables->pushTransform(T->getTransform());
  glPushMatrix();
  glMultMatrixf(T->getTransform().toFloat16());
}

template<class MData, class VData, class EData, class FData, bool RW>
void TemplateEdgesDrawerCoreVisitor<MData, VData, EData, FData, RW>::leaveTransform(Transform *)
{
  TemplateEdgesDrawerStateVariables<MData, VData, EData, FData, RW> *stateVariables = Singleton<TemplateEdgesDrawerStateVariables<MData, VData, EData, FData, RW> >::getInstance();
  glMatrixMode(GL_MODELVIEW);
  stateVariables->popTransform();
  glPopMatrix();
}

}
}
