// elastic_genTerm_Cutmesh_Xfem - A linear solver for elastic problems using X-FEM
// Copyright (C) 2010-2026 Eric Bechet, Frederic Duboeuf
//
// See the LICENSE file for license information and contributions.
// Please report all bugs and problems to <bechet@cadxfem.org>.

#ifndef _SPACEREDUCER_H_
#define _SPACEREDUCER_H_

#include "MVertex.h"
#include "genGroupOfElements.h"
#include "genTerm.h"
// #include "OS.h"

class IntVertex; // [node of the interface belonging a physical edge] or [mesh node along the interface]
class EVertex; // end vertex of an edge with its connectivity
class IntEdge; // edge cut by the interface

class IntVertex
{
public :
  MVertex* v;
  int flag; // flag associated : 0=unclassified, -1=LooserNode, +1=MeshWinnerNode, +2=WinnerNode     (add 1 or 2 PhysicalVertices with WinnerNode)
  IntEdge* edge;
public :
  IntVertex(MVertex* _v, int _flag=0) : v(_v), flag(_flag), edge(0) {}
};

class EVertex
{
public :
  MVertex* v;
  std::set<IntEdge*> connectivity;
  int flag; // flag associated : 0=NonPhysicalVertex, 1=PhysicalVertex
  std::vector<EVertex*> nonPhysicalConnectivity; // if PhysicalVertex
  std::vector<EVertex*> physicalConnectivity; // if NonPhysicalVertex
  std::vector<IntVertex*> connectedToVitalIVertices;
public :
  EVertex(MVertex* _v, int _flag=0) : v(_v), flag(_flag) {}
  int score() {return connectivity.size();}
  int numNonPhysicalConnectivity() {return nonPhysicalConnectivity.size();} // number of connected NonPhysicalVertex
};

class IntEdge
{
public :
  EVertex* v1;
  EVertex* v2;
  std::vector<IntVertex*> iVertices;  // WARNING with 2 IntVertices along 1 edge
public :
  IntEdge(EVertex* _v1, EVertex* _v2) : v1(_v1), v2(_v2) {}
  int score() {return v1->score()+v2->score();}
  EVertex* otherEVertex(EVertex* v) {if (v==v1) return v2; if (v==v2) return v1; return NULL;}
};

class SpaceReducer
{
private :
  genGroupOfElements* g; // level-set subElements

  // Containers (need a specific destructor)
  std::vector<IntVertex*> AllIVertices; // set of all level-set nodes belonging physical edges
  std::map<MVertex*,EVertex*> MapM2EVertices; // map of all end vertices of edges
  std::map<std::pair<EVertex*,EVertex*>,IntEdge*> Map2IntEdges; // map of all edges
  // Other containers
  std::multimap<int, IntVertex*> ScoreNodes;
  std::vector<IntVertex*> VitalIVertices;
  std::vector<EVertex*> NonPhysicalEVertices;

public :
  std::vector<MVertex*> WinnerNodes;
  std::vector<MVertex*> LooserNodes;
  std::vector<MVertex*> PhysicalVertices; // vector of end vertices of edges with Vital vertices except MeshNodes
  std::vector<MVertex*> NonPhysicalVertices; // complementary of PhysicalVertices in EVertices
  std::vector<MVertex*> MeshNodes; // mesh nodes

private :
  // initialization
  void Initialize();
  // compute score
  void ComputeScore();
  // decimation algorithm, the results are IntVertex's flags
  void SortNodes();
  // compute support
  void ComputeSupport();

  // initialization of needed set and maps
  void fillNodeToEdge();
  // find end vertices of edge in the element associate with node // return the number of end vertices
  int findEndVertices(MVertex* v, MElement* e, std::vector<MVertex*> &endVertices);
  // verify if node belong to edge  // ...normaly has to be done when cutting model...
  bool nodeBelongToEdge(MVertex* v, std::vector<MVertex*> &endVertices);
  // change MVertex to EVertex with MapM2EVertices
  void findEVertex(MVertex* v, EVertex* &Ev);
  // change 2 EVertex to IntEdge with Map2IntEdges
  void findIntEdge(EVertex* &Ev1, EVertex* &Ev2, IntEdge* &edge);
  // kill connected edges
  void killConnectedEdges(IntVertex* &Iv);

  // printing information
  int orig_stdout;
  fpos_t pos_stdout;
  void enablePrintInfo(bool enable) { /*fflush(stdout); fgetpos(stdout, &pos_stdout); orig_stdout=dup(fileno(stdout));  if (!enable) freopen("stdout.out", "w", stdout);*/ }
  void rebootPrintInfo() { /*fflush(stdout); dup2(orig_stdout, fileno(stdout)); close(orig_stdout); clearerr(stdout); fsetpos(stdout, &pos_stdout); */}

public :
  SpaceReducer(genGroupOfElements * LevelSetElements, bool enable=false) : g(LevelSetElements)
  {
//    double t1 = Cpu();
//    printf("### SpaceReducer Algorithm - Start t=0\n");
//    enablePrintInfo(enable);

    Initialize();
    ComputeScore();
    SortNodes();
    ComputeSupport();

//    rebootPrintInfo();
//    double t2 = Cpu();
//    printf("### SpaceReducer Algorithm - Complete t=%gs\n", t2-t1);
  }
  ~SpaceReducer()
  {
    std::vector<IntVertex*>::iterator itIv;
    for (itIv=AllIVertices.begin(); itIv!=AllIVertices.end(); ++itIv)
      delete *itIv;

    std::map<MVertex*,EVertex*>::iterator itEv;
    for (itEv=MapM2EVertices.begin(); itEv!=MapM2EVertices.end(); ++itEv)
      delete itEv->second;

    std::map<std::pair<EVertex*,EVertex*>,IntEdge*>::iterator ite;
    for (ite=Map2IntEdges.begin(); ite!=Map2IntEdges.end(); ++ite)
      delete ite->second;
  }
  void BuildLinearConstraints(genTermBase<1> &space, dofManager<std::complex<double> > *assembler, bool enable=false);
  void BuildLinearConstraints( const std::shared_ptr<genTermBase<1> > &space, dofManager<std::complex<double> > *assembler, bool enable=false)
  {
    BuildLinearConstraints(*space, assembler, enable);
  }
  void BuildResults();
};

#endif // _SPACEREDUCER_H_
