/**
 *  \brief     MeshGenerator class
 *  \details   Used for mesh generation. To create mesh, faces and solids have to be added, as well as mesh parameters have to be set.
 *  \author    vadims
 *  \date      May 10, 2017
 *  \bug       Not all memory is freed when deleting an object of this class.
 *  \warning   Improper use can crash your application
 *  \copyright CENOS LLC
 */


#pragma once

#include <TopoDS.hxx>
#include <gp_Trsf.hxx>
#include "Mesh.h"
#include "topology/TopoEntity.h"
#include "topology/geometryData.h"
#include "MeshingData.h"
#include "MeshParameters.h"
#include "misc/ProgressIndicator.hpp"

#include "NetgenWrapper/NetgenWrapper.h"


class MeshGenerator
{
public:
	~MeshGenerator();
	MeshGenerator(TopoDS_Shape);
	MeshGenerator(GeometryData_);
	MeshGenerator(const MeshGenerator& that);
	void setWdir(std::string);
	bool addTopoEntity(std::shared_ptr<TopoEntity>, bool force_add = false);
	void setProgressIndicator(std::shared_ptr<ProgressIndicator>);

	std::shared_ptr<Mesh> generateMesh();

	std::shared_ptr<Mesh> refineMesh();

	void setMesh(std::shared_ptr<Mesh>);

	/// <summary>
	/// Set global meshing parameters
	/// </summary>
	/// <param name="mp"> meshing parameters</param>
	void setParameters(std::shared_ptr<MeshParameters> mp);

	/// <summary>
	/// Meshing parameters applied for specified entity
	/// This function overwrites any MeshParameters applied to specific domains
	/// </summary>
	/// <param name="mp"> meshing parameters</param>
	/// <param name="ent"> Entity that should be meshed with specified parameters</param>
	void setParameters(std::shared_ptr<MeshParameters> mp, std::shared_ptr<TopoEntity> ent);

	/// <summary>
	/// Set the transform for entity. Entity ent will be moved before meshing
	/// </summary>
	/// <param name="ent"> entity to be moved</param>
	/// <param name="transform"> transformation applied to entity</param>
	void setTransform(std::shared_ptr<TopoEntity> ent, gp_Trsf transform);

	void setStopper(bool*);

private:
	std::string wDir;
	TopoDS_Shape partition;
	std::shared_ptr<Mesh> mesh;
	std::vector<std::shared_ptr<TopoEntity>> entities;
	std::shared_ptr < MeshParameters> mesh_parameters;
	std::shared_ptr<ProgressIndicator> progress;
	std::map<std::string, std::vector<std::string>> relations;
	enum Mode
	{
		M_1D,
		M_2D,
		M_3D
	};

	NetgenMesh_ main_ng_mesh;
	Mode mesh_mode;
	ShapeType mesh_shape_type;

	MeshingData meshing_data;
	std::map<int, int> node_copy_map;

	std::map<int, int> edge_id; /// assigns TopoEntity id to edge indices
	std::map<int, int> face_id; /// assigns TopoEntity id to face indices
	std::map<int, int> solid_id; /// assigns TopoEntity id to solid indices

	// pointer to a variable used to stop meshing
	bool* stopper;

	std::map<std::shared_ptr<TopoEntity>, gp_Trsf> entity_transform;

	std::shared_ptr<Mesh> copyMeshData(NetgenMesh_);

	NetgenMesh_ generate1DMesh(std::shared_ptr<TopoEntity> ent);
	NetgenMesh_ generate2DMesh(std::shared_ptr<TopoEntity> ent);
	NetgenMesh_ generate3DMesh(std::shared_ptr<TopoEntity> ent);

	void copyTransformedMeshData();

	void validateInput();

};


typedef std::shared_ptr<MeshGenerator> MeshGenerator_;