// gen_LevelSet - An automatic generator of meshes from level-sets
// Copyright (C) 2012-2026 Eric Bechet, Frederic Duboeuf
//
// See the LICENSE file for license information.
// Please report all bugs and problems to <bechet@cadxfem.org> or <duboeuf@outlook.com>.

#ifndef _BALL_MANAGER_H_
#define _BALL_MANAGER_H_

#include "genSingleton.h"
#include "MElement.h"
#include "genGroupOfElements.h"
#include <functional>
#include <cassert>

template <class DataV,class DataE> class BVertex;

template <class DataE, class DataV> class BElement
{
public :
  typedef BVertex<DataV,DataE> BVertexType;
  typedef BElement<DataE,DataV> BElementType;

  int eNum;
  MElement* e;
  std::vector<BVertexType*> Bvertices; // map of localIndex per BVertex
  int flag;
protected :
  DataE* data;

public :
  BElement(MElement* e_) : eNum(e_->getNum()), e(e_), flag(0), data(0) {}
  BElement(const BElement &other) : eNum(other.eNum), e(other.e), Bvertices(other.Bvertices), flag(other.flag), data(0) {}

  int getNum() const {return eNum;}
  void addBVertex(BVertexType* Bv) {Bvertices.push_back(Bv);}
  void flagBVertices(int flag_) { for (int i=0; i<Bvertices.size(); ++i) {Bvertices[i]->flag=flag_;} }
  BVertexType* getBVertice(int i) {return Bvertices[i];}
  int getNumBvertices() const {return (int)Bvertices.size();}
  void setData(DataE* data_) {data = data_;}
  DataE* getData() const {return data;}
  void clearData() {if (data) {delete data; data = NULL;}}
  void check() {assert((int)Bvertices.size()==e->getNumVertices());}

  friend std::ostream & operator <<(std::ostream &os, const BElement<DataE,DataV> &Be)
  {
    os << "BElement " << Be.getNum() << " : flag=" << Be.flag << ", nbVertices=" << Be.getNumBvertices() << "\n";
    os << " contains Vertices :";
    for (int i=0; i<Be.getNumBvertices(); ++i)
      os << " " << (Be.Bvertices[i])->getNum();
    os << "\n";
    return os;
  }
};

template <class DataV,class DataE> class BVertex
{
public :
  typedef BVertex<DataV,DataE> BVertexType;
  typedef BElement<DataE,DataV> BElementType;

  int vNum;
  MVertex* v;
  std::vector<BElementType*> Belements; // map of localIndex per BElement
  int flag;
protected :
  DataV* data;

public :
  BVertex(MVertex* v_) : vNum(v_->getNum()), v(v_), flag(0), data(0) {}
  BVertex(MVertex* v_, BElementType* Be) : vNum(v_->getNum()), v(v_), flag(0), data(0) {Belements.push_back(Be);}
  BVertex(const BVertex &other) : vNum(other.vNum), v(other.v), Belements(other.Belements), flag(other.flag), data(0) {}

  int getNum() const {return vNum;}
  void addBElement(BElementType* Be) {Belements.push_back(Be);}
  void flagBElements(int flag_) { for (int i=0; i<Belements.size(); ++i) {Belements[i]->flag=flag_;} }
  BElementType* getBElement(int i) {return Belements[i];}
  int getNumBelements() const {return (int)Belements.size();}
  void setData(DataV* data_) {data = data_;}
  DataV* getData() const {return data;}
  void clearData() {if (data) {delete data; data = NULL;}}

  friend std::ostream & operator <<(std::ostream &os, const BVertex<DataV,DataE> &Bv)
  {
    os << "BVertex " << Bv.getNum() << " : flag=" << Bv.flag << ", nbElements=" << Bv.getNumBelements() << "\n";
    os << " contains Elements :";
    for (int i=0; i<Bv.getNumBelements(); ++i)
      os << " " << (Bv.Belements[i])->getNum();
    os << "\n";
    return os;
  }
};


// Classe de comparaison de pointeurs sur BVertex :
template <class BV> class pBVertexLess //: public std::binary_function<BV*, BV*, bool> //deprecated
{
public:
  bool operator()(const BV* Bv1, const BV* Bv2) const
  {
    return (Bv1->getNum() < Bv2->getNum());
  }
};
// Classe de comparaison de pointeurs sur BElement :
template <class BE> class pBElementLess //: public std::binary_function<BE*, BE*, bool> //deprecated
{
public:
  bool operator()(const BE* Be1, const BE* Be2) const
  {
    return (Be1->getNum() < Be2->getNum());
  }
};

struct Deleter
{
  template <typename T> void operator () (T* ptr)
  {
    delete ptr;
  }
};

template <class DataV,class DataE> class BallManager : public Singleton<BallManager<DataV,DataE> >
{
public :
  typedef BVertex<DataV,DataE> BVertexType;
  typedef BElement<DataE,DataV> BElementType;
  typedef std::set<BVertexType*, pBVertexLess<BVertexType> > BVertexContainer;  // set of BVertex per MVertex number
  typedef std::set<BElementType*, pBElementLess<BElementType> > BElementContainer;// set of BElement per MElement number
protected :
  BVertexContainer Bvertices;
  BElementContainer Belements;
  genGroupOfElements* g;
  bool isAlreadyBuilt;

private:
  friend class Singleton<BallManager<DataV,DataE> >;
  BallManager() : g(0) {isAlreadyBuilt=false;}
  ~BallManager() {clearBalls();}
protected :
  void addBElement(BElementType* Be)
  {
//     assert(getBElement(Be->e->getNum())==NULL);
    Be->check();
    Belements.insert(Be);
  }
  void buildBalls()
  {
    assert(g);
    std::set<MElement*>::iterator ite;
    for (ite = g->begin(); ite != g->end(); ++ite)
      addElement(*ite);
    isAlreadyBuilt=true;
  }
  void clearVDatas()
  {
    typename BVertexContainer::iterator itBv;
    for (itBv = Bvertices.begin(); itBv != Bvertices.end(); ++itBv)
    {
      (*itBv)->flag=0;
      (*itBv)->clearData();
    }
  }
  void clearEDatas()
  {
    typename BElementContainer::iterator itBe;
    for (itBe = Belements.begin(); itBe != Belements.end(); ++itBe)
    {
      (*itBe)->flag=0;
      (*itBe)->clearData();
    }
  }
  void clearBvertices()
  {
    std::for_each (Bvertices.begin (), Bvertices.end (), Deleter());
    Bvertices.clear();
  }
  void clearBelements()
  {
    std::for_each (Belements.begin (), Belements.end (), Deleter());
    Belements.clear();
  }
  void clearBalls()
  {
    clearDatas();
    clearBvertices();
    clearBelements();
    isAlreadyBuilt=false;
  }
public :
  bool isItAlreadyBuilt() const {return isAlreadyBuilt;}
  void addVertexOfBElement(MVertex* v, BElementType* Be)
  {
    BVertexType* Bv = NULL;
    typename BVertexContainer::const_iterator itBv;
    itBv = vfind(v);
    if (itBv!=vend())
    {
      Bv = (*itBv);
      Bv->addBElement(Be);
    }
    else
    {
      Bv = new BVertexType(v,Be);
      Bvertices.insert(Bv);
    }
    Be->Bvertices.push_back(Bv);
  }
  void addElement(MElement* e)
  {
    BElementType* Be = new BElementType(e);

    std::vector<MVertex*> vertices;
    e->getVertices(vertices);
    for (int j=0; j<e->getNumVertices(); ++j)
    {
      MVertex* v = vertices[j];
      addVertexOfBElement(v,Be);
    }
    addBElement(Be);
  }
  int vsize() const {return (int)Bvertices.size();}
  int size() const {return (int)Belements.size();}
  typename BVertexContainer::const_iterator vbegin() const {return Bvertices.begin();}
  typename BVertexContainer::const_iterator vend() const {return Bvertices.end();}
  typename BElementContainer::const_iterator begin() const {return Belements.begin();}
  typename BElementContainer::const_iterator end() const {return Belements.end();}
  typename BVertexContainer::iterator vfind(MVertex* v) const {BVertexType Bv(v); return Bvertices.find(&Bv);}
  typename BElementContainer::iterator find(MElement* e) const {BElementType Be(e); return Belements.find(&Be);}
  typename BVertexContainer::iterator Bvfind(BVertexType* Bv) const {return Bvertices.find(Bv);}
  typename BElementContainer::iterator Bfind(BElementType* Be) const {return Belements.find(Be);}

  void clearDatas()
  {
    clearVDatas();
    clearEDatas();
  }

  void setGroup(genGroupOfElements* g_)
  {
    if (g!=g_)
    {
      clearBalls();
      g = g_;
      buildBalls();
    }
    else clearDatas();
  }
  genGroupOfElements* getGroup() const {return g;}
};


#endif //_BALL_MANAGER_H_
