#include <Mesh.hpp>

#ifndef __element_iterator__hpp__
#define __element_iterator__hpp__

namespace SpaMesh
{
  class ElementIterator
  {
    public:
    ElementIterator(const Mesh<3>& mesh) : elemID(0), elemIDStart(0)//, elemTypeID(0), type(0)
    {
      std::copy(mesh.elemMesh.previousTypesSize.begin(), mesh.elemMesh.previousTypesSize.end(),  std::back_inserter(previousTypesSize));
      elemIDEnd = previousTypesSize[previousTypesSize.size()-1]; 
//      while (elemTypeID == previousTypesSize[type+1])
//	type++;
    }
    ElementIterator(const Mesh<2>& mesh) : elemID(0), elemIDStart(0)//, elemTypeID(0), type(0)
    {
      std::copy(mesh.elemMesh.previousTypesSize.begin(), mesh.elemMesh.previousTypesSize.end(),  std::back_inserter(previousTypesSize));
      elemIDEnd = previousTypesSize[previousTypesSize.size()-1]; 
//      while (elemTypeID == previousTypesSize[type+1])
//	type++;
    }
    ElementIterator(const Mesh<1>& mesh) : elemID(0), elemIDStart(0)//, elemTypeID(0), type(0)
    {
      std::copy(mesh.elemMesh.previousTypesSize.begin(), mesh.elemMesh.previousTypesSize.end(),  std::back_inserter(previousTypesSize));
      elemIDEnd = previousTypesSize[previousTypesSize.size()-1]; 
//      while (elemTypeID == previousTypesSize[type+1])
//	type++;
    }
    ElementIterator(const Mesh<3>& mesh, const std::pair<int,int>& elemRange) : elemID(elemRange.first), elemIDStart(elemRange.first), elemIDEnd(elemRange.second)
    {
      std::copy(mesh.elemMesh.previousTypesSize.begin(), mesh.elemMesh.previousTypesSize.end(),  std::back_inserter(previousTypesSize));
    }
    ElementIterator(const Mesh<2>& mesh, const std::pair<int,int>& elemRange) : elemID(elemRange.first), elemIDStart(elemRange.first), elemIDEnd(elemRange.second)
    {
      std::copy(mesh.elemMesh.previousTypesSize.begin(), mesh.elemMesh.previousTypesSize.end(),  std::back_inserter(previousTypesSize));
    }
    ElementIterator(const Mesh<1>& mesh, const std::pair<int,int>& elemRange) : elemID(elemRange.first), elemIDStart(elemRange.first), elemIDEnd(elemRange.second)
    {
      std::copy(mesh.elemMesh.previousTypesSize.begin(), mesh.elemMesh.previousTypesSize.end(),  std::back_inserter(previousTypesSize));
    }

    inline ElementIterator& operator++() 
    { 
      elemID++;
//      elemTypeID++;
//      bool reinitTypeID = false;
//      while (elemTypeID == previousTypesSize[type+1])
//      {
//	type++;
//	reinitTypeID = true;
//      }
//      if (reinitTypeID)
//	elemTypeID = 0;
      return *this; 
    }

    inline operator bool() const { 
      return elemID < elemIDEnd && elemID >= elemIDStart; 
    }

    //inline int getType() const { return type; }
    //inline int getTypeID() const { return elemTypeID; }
    inline int value() const { return elemID; }
    
    inline int getType() const 
    {
      int type = 0;
      while (this->value() - previousTypesSize[type+1] >= 0) 
	type++;
      return type;
    }

    inline int getTypeID() const 
    {
      int type = 0;
      int lid = this->value();
      while (lid - previousTypesSize[type+1] >= 0) 
	type++;
      return lid - previousTypesSize[type];
    }

    private:
    //int type;
    int elemID;
    int elemIDStart;
    int elemIDEnd;
    //int elemTypeID;
    std::vector<int> previousTypesSize;
  };
  
  class FaceIterator
  {
    public:
    FaceIterator(const Mesh<3>& mesh) : faceID(0), faceIDStart(0)
    {
      std::copy(mesh.faceMesh.previousTypesSize.begin(), mesh.faceMesh.previousTypesSize.end(),  std::back_inserter(previousTypesSize));
      faceIDEnd = previousTypesSize[previousTypesSize.size()-1]; 
    }
    FaceIterator(const Mesh<2>& mesh) : faceID(0), faceIDStart(0)
    {
      std::copy(mesh.faceMesh.previousTypesSize.begin(), mesh.faceMesh.previousTypesSize.end(),  std::back_inserter(previousTypesSize));
      faceIDEnd = previousTypesSize[previousTypesSize.size()-1]; 
    }
    FaceIterator(const Mesh<1>& mesh) : faceID(0), faceIDStart(0)
    {
      std::copy(mesh.faceMesh.previousTypesSize.begin(), mesh.faceMesh.previousTypesSize.end(),  std::back_inserter(previousTypesSize));
      faceIDEnd = previousTypesSize[previousTypesSize.size()-1]; 
    }

    inline FaceIterator& operator++() 
    { 
      faceID++;
      return *this; 
    }

    inline operator bool() const { 
      return faceID < faceIDEnd && faceID >= faceIDStart; 
    }

    inline int value() const { return faceID; }
    
    inline int getType() const 
    {
      int type = 0;
      while (this->value() - previousTypesSize[type+1] >= 0) 
	type++;
      return type;
    }

    inline int getTypeID() const 
    {
      int type = 0;
      int lid = this->value();
      while (lid - previousTypesSize[type+1] >= 0) 
	type++;
      return lid - previousTypesSize[type];
    }

    template<unsigned int dim>
    inline std::pair<int,int> getNeighboringElems(const SpaMesh::Mesh<dim>& mesh)
    {
      int type = 0;
      int lid = this->value();
      while (lid - previousTypesSize[type+1] >= 0) 
	type++;
      int typeLid = lid - previousTypesSize[type];
      return std::make_pair(mesh.faceMesh.sideElemsConnectivity[type](0,typeLid),mesh.faceMesh.sideElemsConnectivity[type](1,typeLid));
    }

    private:
    int faceID;
    int faceIDStart;
    int faceIDEnd;
    std::vector<int> previousTypesSize;
  };

  class NeighborIterator : public Connectivity::ElemSidesConnMatrixDynamic::InnerIterator
  {
    public:
    NeighborIterator(const Mesh<3>& mesh, const ElementIterator& elemIt) 
        : Connectivity::ElemSidesConnMatrixDynamic::InnerIterator(mesh.elemMesh.elemElemsConnectivity[elemIt.getType()],elemIt.getTypeID())
    {
      std::copy(mesh.elemMesh.previousTypesSize.begin(), mesh.elemMesh.previousTypesSize.end(),  std::back_inserter(previousTypesSize));
    }
    NeighborIterator(const Mesh<2>& mesh, const ElementIterator& elemIt) 
        : Connectivity::ElemSidesConnMatrixDynamic::InnerIterator(mesh.elemMesh.elemElemsConnectivity[elemIt.getType()],elemIt.getTypeID())
    {
      std::copy(mesh.elemMesh.previousTypesSize.begin(), mesh.elemMesh.previousTypesSize.end(),  std::back_inserter(previousTypesSize));
    }
    NeighborIterator(const Mesh<1>& mesh, const ElementIterator& elemIt) 
        : Connectivity::ElemSidesConnMatrixDynamic::InnerIterator(mesh.elemMesh.elemElemsConnectivity[elemIt.getType()],elemIt.getTypeID())
    {
      std::copy(mesh.elemMesh.previousTypesSize.begin(), mesh.elemMesh.previousTypesSize.end(),  std::back_inserter(previousTypesSize));
    }
   
    inline int getType() const 
    {
      int type = 0;
      while (this->value() - previousTypesSize[type+1] >= 0) 
	type++;
      return type;
    }

    inline int getTypeID() const 
    {
      int type = 0;
      int lid = this->value();
      while (lid - previousTypesSize[type+1] >= 0) 
	type++;
      return lid - previousTypesSize[type];
    }

    private:
    std::vector<int> previousTypesSize;
  };
};
#endif
