////////////////////////////////////////////////////////////////////////////////
//                            MESH_Mesh.inl                                   //
////////////////////////////////////////////////////////////////////////////////

namespace X3DTK {
namespace MESH {

template<class MData, class VData, class EData, class FData, bool RW>
TemplateMesh<MData, VData, EData, FData, RW>::TemplateMesh()
: X3DMeshNode()
{
  define(Recorder<TemplateMesh<MData, VData, EData, FData, RW> >::getTypeName(typeid(*this).name()));
}

template<class MData, class VData, class EData, class FData, bool RW>
TemplateMesh<MData, VData, EData, FData, RW>::~TemplateMesh() 
{
  if ((vertex == 0) && (_faces.size() != 0))
    cx3d << "MESH::Vertex::~TemplateMesh: the vertex child must not be deleted before the destruction of this!" << std::endl;
  else
  {  
    // remove faces from neighbourhood
    for (typename MFFace::iterator f = _faces.begin(); f != _faces.end(); ++f)
      removeFaceFromNeighBourhood(*f); 
  }
  
  // delete faces
  for (typename MFFace::iterator df = _faces.begin(); df != _faces.end(); ++df)
    delete *df; 
}

template<class MData, class VData, class EData, class FData, bool RW>
void TemplateMesh<MData, VData, EData, FData, RW>::setVertex(TemplateVertex<MData, VData, EData, FData, RW> *vertex)
{
  removeParentFromChild(this, this->vertex);  
  this->vertex = vertex;
  addParentToChild(this, this->vertex);  
}

template<class MData, class VData, class EData, class FData, bool RW>
const typename TemplateMesh<MData, VData, EData, FData, RW>::MFVertex &TemplateMesh<MData, VData, EData, FData, RW>::getVertices() const 
{
  if (vertex != 0)
    return static_cast<TemplateVertex<MData, VData, EData, FData, RW> *>(vertex)->getVertices();

  cx3d << "MESH::Vertex::getVertices: cannot return vertices because Vertex is NULL!" << std::endl;  
  static typename TemplateVertex<MData, VData, EData, FData, RW>::MFVertex mv;
  return mv;
}

template<class MData, class VData, class EData, class FData, bool RW> 
const typename TemplateMesh<MData, VData, EData, FData, RW>::MFEdge &TemplateMesh<MData, VData, EData, FData, RW>::getEdges() const 
{
  if (vertex != 0)
    return static_cast<TemplateVertex<MData, VData, EData, FData, RW> *>(vertex)->getEdges();
  
  cx3d << "MESH::Vertex::getEdges: cannot return edges because Vertex is NULL!" << std::endl;  
  static typename TemplateVertex<MData, VData, EData, FData, RW>::MFEdge me;
  return me;
}

template<class MData, class VData, class EData, class FData, bool RW>
const typename TemplateMesh<MData, VData, EData, FData, RW>::MFFace &TemplateMesh<MData, VData, EData, FData, RW>::getFaces() const 
{
  return _faces;
}

template<class MData, class VData, class EData, class FData, bool RW>
MData &TemplateMesh<MData, VData, EData, FData, RW>::data() 
{
  return _data;
}

template<class MData, class VData, class EData, class FData, bool RW>
const MData &TemplateMesh<MData, VData, EData, FData, RW>::data() const 
{
  return _data;
}

#ifdef TEMPLATE_SPECIALIZATION_SUPPORTED
template<class MData, class VData, class EData, class FData, bool RW>
template<class F>  
F &TemplateMesh<MData, VData, EData, FData, RW>::getData()
{
  return _data.template get<F, false>();
}

template<class MData, class VData, class EData, class FData, bool RW>
template<class F>  
F &TemplateMesh<MData, VData, EData, FData, RW>::ogetData()
{
  return _data.template get<F, true>();
}
#endif

template<class MData, class VData, class EData, class FData, bool RW>
SFTemplateFace<MData, VData, EData, FData, RW> *TemplateMesh<MData, VData, EData, FData, RW>::createFace(const MFInt32 &coordIndex, bool ccw) 
{
  return createFace(_faces.size(), coordIndex, ccw);
}

template<class MData, class VData, class EData, class FData, bool RW>  
SFTemplateFace<MData, VData, EData, FData, RW> *TemplateMesh<MData, VData, EData, FData, RW>::createFace(unsigned int i, const MFInt32 &coordIndex, bool ccw) 
{
  if (vertex == 0)
  {
    cx3d << "MESH::Mesh::createFace: cannot create face because Vertex is NULL!" << std::endl;
    return 0;
  }
      
  // Creating the Face.
  SFTemplateFace<MData, VData, EData, FData, RW> *F = new SFTemplateFace<MData, VData, EData, FData, RW>(i);
  F->_mesh = this;
  _faces.insert(std::pair<unsigned int, SFTemplateFace<MData, VData, EData, FData, RW> *>(i, F));
  
  if (ccw)
  {
    // Creating the Edges.
    MFInt32::const_iterator itCoordIndex = coordIndex.begin();
    SFTemplateVertex<MData, VData, EData, FData, RW> *A, *B;
    SFTemplateEdge<MData, VData, EData, FData, RW> *E;
  
    const MFVertex &mv = getVertices();
    
    if ((*itCoordIndex < 0) || (static_cast<unsigned int>(*itCoordIndex) >= mv.size())) return 0;
    SFTemplateVertex<MData, VData, EData, FData, RW> *start = mv[*itCoordIndex]; 
  
    A = start;
    ++itCoordIndex;
  
    while (itCoordIndex != coordIndex.end())
    {
      if ((*itCoordIndex < 0) || (static_cast<unsigned int>(*itCoordIndex) >= mv.size())) return 0;
      B = mv[*itCoordIndex];
      ++itCoordIndex;
    
      E = static_cast<TemplateVertex<MData, VData, EData, FData, RW> *>(vertex)->getEdge(A, B);
      if (E == 0)
        E = static_cast<TemplateVertex<MData, VData, EData, FData, RW> *>(vertex)->createEdge(A, B);
      
      E->addLeftFace(F);    
      E->getFromVertex()->addFace(F);    
      F->addEdge(E);
    
      A = B;
    }
    B  = start;
    E = static_cast<TemplateVertex<MData, VData, EData, FData, RW> *>(vertex)->getEdge(A, B);
    if (E == 0)
      E = static_cast<TemplateVertex<MData, VData, EData, FData, RW> *>(vertex)->createEdge(A, B);
 
    E->addLeftFace(F);    
    E->getFromVertex()->addFace(F);
    F->addEdge(E);
  }
  else
  {
    // Creating the Edges.
    MFInt32::const_reverse_iterator itCoordIndex = coordIndex.rbegin();
    SFTemplateVertex<MData, VData, EData, FData, RW> *A, *B;
    SFTemplateEdge<MData, VData, EData, FData, RW> *E;
  
    const MFVertex &mv = getVertices();
    
    if ((*itCoordIndex < 0) || (static_cast<unsigned int>(*itCoordIndex) >= mv.size())) return 0;
    SFTemplateVertex<MData, VData, EData, FData, RW> *start = mv[*itCoordIndex]; 
  
    A = start;
    ++itCoordIndex;
  
    while (itCoordIndex != coordIndex.rend())
    {
      if ((*itCoordIndex < 0) || (static_cast<unsigned int>(*itCoordIndex) >= mv.size())) return 0;
      B = mv[*itCoordIndex];
      ++itCoordIndex;
    
      E = static_cast<TemplateVertex<MData, VData, EData, FData, RW> *>(vertex)->getEdge(A, B);
      if (E == 0)
        E = static_cast<TemplateVertex<MData, VData, EData, FData, RW> *>(vertex)->createEdge(A, B);

      E->addLeftFace(F);    
      E->getFromVertex()->addFace(F);    
      F->addEdge(E);
    
      A = B;
    }
    B = start;
    E = static_cast<TemplateVertex<MData, VData, EData, FData, RW> *>(vertex)->getEdge(A, B);
    if (E == 0)
      E = static_cast<TemplateVertex<MData, VData, EData, FData, RW> *>(vertex)->createEdge(A, B);
  
    E->addLeftFace(F);    
    E->getFromVertex()->addFace(F);
    F->addEdge(E);
  }
          
  return F;
}

template<class MData, class VData, class EData, class FData, bool RW>
void TemplateMesh<MData, VData, EData, FData, RW>::removeFaceFromNeighBourhood(SFTemplateFace<MData, VData, EData, FData, RW> *face)
{
  const typename SFTemplateFace<MData, VData, EData, FData, RW>::MFEdge &me = face->getEdges();
  for (typename SFTemplateFace<MData, VData, EData, FData, RW>::MFEdge::const_iterator e = me.begin(); e != me.end(); ++e)
  {
    (*e)->removeLeftFace(face);
    (*e)->getFromVertex()->removeFace(face);
    if ((*e)->getRightFaces().size() == 0 && (*e)->getLeftFaces().size() == 0)
      (*e)->getFromVertex()->removeEdge(*e);
  }
}

template<class MData, class VData, class EData, class FData, bool RW>
bool TemplateMesh<MData, VData, EData, FData, RW>::removeFace(SFTemplateFace<MData, VData, EData, FData, RW> *face)
{
  // removing the face from the face array.
  typename MFFace::iterator it = _faces.find(face);
  if (it != _faces.end())
    _faces.erase(it);
  else
    return false;
  
  removeFaceFromNeighBourhood(face);

  const typename SFTemplateFace<MData, VData, EData, FData, RW>::MFEdge &me = face->getEdges();
  for (typename SFTemplateFace<MData, VData, EData, FData, RW>::MFEdge::const_iterator e = me.begin(); e != me.end(); ++e)
  {
    if ((*e)->getRightFaces().size() == 0 && (*e)->getLeftFaces().size() == 0)
      static_cast<TemplateVertex<MData, VData, EData, FData, RW> *>(vertex)->removeEdge(*e);
  }
    
  delete face;
  return true;
}

}
}
