#define NOMINMAX

#include <chrono>
#include "mesh/Mesh.h"
#include "mesh/MeshGenerator.h"
#include <BRepBuilderAPI_MakeWire.hxx>
#include <GC_MakeSegment.hxx>
#include <BRepBuilderAPI_MakeEdge.hxx>
#include <BRepBuilderAPI_MakeFace.hxx>
#include <TopoDS.hxx>
#include <TopoDS_Face.hxx>
#include <TopExp_Explorer.hxx>
#include <BRepPrimAPI_MakeBox.hxx>
#include <BRepPrimAPI_MakeSphere.hxx>
#include <BRepTools.hxx>
#include <STEPControl_Writer.hxx>
#include <STEPControl_Reader.hxx>
#include <GC_MakeArcOfCircle.hxx>
#include <filesystem>
#include "topology/TopoEntity.h"
#include "topology/shapePartition.h"
#include "easylogging++/easylogging++.h"
#include "misc/Timer.hpp"
#include "config.h"

INITIALIZE_EASYLOGGINGPP

//simple entity meshing tests
bool testMeshingEdges();
bool testMeshingWire();
bool testMeshingFace();
bool testMeshingFaces();
bool testMeshingSphereFaces();
bool testMeshingBox();

//compounds and step files
bool testMeshingCompound();
bool testMeshingStep1();
bool testMeshingStep2();
bool testMeshingStep3();

// boundary layers
bool test2DBoundaryLayers1();
bool test3DBoundaryLayers1();
bool test3DBoundaryLayers2();
bool test3DBoundaryLayers3();

//adaptive meshing (currently off)
bool testAdaptiveMeshing();
bool testRemeshingFace();

bool assertMeshSize(std::shared_ptr<Mesh> mesh, int nnodes, int nelems);

//geometry creation functions
TopoDS_Shape createPatch();
TopoDS_Shape createBox(double width = 32, double length = 55, double height = 12);
TopoDS_Shape createSphere(double radius = 15);

int main(int argc, char* argv[])
{
	bool res = true;

	std::string testdir = TEST_DIR;
	std::string assets_dir = testdir + "/meshGenerator/tests/assets";
	std::string wdir = testdir + "/meshGenerator/tests/current";

	std::error_code ec;
	if (std::filesystem::exists(wdir, ec))
	{
		if (!std::filesystem::remove_all(wdir, ec))
		{
			std::cout << "could not delete directory " << ec.value() << std::endl;
			return 1;
		}

	}

	if (!std::filesystem::create_directories(wdir, ec))
	{
		std::cout << "could not create directory " << ec.value() << std::endl;
		return 1;
	}
	std::filesystem::copy(assets_dir, wdir, std::filesystem::copy_options::recursive);

	try
	{
		Timer t;
		t.setMessage("Testing meshGeneratorTest.");

		//*********************************************
		//      simple entity meshing tests
		// 
		// TEST for meshing edges
		// geometry consists of several connected edges.
		t.start("testMeshingEdges");
		res &= testMeshingEdges();
		t.stop();



		// TEST MESHING WIRE
		// geometry consists of wire (from patch)
		t.start("testMeshingWire");
		res &= testMeshingWire();
		t.stop();


		// TEST MESHING FACE
		// geometry is patch from patch antenna
		t.start("testMeshingFace");
		res &= testMeshingFace();
		t.stop();


		// TEST MESHING FACES
		// geometry is multiple connected faces (all faces of box)
		t.start("testMeshingFaces");
		res &= testMeshingFaces();
		t.stop();

		// TEST MESHING SPHERE FACES
		// geometry is multiple a face of the sphere
		t.start("testMeshingSphereFaces");
		res &= testMeshingSphereFaces();
		t.stop();

		// TEST MESHING SOLID
		// geometry is simple box
		t.start("testMeshingBox");
		res &= testMeshingBox();
		t.stop();
		//
		//*********************************************


		//*********************************************
		//			compounds and step files


		// TEST MESHING COMPOUND
		// geometry is partition of box and sphere
		t.start("testMeshingCompound");
		res &= testMeshingCompound();
		t.stop();

		// TEST MESHING STEP
		t.start("testMeshingStep");
		res &= testMeshingStep1();
		t.stop();

		t.start("testMeshingStep2");
		res &= testMeshingStep2();
		t.stop();

		t.start("testMeshingStep3");
		res &= testMeshingStep3();
		t.stop();

		//
		//*********************************************

		//*********************************************
		// boundary layers
		t.start("test2DBoundaryLayers1");
		res &= test2DBoundaryLayers1();
		t.stop();
		/*
		t.start("test3DBoundaryLayers1");
		res &=test3DBoundaryLayers1();
		t.stop();

		t.start("test3DBoundaryLayers2");
		res &=test3DBoundaryLayers2();
		t.stop();


		t.start("test3DBoundaryLayers3");
		res &=test3DBoundaryLayers3();
		t.stop();  */

		
		//adaptive meshing (currently off)
/*		
		t.start("testAdaptiveMeshing");
		res &=testAdaptiveMeshing();
		t.stop();

		t.start("testRemeshingFace");
		res &=testRemeshingFace();
		t.stop();
  */

	}
	catch (std::exception &e)
	{
		std::cout << "ERROR: exception caught  while meshing" << e.what() << std::endl;
		return 1;
	}

	return res ? 0 : 1;
}

bool testMeshingEdges()
{
	std::cout << "testMeshingEdges()" << std::endl;

	gp_Pnt p1 = gp_Pnt(0, 0, 0);
	gp_Pnt p2 = gp_Pnt(0, 0, 5);
	gp_Pnt p3 = gp_Pnt(0, 3, 5);

	TopoDS_Edge edge1 = BRepBuilderAPI_MakeEdge(p1, p2).Edge();
	TopoDS_Edge edge2 = BRepBuilderAPI_MakeEdge(p2, p3).Edge();
	TopoDS_Shape edge1_shape = TopoDS_Shape(edge1);
	TopoDS_Shape edge2_shape = TopoDS_Shape(edge2);

	Handle(Geom_TrimmedCurve) arc = GC_MakeArcOfCircle(p1, p2, p3);
	TopoDS_Edge arc_edge = BRepBuilderAPI_MakeEdge(arc);
	TopoDS_Shape arc_edge_shape = TopoDS_Shape(arc_edge);

	ShapePartition part;
	part.addShape(edge1_shape);
	part.addShape(edge2_shape);
	part.addShape(arc_edge_shape);
	TopoDS_Shape part_shape = part.getPartition();

	MeshGenerator mg(part_shape);

	int i = 1;
	for (TopExp_Explorer explorer(part_shape, TopAbs_ShapeEnum::TopAbs_EDGE); explorer.More(); explorer.Next())
	{
		std::shared_ptr<TopoEntity> te = std::make_shared<TopoEntity>(explorer.Current(), "Edge_" + std::to_string(i));
		if (mg.addTopoEntity(te))
			i++;
	}

	std::shared_ptr<Mesh> mesh = mg.generateMesh();
	mesh->writeMesh(std::string(TEST_DIR) + "/meshGenerator/tests/current/testmesh_edges.msh", std::string("GMSH"));
	std::cout << "testMeshingEdges() FINISHED" << std::endl;
	int NODES = 48, ELEMENTS = 49;
	return assertMeshSize(mesh, NODES, ELEMENTS);
}


bool testMeshingWire()
{
	std::cout << "testMeshingWire()" << std::endl;

	TopoDS_Shape patch = createPatch();
	TopExp_Explorer explorer(patch, TopAbs_ShapeEnum::TopAbs_WIRE);
	TopoDS_Shape shape = explorer.Current();

	MeshGenerator mg(shape);

	int i = 0;
	for (TopExp_Explorer explorer(patch, TopAbs_ShapeEnum::TopAbs_EDGE); explorer.More(); explorer.Next())
	{
		TopoDS_Shape curr_shape = explorer.Current();
		std::shared_ptr<TopoEntity> tf = std::make_shared<TopoEntity>(TopoDS_Shape(curr_shape), "Edge_" + std::to_string(i));
		mg.addTopoEntity(tf);
		i++;

	}

	std::shared_ptr<Mesh> mesh = mg.generateMesh();


	mesh->writeMesh(std::string(TEST_DIR) + "/meshGenerator/tests/current/testmesh_wire.msh", std::string("GMSH"));

	std::cout << "testMeshingWire() FINISHED" << std::endl;
	int NODES = 71, ELEMENTS = 71;
	return assertMeshSize(mesh, NODES, ELEMENTS);
}



bool testMeshingFace()
{
	std::cout << "testMeshingFace()" << std::endl;

	TopoDS_Shape patch = createPatch();
	MeshGenerator mg(patch);

	int i = 1;
	std::shared_ptr<TopoEntity> tf = std::make_shared<TopoEntity>(patch, "Face_1");
	mg.addTopoEntity(tf);


	for (TopExp_Explorer explorer(patch, TopAbs_ShapeEnum::TopAbs_EDGE); explorer.More(); explorer.Next())
	{
		std::shared_ptr<TopoEntity> te = std::make_shared<TopoEntity>(explorer.Current(), "Edge_" + std::to_string(i));
		if (mg.addTopoEntity(te))
			i++;
	}

	std::shared_ptr<Mesh> mesh = mg.generateMesh();

	mesh->writeMesh(std::string(TEST_DIR) + "/meshGenerator/tests/current/testmesh_face.msh", std::string("GMSH"));

	std::cout << "testMeshingFace() FINISHED" << std::endl;
	int NODES = 216, ELEMENTS = 430;
	return assertMeshSize(mesh, NODES, ELEMENTS);
}


bool testMeshingFaces()
{
	std::cout << "testMeshingFaces()" << std::endl;

	TopoDS_Shape box = createBox();
	ShapePartition part;

	for (TopExp_Explorer explorer(box, TopAbs_ShapeEnum::TopAbs_FACE); explorer.More(); explorer.Next())
	{
		TopoDS_Shape shape = explorer.Current();
		part.addShape(shape);
	}

	TopoDS_Shape faces = part.getPartition();
	MeshGenerator mg(faces);

	int i = 1;
	for (TopExp_Explorer explorer(box, TopAbs_ShapeEnum::TopAbs_FACE); explorer.More(); explorer.Next())
	{
		std::shared_ptr<TopoEntity> tf = std::make_shared<TopoEntity>(explorer.Current(), "Face_" + std::to_string(i));
		if (mg.addTopoEntity(tf))
			i++;
	}

	i = 1;
	for (TopExp_Explorer explorer(box, TopAbs_ShapeEnum::TopAbs_EDGE); explorer.More(); explorer.Next())
	{
		std::shared_ptr<TopoEntity> te = std::make_shared<TopoEntity>(explorer.Current(), "Edge_" + std::to_string(i));
		if (mg.addTopoEntity(te))
			i++;
	}

	std::shared_ptr<Mesh> mesh = mg.generateMesh();

	mesh->writeMesh(std::string(TEST_DIR) + "/meshGenerator/tests/current/testmesh_faces.msh", std::string("GMSH"));

	std::cout << "testMeshingFaces() FINISHED" << std::endl;
	int NODES = 604, ELEMENTS = 1328;
	return assertMeshSize(mesh, NODES, ELEMENTS);
}



bool testMeshingSphereFaces()
{
	std::cout << "testMeshingSphereFaces()" << std::endl;

	TopoDS_Shape sphere = createSphere();
	ShapePartition part;

	for (TopExp_Explorer sexplorer(sphere, TopAbs_ShapeEnum::TopAbs_SHELL); sexplorer.More(); sexplorer.Next())
	{
		for (TopExp_Explorer fexplorer(sexplorer.Current(), TopAbs_ShapeEnum::TopAbs_FACE); fexplorer.More(); fexplorer.Next())
		{
			TopoDS_Shape shape = fexplorer.Current();
			part.addShape(shape);
		}
	}

	for (TopExp_Explorer explorer(sphere, TopAbs_ShapeEnum::TopAbs_EDGE); explorer.More(); explorer.Next())
	{
		TopoDS_Shape shape = explorer.Current();
		part.addShape(shape);
	}

	TopoDS_Shape faces = part.getPartition();

	MeshGenerator mg(faces);

	int i = 1;
	for (TopExp_Explorer explorer(sphere, TopAbs_ShapeEnum::TopAbs_FACE); explorer.More(); explorer.Next())
	{
		std::shared_ptr<TopoEntity> tf = std::make_shared<TopoEntity>(explorer.Current(), "Face_" + std::to_string(i));
		if (mg.addTopoEntity(tf))
			i++;
	}

	i = 1;
	for (TopExp_Explorer explorer(sphere, TopAbs_ShapeEnum::TopAbs_EDGE); explorer.More(); explorer.Next())
	{
		std::shared_ptr<TopoEntity> te = std::make_shared<TopoEntity>(explorer.Current(), "Edge_" + std::to_string(i));
		if (mg.addTopoEntity(te))
			i++;
	}

	std::shared_ptr<Mesh> mesh = mg.generateMesh();

	mesh->writeMesh(std::string(TEST_DIR) + "/meshGenerator/tests/current/testmesh_sphere.msh", std::string("GMSH"));

	std::cout << "testMeshingSphereFaces() FINISHED" << std::endl;
	int NODES = 477, ELEMENTS = 968;
	return assertMeshSize(mesh, NODES, ELEMENTS);
}

bool testMeshingBox()
{
	std::cout << "testMeshingBox()" << std::endl;

	TopoDS_Shape box = createBox();

	MeshGenerator mg(box);

	int i = 1;
	std::shared_ptr<TopoEntity> tf = std::make_shared<TopoEntity>(box, "someBox");
	mg.addTopoEntity(tf);


	for (TopExp_Explorer explorer(box, TopAbs_ShapeEnum::TopAbs_EDGE); explorer.More(); explorer.Next())
	{
		std::shared_ptr<TopoEntity> te = std::make_shared<TopoEntity>(explorer.Current(), "Edge_" + std::to_string(i));
		if (mg.addTopoEntity(te))
			i++;
	}

	i = 1;
	for (TopExp_Explorer explorer(box, TopAbs_ShapeEnum::TopAbs_FACE); explorer.More(); explorer.Next())
	{
		std::shared_ptr<TopoEntity> te = std::make_shared<TopoEntity>(explorer.Current(), "FaceXX_" + std::to_string(i));
		if (mg.addTopoEntity(te))
			i++;
	}

	std::shared_ptr<MeshParameters> mp = std::make_shared<MeshParameters>();
	mp->setMaxH(7);
	mp->setMinH(2);
	mg.setParameters(mp);
	std::shared_ptr<Mesh> mesh = mg.generateMesh();

	mesh->writeMesh(std::string(TEST_DIR) + "/meshGenerator/tests/current/testmesh_box.msh", std::string("GMSH"));
	std::cout << "testMeshingBox() FINISHED" << std::endl;
	int NODES = 132, ELEMENTS = 627;
	return assertMeshSize(mesh, NODES, ELEMENTS);
}


bool testMeshingCompound()
{
	std::cout << "testMeshingCompound()" << std::endl;

	TopoDS_Shape box = createBox();
	TopoDS_Shape box2 = createBox(60, 27, 27);
	ShapePartition part;
	part.addShape(box);
	part.addShape(box2);
	TopoDS_Shape partition = part.getPartition();

	MeshGenerator mg(partition);

	std::shared_ptr<TopoEntity> face1; //will be used for refinement later

	int i = 1;
	for (TopExp_Explorer explorer(partition, TopAbs_ShapeEnum::TopAbs_SOLID); explorer.More(); explorer.Next())
	{
		std::shared_ptr<TopoEntity> ts = std::make_shared<TopoEntity>(explorer.Current(), "Solid_" + std::to_string(i));
		if (mg.addTopoEntity(ts))
			i++;
	}

	i = 1;
	for (TopExp_Explorer explorer(partition, TopAbs_ShapeEnum::TopAbs_EDGE); explorer.More(); explorer.Next())
	{
		std::shared_ptr<TopoEntity> te = std::make_shared<TopoEntity>(explorer.Current(), "Edge_" + std::to_string(i));
		if (mg.addTopoEntity(te))
			i++;
	}

	i = 1;
	for (TopExp_Explorer explorer(partition, TopAbs_ShapeEnum::TopAbs_FACE); explorer.More(); explorer.Next())
	{
		std::shared_ptr<TopoEntity> te = std::make_shared<TopoEntity>(explorer.Current(), "FaceXX_" + std::to_string(i));
		if (i == 1)
			face1 = te;
		if (mg.addTopoEntity(te))
			i++;
	}

	std::shared_ptr<MeshParameters> mp = std::make_shared<MeshParameters>(partition);
	mp->setDensityFactor(1.2);
	mg.setParameters(mp);

	//refinement on one solid
	std::shared_ptr<MeshParameters> mp1 = std::make_shared<MeshParameters>(partition);
	mp1->setDensityFactor(0.2);
	mg.setParameters(mp1, face1);

	std::shared_ptr<Mesh> mesh = mg.generateMesh();

	mesh->writeMesh(std::string(TEST_DIR) + "/meshGenerator/tests/current/testmesh_compound.msh", std::string("GMSH"));

	std::cout << "testMeshingCompound() FINISHED" << std::endl;
	int NODES = 1393, ELEMENTS = 7789;
	return assertMeshSize(mesh, NODES, ELEMENTS);
}


bool testMeshingStep1()
{
	std::cout << "testMeshingStep()" << std::endl;

	TopoDS_Shape shape;
	STEPControl_Reader reader;
	reader.ReadFile((std::string(TEST_DIR) + "/meshGenerator/tests/current/workpiece1.stp").data());
	reader.TransferRoots();
	shape = reader.OneShape();
	BRepTools::Clean(shape);

	MeshGenerator mg(shape);

	int i = 1;
	for (TopExp_Explorer explorer(shape, TopAbs_ShapeEnum::TopAbs_SOLID); explorer.More(); explorer.Next())
	{
		std::shared_ptr<TopoEntity> ts = std::make_shared<TopoEntity>(explorer.Current(), "Solid_" + std::to_string(i));
		if (mg.addTopoEntity(ts))
			i++;
	}

	i = 1;
	for (TopExp_Explorer explorer(shape, TopAbs_ShapeEnum::TopAbs_EDGE); explorer.More(); explorer.Next())
	{
		std::shared_ptr<TopoEntity> te = std::make_shared<TopoEntity>(explorer.Current(), "Edge_" + std::to_string(i));
		if (mg.addTopoEntity(te))
			i++;
	}

	i = 1;
	for (TopExp_Explorer explorer(shape, TopAbs_ShapeEnum::TopAbs_FACE); explorer.More(); explorer.Next())
	{
		std::shared_ptr<TopoEntity> te = std::make_shared<TopoEntity>(explorer.Current(), "Face_" + std::to_string(i));
		if (mg.addTopoEntity(te))
			i++;
	}

	std::shared_ptr<MeshParameters> mp = std::make_shared<MeshParameters>();
	mp->setMaxH(3);
	mp->setMinH(0.5);
	mg.setParameters(mp);
	std::shared_ptr<Mesh> mesh = mg.generateMesh();

	mesh->writeMesh(std::string(TEST_DIR) + "/meshGenerator/tests/current/testmesh_step1.msh", std::string("GMSH"));

	std::cout << "testMeshingStep() FINISHED" << std::endl;
	int NODES = 12142, ELEMENTS = 67689;
	return assertMeshSize(mesh, NODES, ELEMENTS);
}



bool testMeshingStep2()
{
	std::cout << "testMeshingStep2()" << std::endl;

	TopoDS_Shape shape;
	STEPControl_Reader reader;
	reader.ReadFile((std::string(TEST_DIR) + "/meshGenerator/tests/current/workpiece2.stp").data());
	reader.TransferRoots();
	shape = reader.OneShape();
	BRepTools::Clean(shape);

	MeshGenerator mg(shape);

	int i = 1;
	for (TopExp_Explorer explorer(shape, TopAbs_ShapeEnum::TopAbs_SOLID); explorer.More(); explorer.Next())
	{
		std::shared_ptr<TopoEntity> ts = std::make_shared<TopoEntity>(explorer.Current(), "Solid_" + std::to_string(i));
		if (mg.addTopoEntity(ts))
			i++;
	}

	i = 1;
	for (TopExp_Explorer explorer(shape, TopAbs_ShapeEnum::TopAbs_EDGE); explorer.More(); explorer.Next())
	{
		std::shared_ptr<TopoEntity> te = std::make_shared<TopoEntity>(explorer.Current(), "Edge_" + std::to_string(i));
		if (mg.addTopoEntity(te))
			i++;
	}

	i = 1;
	for (TopExp_Explorer explorer(shape, TopAbs_ShapeEnum::TopAbs_FACE); explorer.More(); explorer.Next())
	{
		std::shared_ptr<TopoEntity> te = std::make_shared<TopoEntity>(explorer.Current(), "Face_" + std::to_string(i));
		if (mg.addTopoEntity(te))
			i++;
	}

	std::shared_ptr<MeshParameters> mp = std::make_shared<MeshParameters>();
	mp->setMaxH(10);
	mp->setMinH(0.1);
	mg.setParameters(mp);
	std::shared_ptr<Mesh> mesh = mg.generateMesh();

	mesh->writeMesh(std::string(TEST_DIR) + "/meshGenerator/tests/current/testmesh_step2.msh", std::string("GMSH"));

	std::cout << "testMeshingStep2() FINISHED" << std::endl;
	int NODES = 2431, ELEMENTS = 12844;
	return assertMeshSize(mesh, NODES, ELEMENTS);
}



bool testMeshingStep3()
{
	std::cout << "testMeshingStep3()" << std::endl;

	TopoDS_Shape shape;
	STEPControl_Reader reader;
	reader.ReadFile((std::string(TEST_DIR) + "/meshGenerator/tests/current/inductor1.step").data());
	reader.TransferRoots();
	shape = reader.OneShape();
	BRepTools::Clean(shape);

	MeshGenerator mg(shape);

	int i = 1;
	for (TopExp_Explorer explorer(shape, TopAbs_ShapeEnum::TopAbs_SOLID); explorer.More(); explorer.Next())
	{
		std::shared_ptr<TopoEntity> ts = std::make_shared<TopoEntity>(explorer.Current(), "Solid_" + std::to_string(i));
		if (mg.addTopoEntity(ts))
			i++;
	}

	i = 1;
	for (TopExp_Explorer explorer(shape, TopAbs_ShapeEnum::TopAbs_EDGE); explorer.More(); explorer.Next())
	{
		std::shared_ptr<TopoEntity> te = std::make_shared<TopoEntity>(explorer.Current(), "Edge_" + std::to_string(i));
		if (mg.addTopoEntity(te))
			i++;
	}

	i = 1;
	for (TopExp_Explorer explorer(shape, TopAbs_ShapeEnum::TopAbs_FACE); explorer.More(); explorer.Next())
	{
		std::shared_ptr<TopoEntity> te = std::make_shared<TopoEntity>(explorer.Current(), "Face_" + std::to_string(i));
		if (mg.addTopoEntity(te))
			i++;
	}

	std::shared_ptr<MeshParameters> mp = std::make_shared<MeshParameters>();
	mp->setMaxH(3);
	mp->setMinH(0.5);
	mg.setParameters(mp);
	std::shared_ptr<Mesh> mesh = mg.generateMesh();

	mesh->writeMesh(std::string(TEST_DIR) + "/meshGenerator/tests/current/testmesh_step3.msh", std::string("GMSH"));

	std::cout << "testMeshingStep3() FINISHED" << std::endl;
	int NODES = 19846, ELEMENTS = 98028;
	return assertMeshSize(mesh, NODES, ELEMENTS);
}


bool testMeshingStep4()
{
	std::cout << "testMeshingStep4()" << std::endl;

	TopoDS_Shape shape;
	STEPControl_Reader reader;
	reader.ReadFile((std::string(TEST_DIR) + "/meshGenerator/tests/current/inductor2.step").data());
	reader.TransferRoots();
	shape = reader.OneShape();
	BRepTools::Clean(shape);

	MeshGenerator mg(shape);

	int i = 1;
	for (TopExp_Explorer explorer(shape, TopAbs_ShapeEnum::TopAbs_SOLID); explorer.More(); explorer.Next())
	{
		std::shared_ptr<TopoEntity> ts = std::make_shared<TopoEntity>(explorer.Current(), "Solid_" + std::to_string(i));
		if (mg.addTopoEntity(ts))
			i++;
	}

	i = 1;
	for (TopExp_Explorer explorer(shape, TopAbs_ShapeEnum::TopAbs_EDGE); explorer.More(); explorer.Next())
	{
		std::shared_ptr<TopoEntity> te = std::make_shared<TopoEntity>(explorer.Current(), "Edge_" + std::to_string(i));
		if (mg.addTopoEntity(te))
			i++;
	}

	i = 1;
	for (TopExp_Explorer explorer(shape, TopAbs_ShapeEnum::TopAbs_FACE); explorer.More(); explorer.Next())
	{
		std::shared_ptr<TopoEntity> te = std::make_shared<TopoEntity>(explorer.Current(), "Face_" + std::to_string(i));
		if (mg.addTopoEntity(te))
			i++;
	}

	std::shared_ptr<MeshParameters> mp = std::make_shared<MeshParameters>();
	mp->setMaxH(3);
	mp->setMinH(0.5);
	mg.setParameters(mp);
	std::shared_ptr<Mesh> mesh = mg.generateMesh();

	mesh->writeMesh(std::string(TEST_DIR) + "/meshGenerator/tests/current/testmesh_step4.msh", std::string("GMSH"));

	std::cout << "testMeshingStep4() FINISHED" << std::endl;
	//fix this test and add assert
	return false;
}


//
//
// tests of boundary layer meshing
//--------------------



bool test2DBoundaryLayers1()
{
	std::cout << "test2DBoundaryLayers1()" << std::endl;

	TopoDS_Shape patch = createPatch();
	MeshGenerator mg(patch);

	int i = 1;
	std::shared_ptr<TopoEntity> tf = std::make_shared<TopoEntity>(patch, "Face_1");
	mg.addTopoEntity(tf);


	for (TopExp_Explorer explorer(patch, TopAbs_ShapeEnum::TopAbs_EDGE); explorer.More(); explorer.Next())
	{
		std::shared_ptr<TopoEntity> te = std::make_shared<TopoEntity>(explorer.Current(), "Edge_" + std::to_string(i));
		if (mg.addTopoEntity(te))
			i++;
	}

	std::shared_ptr<MeshParameters> mp = std::make_shared<MeshParameters>();
	mp->setMaxH(5);
	mp->setMinH(1);
	mp->calculateLayers(20, 20);
	mg.setParameters(mp);

	std::shared_ptr<Mesh> mesh = mg.generateMesh();

	mesh->writeMesh(std::string(TEST_DIR) + "/meshGenerator/tests/current/testmesh_2dblayers1.msh", std::string("GMSH"));

	std::cout << "test2DBoundaryLayers1() FINISHED" << std::endl;
	int NODES = 377, ELEMENTS = 430;
	return assertMeshSize(mesh, NODES, ELEMENTS);
}


bool test3DBoundaryLayers1()
{
	std::cout << "test3DBoundaryLayers1()" << std::endl;

	TopoDS_Shape box = createBox(1, 1, 0.05);

	MeshGenerator mg(box);

	int i = 1;
	std::shared_ptr<TopoEntity> tf = std::make_shared<TopoEntity>(box, "someBox");
	mg.addTopoEntity(tf);


	for (TopExp_Explorer explorer(box, TopAbs_ShapeEnum::TopAbs_EDGE); explorer.More(); explorer.Next())
	{
		std::shared_ptr<TopoEntity> te = std::make_shared<TopoEntity>(explorer.Current(), "Edge_" + std::to_string(i));
		if (mg.addTopoEntity(te))
			i++;
	}

	i = 1;
	for (TopExp_Explorer explorer(box, TopAbs_ShapeEnum::TopAbs_FACE); explorer.More(); explorer.Next())
	{
		std::shared_ptr<TopoEntity> te = std::make_shared<TopoEntity>(explorer.Current(), "Face_" + std::to_string(i));
		if (mg.addTopoEntity(te))
			i++;
	}

	std::shared_ptr<MeshParameters> mp = std::make_shared<MeshParameters>();
	mp->setMaxH(0.1);
	mp->setMinH(0.04);
	mp->calculateLayers(0.5, 10);
	mg.setParameters(mp);

	std::shared_ptr<Mesh> mesh = mg.generateMesh();
	mesh->writeMesh(std::string(TEST_DIR) + "/meshGenerator/tests/current/testmesh_3dblayers1.msh", std::string("GMSH"));
	//fix this test and add assert
	return false;
}


bool test3DBoundaryLayers2()
{
	std::cout << "test3DBoundaryLayers2()" << std::endl;
	TopoDS_Shape shape;
	STEPControl_Reader reader;
	reader.ReadFile((std::string(TEST_DIR) + "/meshGenerator/tests/current/workpiece1.stp").data());
	reader.TransferRoots();
	shape = reader.OneShape();
	BRepTools::Clean(shape);

	MeshGenerator mg(shape);

	int i = 1;
	for (TopExp_Explorer explorer(shape, TopAbs_ShapeEnum::TopAbs_SOLID); explorer.More(); explorer.Next())
	{
		std::shared_ptr<TopoEntity> ts = std::make_shared<TopoEntity>(explorer.Current(), "Solid_" + std::to_string(i));
		if (mg.addTopoEntity(ts))
			i++;
	}

	i = 1;
	for (TopExp_Explorer explorer(shape, TopAbs_ShapeEnum::TopAbs_EDGE); explorer.More(); explorer.Next())
	{
		std::shared_ptr<TopoEntity> te = std::make_shared<TopoEntity>(explorer.Current(), "Edge_" + std::to_string(i));
		if (mg.addTopoEntity(te))
			i++;
	}

	i = 1;
	for (TopExp_Explorer explorer(shape, TopAbs_ShapeEnum::TopAbs_FACE); explorer.More(); explorer.Next())
	{
		std::shared_ptr<TopoEntity> te = std::make_shared<TopoEntity>(explorer.Current(), "Face_" + std::to_string(i));
		if (mg.addTopoEntity(te))
			i++;
	}



	std::shared_ptr<MeshParameters> mp = std::make_shared<MeshParameters>();
	mp->setMaxH(2);
	mp->setMinH(0.5);
	mp->calculateLayers(1, 12);

	mg.setParameters(mp);
	std::shared_ptr<Mesh> mesh = mg.generateMesh();

	mesh->writeMesh(std::string(TEST_DIR) + "/meshGenerator/tests/current/testmesh_3dblayers2.msh", std::string("GMSH"));

	std::cout << "test3DBoundaryLayers2() FINISHED" << std::endl;
	//fix this test and add assert
	return false;
}




bool test3DBoundaryLayers3()
{
	std::cout << "test3DBoundaryLayers3()" << std::endl;
	TopoDS_Shape shape;
	STEPControl_Reader reader;
	reader.ReadFile((std::string(TEST_DIR) + "/meshGenerator/tests/current/inductor3.step").data());
	reader.TransferRoots();
	shape = reader.OneShape();
	BRepTools::Clean(shape);

	MeshGenerator mg(shape);

	int i = 1;
	for (TopExp_Explorer explorer(shape, TopAbs_ShapeEnum::TopAbs_SOLID); explorer.More(); explorer.Next())
	{
		std::shared_ptr<TopoEntity> ts = std::make_shared<TopoEntity>(explorer.Current(), "Solid_" + std::to_string(i));
		if (mg.addTopoEntity(ts))
			i++;
	}

	i = 1;
	for (TopExp_Explorer explorer(shape, TopAbs_ShapeEnum::TopAbs_EDGE); explorer.More(); explorer.Next())
	{
		std::shared_ptr<TopoEntity> te = std::make_shared<TopoEntity>(explorer.Current(), "Edge_" + std::to_string(i));
		if (mg.addTopoEntity(te))
			i++;
	}

	i = 1;
	for (TopExp_Explorer explorer(shape, TopAbs_ShapeEnum::TopAbs_FACE); explorer.More(); explorer.Next())
	{
		std::shared_ptr<TopoEntity> te = std::make_shared<TopoEntity>(explorer.Current(), "Face_" + std::to_string(i));
		if (mg.addTopoEntity(te))
			i++;
	}



	std::shared_ptr<MeshParameters> mp = std::make_shared<MeshParameters>();
	mp->setMaxH(1500);
	mp->setMinH(500);
	mp->calculateLayers(30, 200);

	mg.setParameters(mp);
	std::shared_ptr<Mesh> mesh = mg.generateMesh();

	mesh->writeMesh(std::string(TEST_DIR) + "/meshGenerator/tests/current/testmesh_3dblayers3.msh", std::string("GMSH"));

	std::cout << "test3DBoundaryLayers3() FINISHED" << std::endl;
	//fix this test and add assert
	return false;
}


//
//
// tests of adaptive meshing
//--------------------
bool testAdaptiveMeshing()
{
	std::cout << "testAdaptiveMeshing()" << std::endl;
	std::shared_ptr<Mesh> initialmesh;

	initialmesh->readMesh(std::string(TEST_DIR) + "/meshGenerator/tests/current/testmesh_box.msh", std::string("GMSH"));
	std::cout << "adaptive 1 " << std::endl;

	TopoDS_Shape box = createSphere(); // createBox();

	MeshGenerator mg(box);
	std::cout << "adaptive 2 " << std::endl;

	int i = 1;
	std::shared_ptr<TopoEntity> tf = std::make_shared<TopoEntity>(box, "someBox");
	mg.addTopoEntity(tf);
	std::cout << "adaptive 3 " << std::endl;


	for (TopExp_Explorer explorer(box, TopAbs_ShapeEnum::TopAbs_EDGE); explorer.More(); explorer.Next())
	{
		std::shared_ptr<TopoEntity> te = std::make_shared<TopoEntity>(explorer.Current(), "Edge_" + std::to_string(i));
		if (mg.addTopoEntity(te))
			i++;
	}

	i = 1;
	for (TopExp_Explorer explorer(box, TopAbs_ShapeEnum::TopAbs_FACE); explorer.More(); explorer.Next())
	{
		std::shared_ptr<TopoEntity> te = std::make_shared<TopoEntity>(explorer.Current(), "FaceXX_" + std::to_string(i));
		if (mg.addTopoEntity(te))
			i++;
	}


	//marking elements
	for (auto el : initialmesh->getElements())
	{
		if (el->getCentroid()[0] > 10)
			el->setMark(1);
		else
			el->setMark(0);
	}

	mg.setMesh(initialmesh);
	std::shared_ptr<Mesh> mesh = mg.refineMesh();
	mesh->writeMesh(std::string(TEST_DIR) + "/meshGenerator/tests/current/testmesh_adaptive.msh", std::string("GMSH"));
	//fix this test and add assert
	return false;
}

//
//
// tests of remeshing
//--------------------


bool testRemeshingFace()
{
	std::cout << "testRemeshingFace()" << std::endl;

	std::shared_ptr<Mesh> mesh;

	mesh->readMesh(std::string(TEST_DIR) + "/meshGenerator/tests/current/testmesh_face.msh", std::string("GMSH"));
	std::cout << " genmesh in procmesh" << std::endl;


	TopoDS_Shape patch = createPatch();
	MeshGenerator mg(patch);
	std::cout << " genmesh in  g" << std::endl;

	int i = 1;
	std::shared_ptr<TopoEntity> tf = std::make_shared<TopoEntity>(patch, "Face_1");
	mg.addTopoEntity(tf);

	for (TopExp_Explorer explorer(patch, TopAbs_ShapeEnum::TopAbs_EDGE); explorer.More(); explorer.Next())
	{
		std::shared_ptr<TopoEntity> te = std::make_shared<TopoEntity>(explorer.Current(), "Edge_" + std::to_string(i));
		if (mg.addTopoEntity(te))
		{
			i++;
		}
	}
	std::cout << " genmesh in tesrremesh" << std::endl;
	std::shared_ptr<Mesh> mesh2 = mg.generateMesh();

	mesh2->writeMesh(std::string(TEST_DIR) + "/meshGenerator/tests/current/testmesh_remeshface.msh", std::string("GMSH"));
	std::cout << "testRemeshingFace() FINISHED" << std::endl;
	//fix this test and add assert
	return false;

}

//
//
// Geometry creation functions
//----------------------------------------------------


TopoDS_Shape createPatch()
{
	double patch_width = 50;
	double patch_length = 30;
	double inset_width = 5;
	double feed_width = 7;
	double inset_lenght = 12;
	gp_Pnt aPnt[12] = { gp_Pnt(patch_width / 2.0, patch_length / 2.0, 0),
					gp_Pnt(-patch_width / 2.0, patch_length / 2.0, 0),
					gp_Pnt(-patch_width / 2.0, -patch_length / 2.0, 0),
					gp_Pnt(-inset_width - feed_width / 2.0, -patch_length / 2.0, 0),
					gp_Pnt(-inset_width - feed_width / 2.0, -patch_length / 2.0 + inset_lenght, 0),
					gp_Pnt(-feed_width / 2.0, -patch_length / 2.0 + inset_lenght, 0),
					gp_Pnt(-feed_width / 2.0, -patch_length / 2.0, 0),
					gp_Pnt(feed_width / 2.0, -patch_length / 2.0, 0),
					gp_Pnt(feed_width / 2.0, -patch_length / 2.0 + inset_lenght, 0),
					gp_Pnt(inset_width + feed_width / 2.0, -patch_length / 2.0 + inset_lenght, 0),
					gp_Pnt(inset_width + feed_width / 2.0, -patch_length / 2.0, 0),
					gp_Pnt(patch_width / 2.0, -patch_length / 2.0, 0) };

	BRepBuilderAPI_MakeWire aWire;
	for (int i = 0; i < 12; i++)
	{
		Handle(Geom_TrimmedCurve) aSegment = GC_MakeSegment(aPnt[i], aPnt[i == 11 ? 0 : i + 1]);
		TopoDS_Edge anEdge = BRepBuilderAPI_MakeEdge(aSegment);
		aWire.Add(anEdge);
	}

	return BRepBuilderAPI_MakeFace(aWire.Wire());
}

TopoDS_Shape createBox(double width, double length, double height)
{

	gp_Pnt boxPnt1(-width / 2.0, -length / 2.0, 0);
	gp_Pnt boxPnt2(width / 2.0, length / 2.0, height);
	BRepPrimAPI_MakeBox substrate_box(boxPnt1, boxPnt2);
	substrate_box.Build();
	TopoDS_Shape box = substrate_box.Shape();
	return box;
}

TopoDS_Shape createSphere(double radius)
{
	BRepPrimAPI_MakeSphere sphere(gp_Pnt(0, 0, 0), 20);
	sphere.Build();
	TopoDS_Shape sphere_shape = sphere.Shape();

	return sphere_shape;
}


bool assertMeshSize(std::shared_ptr<Mesh> mesh, int nnodes, int nelems)
{
	int n = mesh->getSizeNodes();
	int e = mesh->getSizeElements();

	if (abs(n - nnodes) / n < 0.1)
		return true;

	if (abs(e - nelems) / n < 0.1)
		return true;

	return false;
}