/**
 *  \brief     Mesh abstract class.
 *  \details   Used to store all the mesh data and perform operations such as reordering, extraction of mesh faces, etc. A particular mesh class
 *  is needed to be used in application. An example is GmshMesh class, which implements ReadMesh() and WriteMesh() methods.
 *  \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
 */

#ifndef MESH_H_
#define MESH_H_
#include <WinSock2.h>
#include <Windows.h>
#include <fstream>
#include <iostream>
#include <optional>
#include <string>
#include <sstream>
#include <numeric>
#include <set>
#include <map>
#include "Node.hpp"
#include "MeshEntity.h"
#include "ElementTypes.hpp"
#include "nlohmann/json.hpp"
#include "misc/messagedata.hpp"
#include "easylogging++/easylogging++.h"
#include <optional>
#include "topology/geometryData.h"
#include "MeshStats.hpp"
#include "NetgenWrapper/NetgenWrapper.h"

using json = nlohmann::json;

class Mesh {
protected:

	std::vector<std::shared_ptr<Node>> nodes; ///< Nodes belonging to mesh
	std::vector<std::shared_ptr<Element>> elements; ///< Elements belonging to mesh
	std::vector<std::shared_ptr<MeshEntity>> entities; ///< Entities belonging to mesh

	std::string filename; ///< filename where this mesh is located

	Element::Dim meshDimension;

	/// a number defining a scale of mesh
	/// This is needed if mesh is created in milimeters
	/// then for simulation reasons it has to be rescaled to meters (SI units)
	double meshScale;


	std::string wDir; /// working directory

	/// <summary>
	/// return nodes connected to passed node
	/// TO DO Rewrite!!!
	/// </summary>
	std::set<std::shared_ptr<Node>> getConnectedNodes(std::shared_ptr<Node>, std::shared_ptr<Element>, std::vector<std::shared_ptr<Node>>);


public:
	Mesh();

	~Mesh();

	float version;
	void clear();

	// set methods
	void setMeshScale(double);
	void setWDir(std::string);
	void setFileName(std::string);

	void reserveElementsSize(int);
	void reserveNodesSize(int);
	void addNode(std::shared_ptr<Node>);
	void addEntity(std::shared_ptr<MeshEntity>);
	void addElement(std::shared_ptr<Element>);


	/// Get methods
	size_t getSizeNodes();
	size_t getSizeEntities();
	size_t getSizeElements();
	int getDimension();
	const std::vector<std::shared_ptr<Node> > getNodes();
	const std::shared_ptr<Node> getNode(int); /// get Node by index
	const std::vector<std::shared_ptr<Node>> getNodes(std::shared_ptr<MeshEntity> ); // get all nodes belonging to passed entity
	const std::vector<std::shared_ptr<Element> > getElements();
	double getMeshScale();
	std::string getFileName();
	std::vector<std::shared_ptr<MeshEntity>> getEntities();
	std::shared_ptr<MeshEntity> getEntityById(int id);
	std::shared_ptr<MeshEntity> getEntityByName(std::string n);
	MeshStats getMeshStats();

	// TO REMOVE THIS BLOCK OF FUNCTIONS
	double calculateArea(std::shared_ptr<MeshEntity>);
	std::vector<double> calculateCentroid(std::shared_ptr<MeshEntity> );
	double calculateVolume(std::shared_ptr<MeshEntity>);
	double calculateAxiVolume(std::shared_ptr<MeshEntity>);


	// Mesh read/write
	void readMesh(std::string& filename, std::string& format, char flags = '\0');
	void writeMesh(std::string& filename, std::string& format, char flags = '\0');

	//get netgen mesh ptr
	NetgenMesh_ getNetgenMesh();
	NetgenMesh_ getNetgenMesh(std::string ent_name, gp_Trsf trans = gp_Trsf());

	//add cohomology cut
	//TO DO Rewrite
	void addCut(std::shared_ptr<MeshEntity>);
};


#endif /* MESH_H_ */
