/// This file is part of the X3DToolKit library
/// Copyright (C) 2002-2004  Yannick Le Goc (legoc@imag.fr)

/// http://artis.imag.fr/Members/Yannick.Legoc/X3D/

/// This library is free software; you can redistribute it and/or
/// modify it under the terms of the GNU Lesser General Public
/// License as published by the Free Software Foundation; either
/// version 2.1 of the License, or (at your option) any later version.

/// This library is distributed in the hope that it will be useful,
/// but WITHOUT ANY WARRANTY; without even the implied warranty of
/// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
/// Lesser General Public License for more details.

/// You should have received a copy of the GNU Lesser General Public
/// License along with this library; if not, write to the Free Software
/// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
#ifndef MESH_SCENEGRAPHTYPES_H
#define MESH_SCENEGRAPHTYPES_H

#include "Platform.h"

#include <vector>
#include <map>
#include <set>
#include <list>
#include <iostream>

namespace X3DTK {
namespace MESH {

template<class A, class B>
struct ConstMapIterator : public std::map<A, B>::const_iterator
{
  typedef typename std::map<A, B>::const_iterator Base;
  
  ConstMapIterator(const typename std::map<A, B>::const_iterator &it) : Base(it) {}
//  ConstMapIterator(const typename std::map<A, B>::iterator &it) : std::map<A, B>::const_iterator(it) {}
  
  const B &operator*() const 
  {
    return (this->Base::operator*()).second;
  }  
};

template<class A, class B>
struct NonConstMapIterator : public std::map<A, B>::iterator
{
  typedef typename std::map<A, B>::iterator Base;
  
  NonConstMapIterator(const typename std::map<A, B>::iterator &it) : Base(it) {}
  
  B &operator*() const 
  {
    return (this->Base::operator*()).second;
  }  
};

class X3DNode;

template<class MData, class VData, class EData, class FData, bool RW> class SFTemplateVertex;
template<class MData, class VData, class EData, class FData, bool RW> class SFTemplateEdge;
template<class MData, class VData, class EData, class FData, bool RW> class SFTemplateFace;

/*! \brief Class declared in <b><X3DTK/MESH/scenegraph.h></b> providing a multiple SFVertex values
 *
 *  \ingroup MeshSceneGraph
 */

template<class MData, class VData, class EData, class FData, bool RW>
class EntityMFTemplateVertex : public std::list<SFTemplateVertex<MData, VData, EData, FData, RW> *>
{
public:
  typedef typename std::list<SFTemplateVertex<MData, VData, EData, FData, RW> *> Base;
  
  /// Constructor.
  EntityMFTemplateVertex();
  /// Constructor from a vector.
  explicit EntityMFTemplateVertex(const std::vector<SFTemplateVertex<MData, VData, EData, FData, RW> *> &vector);
};


/*! \brief Class declared in <b><X3DTK/MESH/scenegraph.h></b> providing a multiple SFVertex values
 *
 *  \ingroup MeshSceneGraph
 */

template<class MData, class VData, class EData, class FData, bool RW>
class MeshMFTemplateVertex : public std::map<unsigned int, SFTemplateVertex<MData, VData, EData, FData, RW> *>
{
public:
  typedef typename std::map<unsigned int, SFTemplateVertex<MData, VData, EData, FData, RW> *> Base;

  typedef NonConstMapIterator<unsigned int, SFTemplateVertex<MData, VData, EData, FData, RW> *> iterator;
  typedef ConstMapIterator<unsigned int, SFTemplateVertex<MData, VData, EData, FData, RW> *> const_iterator;

  /// Constructor.
  MeshMFTemplateVertex();
  /// Constructor from a vector.
  explicit MeshMFTemplateVertex(const std::vector<SFTemplateVertex<MData, VData, EData, FData, RW> *> &vector);
  
  /// operator[].
  SFTemplateVertex<MData, VData, EData, FData, RW> *const &operator[](const unsigned int &n) const 
  {
    return (*Base::find(n)).second;
  }
  
  /// Finds the element. Is provided for having a common interface with std::set and std::map.
  typename std::map<unsigned int, SFTemplateVertex<MData, VData, EData, FData, RW> *>::iterator find(SFTemplateVertex<MData, VData, EData, FData, RW> *const &element)
  {
    typename Base::iterator it = Base::find(element->getIndex());
    if ((*it).second == element)
      return it;
    return Base::end();  
  }
  
  /// Finds the element. Is provided for having a common interface with std::set and std::map.
  typename std::map<unsigned int, SFTemplateVertex<MData, VData, EData, FData, RW> *>::const_iterator find(SFTemplateVertex<MData, VData, EData, FData, RW> *const &element) const
  {
    typename Base::const_iterator it = Base::find(element->getIndex());
    if ((*it).second == element)
      return it;
    return Base::end();  
  }
};


/*! \brief Class declared in <b><X3DTK/MESH/scenegraph.h></b> providing a multiple SFEdge values
 *
 *  \ingroup MeshSceneGraph
 */

template<class MData, class VData, class EData, class FData, bool RW>
class EntityMFTemplateEdge : public std::list<SFTemplateEdge<MData, VData, EData, FData, RW> *>
{
public:
  typedef typename std::list<SFTemplateEdge<MData, VData, EData, FData, RW> *> Base;

  /// Constructor.
  EntityMFTemplateEdge();
  /// Constructor from a vector.
  explicit EntityMFTemplateEdge(const std::vector<SFTemplateEdge<MData, VData, EData, FData, RW> *> &vector, bool ccw);
};


/*! \brief Class declared in <b><X3DTK/MESH/scenegraph.h></b> providing a multiple SFEdge values
 *
 *  \ingroup MeshSceneGraph
 */

template<class MData, class VData, class EData, class FData, bool RW>
class MeshMFTemplateEdge : public std::set<SFTemplateEdge<MData, VData, EData, FData, RW> *>
{
public:
  typedef typename std::set<SFTemplateEdge<MData, VData, EData, FData, RW> *> Base;

  /// Constructor.
  MeshMFTemplateEdge();
  /// Constructor from a vector.
  explicit MeshMFTemplateEdge(const std::vector<SFTemplateEdge<MData, VData, EData, FData, RW> *> &vector, bool ccw);

  /// operator[].
  SFTemplateEdge<MData, VData, EData, FData, RW> *const &operator[](const unsigned int &n) const
  {
    typename Base::const_iterator it = Base::begin();
    for (unsigned int i = 0; i < n; ++i)
      ++it;
    return *it;  
  }
};


/*! \brief Class declared in <b><X3DTK/MESH/scenegraph.h></b> providing a multiple SFFace values
 *
 *  \ingroup MeshSceneGraph
 */

template<class MData, class VData, class EData, class FData, bool RW>
class EntityMFTemplateFace : public std::list<SFTemplateFace<MData, VData, EData, FData, RW> *>
{
public:
  typedef typename std::list<SFTemplateFace<MData, VData, EData, FData, RW> *> Base;

  /// Constructor.
  EntityMFTemplateFace();
  /// Constructor from a vector.
  explicit EntityMFTemplateFace(const std::vector<SFTemplateFace<MData, VData, EData, FData, RW> *> &vector);
};


/*! \brief Class declared in <b><X3DTK/MESH/scenegraph.h></b> providing a multiple SFFace values
 *
 *  \ingroup MeshSceneGraph
 */

template<class MData, class VData, class EData, class FData, bool RW>
class MeshMFTemplateFace : public std::map<unsigned int, SFTemplateFace<MData, VData, EData, FData, RW> *>
{
public:
  typedef typename std::map<unsigned int, SFTemplateFace<MData, VData, EData, FData, RW> *> Base;

  typedef NonConstMapIterator<unsigned int, SFTemplateFace<MData, VData, EData, FData, RW> *> iterator;
  typedef ConstMapIterator<unsigned int, SFTemplateFace<MData, VData, EData, FData, RW> *> const_iterator;

  /// Constructor.
  MeshMFTemplateFace();
  /// Constructor from a vector.
  explicit MeshMFTemplateFace(const std::vector<SFTemplateFace<MData, VData, EData, FData, RW> *> &vector);

  /// operator [].
  SFTemplateFace<MData, VData, EData, FData, RW> * const &operator[](const unsigned int &n) const 
  {
    return (*Base::find(n)).second;
  }

  /// Finds the element. Is provided for having a common interface with std::set and std::map.
  typename std::map<unsigned int, SFTemplateFace<MData, VData, EData, FData, RW> *>::iterator find(SFTemplateFace<MData, VData, EData, FData, RW> *const &element)
  {
    typename Base::iterator it = Base::find(element->getIndex());
    if ((*it).second == element)  
      return it;
    return Base::end();
  }
  /// Finds the element. Is provided for having a common interface with std::set and std::map.
  typename std::map<unsigned int, SFTemplateFace<MData, VData, EData, FData, RW> *>::const_iterator find(SFTemplateFace<MData, VData, EData, FData, RW> *const &element) const
  {
    typename Base::const_iterator it = Base::find(element->getIndex());
    if ((*it).second == element)  
      return it;
    return Base::end(); 
  }
};

/// Specialisation: not supported by all compilers

#ifdef TEMPLATE_SPECIALIZATION_SUPPORTED

template<class MData, class VData, class EData, class FData>
class EntityMFTemplateVertex<MData, VData, EData, FData, false> : public std::vector<SFTemplateVertex<MData, VData, EData, FData, false> *>
{
public:
  typedef typename std::vector<SFTemplateVertex<MData, VData, EData, FData, false> *> Base;

  /// Constructor.
  EntityMFTemplateVertex();
  /// Constructor from a vector.
  explicit EntityMFTemplateVertex(const std::vector<SFTemplateVertex<MData, VData, EData, FData, false> *> &vector);
};

template<class MData, class VData, class EData, class FData>
class MeshMFTemplateVertex<MData, VData, EData, FData, false> : public std::vector<SFTemplateVertex<MData, VData, EData, FData, false> *>
{
public:
  typedef typename std::vector<SFTemplateVertex<MData, VData, EData, FData, false> *> Base;
  
  /// Constructor.
  MeshMFTemplateVertex();
  /// Constructor from a vector.
  explicit MeshMFTemplateVertex(const std::vector<SFTemplateVertex<MData, VData, EData, FData, false> *> &vector);

  /// Inserts element x at index x.first of the std::vector. Is provided to have common interface with std::map.
  std::pair<typename std::vector<SFTemplateVertex<MData, VData, EData, FData, false> *>::iterator, bool> insert(const std::pair<unsigned int, SFTemplateVertex<MData, VData, EData, FData, false> *> &x)
  {
    if (x.first >= Base::size())
      resize(x.first + 1, 0);
  
    typename Base::iterator it = Base::begin();
    it += x.first;
    *it = x.second;
    
    return std::pair<typename Base::iterator, bool>(it, true);
  }

  /// Finds the element. Is provided for having a common interface with std::set and std::map.
  typename std::vector<SFTemplateVertex<MData, VData, EData, FData, false> *>::iterator find(SFTemplateVertex<MData, VData, EData, FData, false> *const &element) const
  {
    return std::find(Base::begin(), Base::end(), element);
  }

};

template<class MData, class VData, class EData, class FData>
class EntityMFTemplateEdge<MData, VData, EData, FData, false> : public std::vector<SFTemplateEdge<MData, VData, EData, FData, false> *>
{
public:
  typedef typename std::vector<SFTemplateEdge<MData, VData, EData, FData, false> *> Base;

  /// Constructor.
  EntityMFTemplateEdge();
  /// Constructor from a vector.
  explicit EntityMFTemplateEdge(const std::vector<SFTemplateEdge<MData, VData, EData, FData, false> *> &vector, bool ccw);
};

template<class MData, class VData, class EData, class FData>
class MeshMFTemplateEdge<MData, VData, EData, FData, false> : public std::vector<SFTemplateEdge<MData, VData, EData, FData, false> *>
{
public:
  typedef std::vector<SFTemplateEdge<MData, VData, EData, FData, false> *> Base;
  
  /// Constructor.
  MeshMFTemplateEdge();
  /// Constructor from a vector.
  explicit MeshMFTemplateEdge(const std::vector<SFTemplateEdge<MData, VData, EData, FData, false> *> &vector, bool ccw);

  /// Inserts element x at the end of the std::vector. Is equivalent to push_back and provided to have common interface with std::map.
  std::pair<typename std::vector<SFTemplateEdge<MData, VData, EData, FData, false> *>::iterator, bool> insert(const typename std::vector<SFTemplateEdge<MData, VData, EData, FData, false> *>::value_type &x)
  {
    push_back(x);
    return std::pair<typename Base::iterator, bool>(Base::end(), true);
  }

  /// Finds the element. Is provided for having a common interface with std::set and std::map.
  typename std::vector<SFTemplateEdge<MData, VData, EData, FData, false> *>::iterator find(SFTemplateEdge<MData, VData, EData, FData, false> *const &element) const
  {
    return std::find(Base::begin(), Base::end(), element);
  }
};

template<class MData, class VData, class EData, class FData>
class EntityMFTemplateFace<MData, VData, EData, FData, false> : public std::vector<SFTemplateFace<MData, VData, EData, FData, false> *>
{
public:
  typedef typename std::vector<SFTemplateFace<MData, VData, EData, FData, false> *> Base;

  /// Constructor.
  EntityMFTemplateFace();
  /// Constructor from a vector.
  explicit EntityMFTemplateFace(const std::vector<SFTemplateFace<MData, VData, EData, FData, false> *> &vector);
};

template<class MData, class VData, class EData, class FData>
class MeshMFTemplateFace<MData, VData, EData, FData, false> : public std::vector<SFTemplateFace<MData, VData, EData, FData, false> *>
{
public:
  typedef std::vector<SFTemplateFace<MData, VData, EData, FData, false> *> Base;

  /// Constructor.
  MeshMFTemplateFace();
  /// Constructor from a vector.
  explicit MeshMFTemplateFace(const std::vector<SFTemplateFace<MData, VData, EData, FData, false> *> &vector);

  /// Inserts element x at the end of the std::vector. Is equivalent to push_back and provided to have common interface with std::map.
  std::pair<typename Base::iterator, bool> insert(const std::pair<unsigned int, SFTemplateFace<MData, VData, EData, FData, false> *> &x)
  {
    if (x.first >= Base::size())
      resize(x.first + 1, 0);
  
    typename Base::iterator it = Base::begin();
    it += x.first;
    *it = x.second;
    
    return std::pair<typename Base::iterator, bool>(it, true);
  }

  /// Finds the element. Is provided for having a common interface with std::set and std::map.
  typename std::vector<SFTemplateFace<MData, VData, EData, FData, false> *>::iterator find(SFTemplateFace<MData, VData, EData, FData, false> *const &element)
  {
    return std::find(Base::begin(), Base::end(), element);
  }
  /// Finds the element. Is provided for having a common interface with std::set and std::map.
  typename std::vector<SFTemplateFace<MData, VData, EData, FData, false> *>::const_iterator find(SFTemplateFace<MData, VData, EData, FData, false> *const &element) const
  {
    return std::find(Base::begin(), Base::end(), element);
  }
};

#endif

}
}

#include "MESH_SceneGraphTypes.inl"

#endif
