namespace X3DTK {
namespace MESH {

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

template<class MData, class VData, class EData, class FData, bool RW>
void TemplateNormalsDrawerCoreVisitor<MData, VData, EData, FData, RW>::enterMesh(TemplateMesh<MData, VData, EData, FData, RW> *M)
{
  TemplateNormalsDrawerStateVariables<MData, VData, EData, FData, RW> *stateVariables = Singleton<TemplateNormalsDrawerStateVariables<MData, VData, EData, FData, RW> >::getInstance();
  const typename TemplateMesh<MData, VData, EData, FData, RW>::MFFace &faces = M->getFaces();
 
  float amplitude = stateVariables->getAmplitude();

  glColor3f(0.2f, 0.2f, 0.9f);

  glBegin(GL_LINES);
  for (typename TemplateMesh<MData, VData, EData, FData, RW>::MFFace::const_iterator f = faces.begin(), end = faces.end() ; f != end; ++f)
  {
    const typename SFTemplateFace<MData, VData, EData, FData, RW>::MFEdge &edges = (*f)->getEdges();
  
    if (!M->template ogetData<MESH::MeshNormalData>().getNormalPerVertex())
    {
      SFPoint3f center;
    
      // computing the center of the face.
      typename SFTemplateFace<MData, VData, EData, FData, RW>::MFEdge::const_iterator e = edges.begin();
    
      if (e != edges.end())
        #ifdef TEMPLATE_SPECIALIZATION_SUPPORTED
        center = (*edges.begin())->getFromVertex()->template ogetData<MESH::VertexPointData>().getPoint();
        #else
        center = (*edges.begin())->getFromVertex()->data().getPoint();
        #endif
      else
        break;
    
      ++e;  
      while (e != edges.end())
      {
        #ifdef TEMPLATE_SPECIALIZATION_SUPPORTED
        center += (*e)->getFromVertex()->template ogetData<MESH::VertexPointData>().getPoint();
        #else
        center += (*e)->getFromVertex()->data().getPoint();
        #endif
        ++e;
      }
    
      float n = 1.0f/static_cast<float>(edges.size());
      center = n*center;
    
      #ifdef TEMPLATE_SPECIALIZATION_SUPPORTED
      SFPoint3f normal = center + amplitude*((*f)->template ogetData<MESH::FaceNormalData>().getNormal());
      #else
      SFPoint3f normal = center + amplitude*((*f)->data().getNormal());
      #endif
      // drawing.
      glVertex3fv(center.f_data());
      glVertex3fv(normal.f_data());  
    }
    else
    {
      for (typename SFTemplateFace<MData, VData, EData, FData, RW>::MFEdge::const_iterator e = edges.begin(), end = edges.end() ; e != end; ++e)
      {
        #ifdef TEMPLATE_SPECIALIZATION_SUPPORTED
        SFPoint3f center = (*e)->getFromVertex()->template ogetData<MESH::VertexPointData>().getPoint();
        SFPoint3f normal = center + amplitude*((*e)->getFromVertex()->template ogetData<MESH::VertexNormalData>().getNormalOfFace(*f));
        #else
        SFPoint3f center = (*e)->getFromVertex()->data().getPoint();
        SFPoint3f normal = center + amplitude*((*e)->getFromVertex()->data().getNormalOfFace(*f));
        #endif
        
        // drawing.
        glVertex3fv(center.f_data());
        glVertex3fv(normal.f_data()); 
      }
    }  
  }
  glEnd();
}
template<class MData, class VData, class EData, class FData, bool RW>
void TemplateNormalsDrawerCoreVisitor<MData, VData, EData, FData, RW>::enterTransform(Transform *T)
{
  TemplateNormalsDrawerStateVariables<MData, VData, EData, FData, RW> *stateVariables = Singleton<TemplateNormalsDrawerStateVariables<MData, VData, EData, FData, RW> >::getInstance();
  glMatrixMode(GL_MODELVIEW);
  stateVariables->pushMatrix(T->getTransform());
  glPushMatrix();
  glMultMatrixf(T->getTransform().toFloat16());
}

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

}
}
