// JL20170204: TO BE REVISED
// Interface_TetGen.cpp
// Inrerface of DarcyLite+/FemLiteC++ with TetGen
// NOTE: The default configuration file ("TetgenConfig.txt") for TetGen
// must be in the project executable directory
// James Liu, Graham Harper, ColoState; 2007/01--2017/02

#include <iostream>
#include <fstream>
#include <string>
#include <stdio.h>

#include "tetgen.h"

#include "TetraMesh.h"


// Getting a piecewise linear complex filename (.poly) from "TetgenConfig.txt",
// which will be used by TetGen

char* Get_PolyFileName_TetGen()
{
  std::string fileName_string;
  std::string line;
  std::ifstream myfile("TetgenConfig.txt");
  
  if(myfile.is_open())  // If the file opens
  {
    while (getline(myfile,line)) {  // Grabbing the next line from the file
      // If the line is empty, skip it;
      // If it starts with $, it is the filename
      if (line.length()>0 && line.compare(0,1,"$")==0) {
        // Grabbing the next line, which is the filename
        getline(myfile,fileName_string);
        break;
      }
    }
  }
  else {  // If there is a problem opening the file
    std::cout << "Unable to open TetGen config file.  Exiting! \n";
    exit(EXIT_FAILURE);
  }
  
  // Converting the string to a character array
  char* fileName = new char[fileName_string.size()+1];
  strcpy(fileName, fileName_string.c_str());

  return fileName;
  // JL20160721: MEMORY LEAKING !!!  fileName not released !!!
}


// Getting TetGen switches from the default config file "TetgenConfig.txt"

char* Get_SwitchString_TetGen()
{
  std::string parameterString;
  std::string configSwitch;
  std::string line;
  std::ifstream myfile("TetgenConfig.txt");

  if (myfile.is_open()) {  // If the file opens
    while (getline(myfile,line)) {  // Grabbing the next line from the file
      // If the line is empty, skip it;
      // If it starts with %, it is a switch
      if (line.length()>0 && line.compare(0,1,"%")==0) {
        // Small block to check whether this is a two-letter switch or a one-letter switch
        if (line.compare(4,1," ")==0) {
          configSwitch = line.substr(3,1);
        }
        else {
          configSwitch = line.substr(3,2);
        }
      
        // Grabbing the next line, since the switch has been identified
        getline(myfile,line);
      
        if (configSwitch.compare(0,1,"a")==0 && line.compare(0,4,"TRUE")!=0 && line.compare(0,5,"FALSE")!=0) {
          parameterString += ("a" + line);  // If a max tetrahedron volume is specified, use it
        }
        else if (configSwitch.compare(0,1,"q")==0 && line.compare(0,4,"TRUE")!=0 && line.compare(0,5,"FALSE")!=0) {
          parameterString += ("q" + line);  // If a max radius-edge ratio and min dihedral angle are specified, use them
        }
        else if (configSwitch.compare(0,1,"O")==0 && line.compare(0,4,"TRUE")!=0 && line.compare(0,5,"FALSE")!=0) {
          parameterString += ("O" + line);  // If a mesh optimization level and methods are given, use them
        }
        else if (configSwitch.compare(0,1,"x")==0 && line.compare(0,4,"TRUE")!=0 && line.compare(0,5,"FALSE")!=0) {
          parameterString += ("x" + line);  // If a value for tetrahedra per memory chunk is given, use it
        }
        else if (configSwitch.compare(0,1,"S")==0 && line.compare(0,4,"TRUE")!=0 && line.compare(0,5,"FALSE")!=0) {
          parameterString += ("S" + line);  // If a value for the maximum number of Steiner points is given, use it
        }
        else if (configSwitch.compare(0,1,"T")==0 && line.compare(0,4,"TRUE")!=0 && line.compare(0,5,"FALSE")!=0) {
          parameterString += ("T" + line);  // If a value for the coplanar tolerance is given, use it
        }
        else if(line.compare(0,4,"TRUE")==0) {
          parameterString += (configSwitch);  // If the switch is marked true (no constants given), add in the switch
        }
      }
    }
  }
  else {  // If there is a problem opening the file
    std::cout << "Unable to open config file.  Exiting! \n";
    exit(EXIT_FAILURE);
  }

  std::cout << "Using switches for Tetgen: " << parameterString << "\n";
  
  // Converting the string to a character array
  char* switches = new char[parameterString.size()+1];
  strcpy(switches, parameterString.c_str());

  return switches;  // Returning the switches
  // JL20160721: MEMORY LEAKING !!!  switches not released !!!
}


// Generating a tetrahedral mesh (actually filling mesh info) by calling TetGen
// TetGen poly filename and swtches are provided by the above two functions

int Gen_TetraMesh_TetGen(TetraMesh &mesh)
{
  int numberNodes, numberFaces, numberElements;
  int *boundaryNodeMark, *boundaryFaceMark;
  int (*labelFaceNode)[3], (*labelFaceElement)[2], (*labelElementNode)[4];
  double (*crd)[3];
  
  tetgenio in, out;

  char* polyFileName = Get_PolyFileName_TetGen();

  in.load_poly(polyFileName);

  in.firstnumber = 1;  // All indices start from 1.
  
  in.save_nodes("barin");  // PLC output to file 'barin.node' & 'barin.poly'
  in.save_poly("barin");
  
  // GH20160607:
  // These default switches are set in the file located in the same folder as the binary now
  // Grabbing the switches from the file in the same folder as the binary
  char* switches = Get_SwitchString_TetGen();
  
  // Tetrahedralizing the PLC.
  // Switches are chosen to read a PLC (p),
  // Do quality mesh generation (q) with a specified quality bound (1.414)
  // Apply a maximum volume constraint (a0.1)
  tetrahedralize(switches, &in, &out);
  
  // Outputing mesh info to files 'barout.node', 'barout.ele', 'barout.face'
  out.save_nodes("barout");
  out.save_elements("barout");
  out.save_faces("barout");
  
  // Counts
  numberNodes = out.numberofpoints;
  // JL20160721: TO BE REVISED: out.numberofedges is NOT all edges
  // numberEdges = out.numberofedges;
  numberFaces = out.numberoftrifaces;
  numberElements = out.numberoftetrahedra;

  // Allocating
  crd = new double[numberNodes][3];
  boundaryNodeMark = new int[numberNodes];
  boundaryFaceMark = new int[numberFaces];
  labelFaceNode = new int[numberFaces][3];
  labelFaceElement = new int[numberFaces][2];
  labelElementNode = new int[numberElements][4];

  // Getting mesh info: Node coordinates
  for (int i=0; i<out.numberofpoints; ++i) {
    crd[i][0] = out.pointlist[3*i+0];
    crd[i][1] = out.pointlist[3*i+1];
    crd[i][2] = out.pointlist[3*i+2];
  }

  // JL20161020: NEED node boundary markers
  // Getting mesh info: Node boundary marks
  // std::cout << "out.numberofpoints=" << out.numberofpoints << "\n" << std::flush;
  // std::cout << "i= ";
  for (int i=0; i<out.numberofpoints; ++i) {
    // std::cout << i << "  " << std::flush;
    // std::cout << out.pointmarkerlist[i] << "  ";
    boundaryNodeMark[i] = out.pointmarkerlist[i];
  }

  // Getting mesh info: Triangular-face vs. 3 nodes
  for (int i=0; i<out.numberoftrifaces; ++i) {
    labelFaceNode[i][0] = out.trifacelist[3*i+0];
    labelFaceNode[i][1] = out.trifacelist[3*i+1];
    labelFaceNode[i][2] = out.trifacelist[3*i+2];
  }

  // Getting mesh info: Triangular-face boundary marks
  for (int i=0; i<out.numberoftrifaces; ++i) {
    boundaryFaceMark[i] = out.trifacemarkerlist[i];
  }
  
  // Getting mesh info: Triangular-face vs. (1 or 2) adjacent element(s)
  for (int i=0; i<out.numberoftrifaces; ++i) {
    labelFaceElement[i][0] = out.adjtetlist[2*i];
    labelFaceElement[i][1] = out.adjtetlist[2*i+1];
  }
  
  // JL20160721: UNFORTUNATELY out.edgelist IS NOT GOOD ENOUGH
  /*
  for (int i=0; i<out.numberofedges; ++i) {  // Added by Graham (edge information)
    // order them smaller then larger
    if (out.edgelist[2*i]<out.edgelist[2*i+1]) {
      labelEdgeNode[i][0] = out.edgelist[2*i];
      labelEdgeNode[i][1] = out.edgelist[2*i+1];
    }
    else {
      labelEdgeNode[i][0] = out.edgelist[2*i+1];
      labelEdgeNode[i][1] = out.edgelist[2*i];
    }
  }
  */

  // Getting mesh info: tetrahedron vs. 4 nodes
  for (int i=0; i<out.numberoftetrahedra; ++i) {
    for (int j=0; j<4; ++j) {
      labelElementNode[i][j] = out.tetrahedronlist[4*i+j];
    }
  }
  
  // Filling-in TetraMesh
  mesh.fillNodeInfo(numberNodes, crd, boundaryNodeMark);
  mesh.fillFaceInfo(numberFaces, labelFaceNode, labelFaceElement, boundaryFaceMark);
  mesh.fillElementInfo(numberElements, labelElementNode);
  
  // Releasing
  delete[] crd;
  delete[] boundaryNodeMark, boundaryFaceMark;
  delete[] labelFaceNode, labelFaceElement;
  delete[] labelElementNode;

  return(0);  // If successful
}

// Interface_TetGen.cpp