// configure, compile and install Gmsh as a library with
//
//   ./configure --disable-gui --disable-netgen --disable-chaco --prefix=/usr/local/
//   make install-lib
//
// Then compile this driver with "g++ celldriver.cpp -lGmsh -llapack -lblas -lgmp"
//
//
// This program creates .msh file chains.msh which represents the generators and
// the cuts of an two port transformer model's homology groups.


#include <stdio.h>
#include <sstream>
#include <gmsh/Gmsh.h>
#include <gmsh/GModel.h>
#include <gmsh/MElement.h>
#include <gmsh/CellComplex.h>
#include <gmsh/ChainComplex.h>

template <class TTypeA, class TTypeB>
bool convert(const TTypeA& input, TTypeB& output ){
  std::stringstream stream;
  stream << input;
  stream >> output;
  return stream.good();
}


int main(int argc, char **argv)
{
  GmshInitialize(argc, argv);
  GModel *m = new GModel();
  m->readGEO("transformer.geo");
  m->mesh(3);
  
  printf("This model has: %d GRegions, %d GFaces, %d GEdges and %d GVertices. \n" , m->getNumRegions(), m->getNumFaces(), m->getNumEdges(), m->getNumVertices());
  
  std::vector<GEntity*> domain;
  std::vector<GEntity*> subdomain;
  
  // whole model the domain
  for(GModel::riter rit = m->firstRegion(); rit != m->lastRegion(); rit++){
    GEntity *r = *rit;
    domain.push_back(r);
  }

  // the ports as subdomain
  GModel::fiter sub = m->firstFace();
  GEntity *s = *sub;
  subdomain.push_back(s);
  s = *(++sub);
  subdomain.push_back(s);
  s =*(++sub);
  subdomain.push_back(s);
  s= *(++sub);
  subdomain.push_back(s);
  
  // create a cell complex
  CellComplex* complex = new CellComplex(domain, subdomain);

  // write the cell complex to a .msh file
  complex->writeComplexMSH("chains.msh");

  printf("Cell complex of this model has: %d volumes, %d faces, %d edges and %d vertices\n",
         complex->getSize(3), complex->getSize(2), complex->getSize(1), complex->getSize(0));  
  
  // reduce the complex in order to ease homology computation
  complex->reduceComplex();
  
  // create a chain complex of a cell complex (construct boundary operator matrices)
  ChainComplex* chains = new ChainComplex(complex);
  // compute the homology using the boundary operator matrices
  chains->computeHomology();
  
  // Append the homology generators to the .msh file as post-processing views. 
  // To visualize the result, open chains.msh with Gmsh GUI and deselect all mesh
  // entities from Tools->Options->Mesh->Visibility.
  for(int j = 0; j < 4; j++){
    for(int i = 1; i <= chains->getBasisSize(j); i++){
    
      std::string generator;
      std::string dimension;
      convert(i, generator);
      convert(j, dimension);
      std::string name = dimension + "D Generator " + generator;
      
      Chain* chain = new Chain(complex->getCells(j), chains->getCoeffVector(j,i), complex, name);
      chain->writeChainMSH("chains.msh");
      delete chain;
      
    }
  }
  
  delete chains;
  
  // restore the cell complex to its prereduced state
  complex->restoreComplex();
  
  // reduce the complex by coreduction, this removes all vertices, hence 0 as an argument 
  complex->coreduceComplex(0);
  
  // create a chain complex of a cell complex (construct boundary operator matrices)
  ChainComplex* dualChains = new ChainComplex(complex);
  
  // transpose the boundary operator matrices, 
  // these are the boundary operator matrices for the dual chain complex
  dualChains->transposeHMatrices();
  
  // compute the homology of the dual complex
  dualChains->computeHomology(true);  
  
  // Append the duals of the cuts of the homology generators to the .msh file.
  for(int j = 3; j > -1; j--){
    for(int i = 1; i <= dualChains->getBasisSize(j); i++){

      std::string generator;
      std::string dimension;
      convert(i, generator);
      convert(3-(j-1), dimension);

      std::string name = dimension + "D Dual cut " + generator;
      Chain* chain = new Chain(complex->getCells(j-1), dualChains->getCoeffVector(j,i), complex, name);
                chain->writeChainMSH("chains.msh");
      delete chain;
    }
  }

  delete dualChains;

  
  delete complex;  
  delete m;
  GmshFinalize();
  
}