
#define _WIN32_WINNT _WIN32_WINNT_WIN2K
#define OCCGEOMETRY

#include <tchar.h>
#include <iostream>
#include <fstream>
#include <climits>

using namespace std;

namespace nglib {
#include "nglib.h"
}

int showerror(int);

int main(int argc, char* argv[])
{

using namespace nglib;

Ng_Geometry_2D *in2d_geom;
Ng_OCC_Geometry *occ_geom;

Ng_STL_Geometry *stl_geom;
Ng_Mesh *output_mesh;
Ng_Meshing_Parameters mp;
Ng_Result ng_res;
char *fileName;
int geometrytype; // 1=OCC, 0=other
int visualization; // 1=make surface triangulation for visualization, 0=make FEA mesh
double visualizationdeflection;
int numberOfPoints;
double **pointcoords;
double* pointsize;

// Read command line parameters
fileName = argv[1]; // 1st command line parameter is the geometry file

Ng_Init();

cout << "\nNetgen 6.2.1808. Open CASCADE Technology 7.3.0\n\n";


 // 2nd command line parameter is either "v" to generate visualization mesh or the meshing parameters filename to generate an FEA mesh
if(strcmp(argv[2], "v") == 0){

	visualization = 1;
	
	// 3rd command line parameter is the deflection that determines the smoothness of curves in the geometry's representation.
	if(argc>=4){
		visualizationdeflection=atof(argv[3]);
	} else {
		visualizationdeflection=0.01;
	}

}
else {

	visualization = 0;
	
	// Read meshing parameters from dropfile
	ifstream paramFile;
	paramFile.open(argv[2]);
	if (!paramFile) {
		cerr << "Error. Meshing parameter file could not be opened: " << argv[2];
		return showerror(-1);
	}
	paramFile >> mp.uselocalh;
	paramFile >> mp.maxh;
	paramFile >> mp.minh;
	paramFile >> mp.fineness;
	paramFile >> mp.grading;
	paramFile >> mp.elementsperedge;
	paramFile >> mp.elementspercurve;
	paramFile >> mp.closeedgeenable;
	paramFile >> mp.closeedgefact;
	paramFile >> mp.second_order;
	paramFile >> mp.quad_dominated;
	paramFile >> mp.optsurfmeshenable;
	paramFile >> mp.optvolmeshenable;
	paramFile >> mp.optsteps_3d;
	paramFile >> mp.optsteps_2d;


	paramFile >> numberOfPoints;

	pointcoords = (double**)malloc(numberOfPoints * sizeof(*pointcoords));
		for(int i = 0; i < numberOfPoints; i++)
			pointcoords[i] = (double*)malloc(3 * sizeof(**pointcoords));
	pointsize = new double[numberOfPoints];

	for(int pointNum = 0; pointNum < numberOfPoints; pointNum++){
		paramFile >> pointcoords[pointNum][0];
		paramFile >> pointcoords[pointNum][1];
		paramFile >> pointcoords[pointNum][2];
		paramFile >> pointsize[pointNum];
	}

	paramFile.close();

}


geometrytype = 0;
if ((fileName[strlen(fileName)-1] == 'p') || (fileName[strlen(fileName)-1] == 'P')){
	// STEP
	cout << "\n-- Loading STEP file --\n\n";
	occ_geom = Ng_OCC_Load_STEP(fileName);
	geometrytype = 1;
} else

if ((fileName[strlen(fileName)-1] == 's') || (fileName[strlen(fileName)-1] == 'S')){
	// IGES or IGS
	cout << "\n-- Loading IGES file --\n\n";
	occ_geom = Ng_OCC_Load_IGES(fileName);
	geometrytype = 1;
} else

if ((fileName[strlen(fileName)-1] == 'd') || (fileName[strlen(fileName)-1] == 'D')){
	// .in2d 2D line and spline geometry

	cout << "\n-- Loading 2D geometry file --\n\n";
	in2d_geom = Ng_LoadGeometry_2D(fileName);

	cout << "\n-- Generating 2D mesh --\n\n";
	ng_res = Ng_GenerateMesh_2D (in2d_geom, &output_mesh, &mp);
	if (ng_res != NG_OK) return showerror(ng_res);

	if(mp.second_order == 1){
		cout << "\n-- Converting elements to quadratic shapes --\n\n";
		Ng_2D_Generate_SecondOrder(in2d_geom, output_mesh);
	}

} else

if ((fileName[strlen(fileName)-1] == 'l') || (fileName[strlen(fileName)-1] == 'L')){
	// STL surface
	stl_geom = Ng_STL_LoadGeometry(fileName, 0);
	
	output_mesh = Ng_NewMesh();

	ng_res = Ng_STL_InitSTLGeometry (stl_geom);
	if (ng_res != NG_OK) return showerror(ng_res);

	cout << "\n-- Detecting edges --\n\n";
	ng_res = Ng_STL_MakeEdges (stl_geom, output_mesh, &mp);
	if (ng_res != NG_OK) return showerror(ng_res);

	// Local refinement
	for(int pointNum = 0; pointNum< numberOfPoints; pointNum++){
		Ng_RestrictMeshSizePoint (output_mesh, pointcoords[pointNum], pointsize[pointNum]);
	}

	cout << "\n-- Generating surface mesh --\n\n";
	ng_res = Ng_STL_GenerateSurfaceMesh (stl_geom, output_mesh, &mp);
	if (ng_res != NG_OK) return showerror(ng_res);

	if(mp.optvolmeshenable == 1){
		cout << "\n-- Generating volume mesh --\n\n";
		ng_res = Ng_GenerateVolumeMesh(output_mesh, &mp);
		if (ng_res != NG_OK) return showerror(ng_res);
	}

	if(mp.second_order == 1){
		cout << "\n-- Converting elements to quadratic shapes --\n\n";
		Ng_STL_Generate_SecondOrder(stl_geom, output_mesh);
	}

}

if(geometrytype == 1 && visualization == 0){
	// STEP, IGES
	output_mesh = Ng_NewMesh();

	cout << "Ng_OCC_SetLocalMeshSize()\n";
	Ng_OCC_SetLocalMeshSize(occ_geom, output_mesh, &mp);
	cout << "Ng_OCC_SetLocalMeshSize() done\n";

	// Local refinement
	for(int pointNum = 0; pointNum< numberOfPoints; pointNum++){
		Ng_RestrictMeshSizePoint (output_mesh, pointcoords[pointNum], pointsize[pointNum]);
	}

	cout << "\n-- Generating edge mesh --\n\n";
	ng_res = Ng_OCC_GenerateEdgeMesh(occ_geom, output_mesh, &mp);
	if (ng_res != NG_OK) return showerror(ng_res);
	
	cout << "\n-- Generating surface mesh --\n\n";
	ng_res = Ng_OCC_GenerateSurfaceMesh(occ_geom, output_mesh, &mp);
	if (ng_res != NG_OK) return showerror(ng_res);

	if(mp.optvolmeshenable == 1){
		cout << "\n-- Generating volume mesh --\n\n";
		ng_res = Ng_GenerateVolumeMesh(output_mesh, &mp);
		if (ng_res != NG_OK) return showerror(ng_res);
	}

	if(mp.second_order == 1){
		cout << "\n-- Converting elements to quadratic shapes --\n\n";
		Ng_OCC_Generate_SecondOrder(occ_geom, output_mesh);
	}

}

if(geometrytype == 1 && visualization == 1){
	// STEP, IGES visualization mesh
	output_mesh = Ng_NewMesh();

	Ng_Mesh *normals;
	normals = Ng_NewMesh();

	Ng_OCC_BuildVisualizationMesh(occ_geom, output_mesh, normals, visualizationdeflection); // 0.01 for fast preview. 0.001 for mesher.

	// Write normals to file
	Ng_SaveMesh(normals, "normals.vol");

}



Ng_SaveMesh(output_mesh, "ngmeshed.vol");



return 0;
}

int showerror(int errorcode)
{

	for(int i=0;i<20;i++){
		switch(errorcode){
			case -1:
				cout << "FATAL ERROR";
				break;
			case 0:
				cout << "OK";
				break;
			case 1:
				cout << "FATAL ERROR: Surface input error";
				break;
			case 2:
				cout << "FATAL ERROR: Volume failure";
				break;
			case 3:
				cout << "FATAL ERROR: STL input error";
				break;
			case 4:
				cout << "FATAL ERROR: Surface failure";
				break;
			case 5:
				cout << "FATAL ERROR: File not found";
				break;
		}
		cout << "             \n";

	}

	return errorcode;

}

