
#pragma once

#include <TopTools_IndexedMapOfShape.hxx>
#include "mesh/MeshParameters.h"
#include "mesh/ElementTypesEnum.hpp"


//forward declarations to prevent including netgen stuff in cenos code
namespace netgen
{
	class OCCGeometry;
	class Mesh;
	class LocalH;
	class MeshingParameters;
	class OCCParameters;
}

struct NetgenGeometryWrapper;


typedef netgen::OCCGeometry NetgenOCCShape;
typedef netgen::Mesh NgMesh;
typedef netgen::LocalH NetgenLocalH;

void InitializeNetgen();
void FinalizeNetgen();
void TransferSizing(MeshParameters mp);

struct NetgenMesh
{
	~NetgenMesh();
	std::shared_ptr < NgMesh> mesh;
	NetgenMesh();
	void ReverseSegments();
	void ReverseFaces();
	void SetGeometry(NetgenGeometryWrapper);

	double GetMaxH();

	void AddPoint(double*);
	void AddSegment(int pi1, int pi2, int edgeIndex);
	void AddSurfaceElement(ElementTypes et, int* pi, int surfIndx);
	void AddVolumeElement(ElementTypes et, int* pi, int volIndx);

	void AddFaceDescriptor(int faceId, int dominId, int domOutId, int TLOface);

	/// <summary>
	/// Merge mesh orig_mesh into this mesh
	/// </summary>
	void MergeMesh(std::shared_ptr<NetgenMesh> orig_mesh, int index = -1);
	void DeleteMesh();
	void SetElementRefinement(int el_index, bool flag);
	void Refine();

	void GetPoint(int , double* );
	void GetSegment(int index, int* point_indices);
	int GetNP();
	int GetNSeg();
	int GetNSE();
	int GetNE();


	int GetEdgeIndexOfSegment(int i);
	int GetFaceIndexOfSE(int i);
	int GetSolidIndexOfElement(int i);
	ElementTypes GetSurfaceElement(int i, int* pi);
	ElementTypes GetVolumeElement(int i, int* pi);


	void CalculateSurfaceOfNode();
	void PrepareSurfaceMeshing();


	void SetMeshSizing(NetgenGeometryWrapper*, MeshParameters_);
	std::shared_ptr<NetgenLocalH> GetLocalH();

	void RestrictSizeByLocalH(NetgenLocalH*);
	void RestrictSizeByMesh(std::shared_ptr<NetgenMesh>);
	void CopyLocalH(std::shared_ptr<NetgenMesh>);

	void SaveMesh(const char*);


	// Meshing commands

	// Mesh single edge - shape should consist of one edge only
	void DivideEdges(NetgenGeometryWrapper, MeshParameters );
	void GenerateEdgeMesh(NetgenGeometryWrapper ng_geom, MeshParameters mp);

	void GenerateSurfaceMesh(NetgenGeometryWrapper, MeshParameters);
	void GenerateBoundaryLayer2D(int dom_nr, double* heights_arr, int heights_count);

	void GenerateVolumeMesh(MeshParameters);
	void GenerateBoundaryLayers(int* surfid_arr, int surfid_count, double* heights_arr, int heights_count);

};

typedef std::shared_ptr<NetgenMesh> NetgenMesh_;

struct NetgenGeometryWrapper
{
	NetgenGeometryWrapper(TopoDS_Shape);
	NetgenGeometryWrapper(TopoDS_Shape*);
	std::shared_ptr<NetgenOCCShape> occ_shape;
	TopTools_IndexedMapOfShape GetSolidMap();
	TopTools_IndexedMapOfShape GetFaceMap();
	TopTools_IndexedMapOfShape GetEdgeMap();
};


