#ifndef CNGLIB
#define CNGLIB

#include <mystdlib.h>
namespace nglib {
#include <nglib.h>
}

#include <occgeom.hpp>

using netgen::OCCGeometry;
using namespace nglib;

// *** Data-types for accessing Netgen functionality ***
typedef void* Ng_LocalH;

/// Data type for NETGEN OpenCascade geometry
typedef void* Ng_OCC_Geometry;
typedef void* Ng_OCC_TopTools_IndexedMapOfShape;
typedef void* Ng_TopoDS_Shape;
typedef void Cenos_TopoDS_Shape;
OCCGeometry* CreateOCCGeometryFromTopoDS(Cenos_TopoDS_Shape* shape);

// Copy nodes and elements from origin_mesh into destination_mesh
void Cenos_MergeMesh(Ng_Mesh* orig_mesh, Ng_Mesh* dest_mesh, int index = 0);

// Copy segments from origin_mesh into destination_mesh, assign edge index to segments
void Ng_CopySegments(Ng_Mesh* orig_mesh, Ng_Mesh* dest_mesh, int edge_index);

// Copy surface elements from origin_mesh into destination_mesh, assign edge index to segments
void Ng_CopySurfaceElements(Ng_Mesh* orig_mesh, Ng_Mesh* dest_mesh, int face_index);

// Prepare surface meshing
void Ng_PrepareSurfaceMeshing(Ng_Mesh* mesh);


// Reverses all segments in mesh
void Ng_ReverseSegments(Ng_Mesh* mesh);

// Reverse all faces in mesh
void Ng_ReverseFaces(Ng_Mesh* mesh);

Ng_LocalH Ng_GetLocalH(Ng_Mesh* mesh);

void Ng_CopyLocalH(Ng_Mesh* orig_mesh, Ng_Mesh* dest_mesh);

double Ng_GetMaxH(Ng_Mesh* occ_mesh);

void Ng_ExportMeshToGmesh2(Ng_Mesh* mesh, const char* filename);

void Ng_ExportMeshToOpenFOAM(Ng_Mesh* mesh, const char* filename);


Ng_Result Ng_GetSolidMap(Ng_OCC_Geometry* geom,
    Ng_OCC_TopTools_IndexedMapOfShape* SoMap);

// Get the face map of an already loaded OCC geometry
Ng_Result Ng_GetFaceMap(Ng_OCC_Geometry* geom,
    Ng_OCC_TopTools_IndexedMapOfShape* FMap);

Ng_Result Ng_GetEdgeMap(Ng_OCC_Geometry* geom,
    Ng_OCC_TopTools_IndexedMapOfShape* EdgeMap);

int Ng_GetEdgeElementIndex(Ng_Mesh* mesh, int num);

int Ng_GetSurfaceElementIndex(Ng_Mesh* mesh, int num);

int Ng_GetVolumeElementIndex(Ng_Mesh* mesh, int num);

void Ng_RedirectCout(void* ptr_filestream);

void Ng_CalculateSurfacesOfNode(Ng_Mesh* mesh);

void Ng_DumpSegments(Ng_Mesh* mesh, void* ptr_filestream);

int Ng_GetNSeg(Ng_Mesh* mesh);

int Ng_GetSegment(Ng_Mesh* mesh, int num, int* pi, int* matnum);

void Ng_AddSegmentElement(Ng_Mesh* mesh, int pi1, int pi2, int edgeIndex);

void Ng_RestrictMeshSizeMesh(Ng_Mesh* orig_mesh, Ng_Mesh* dest_mesh);

void Ng_RestrictMeshSizeLocalH(Ng_Mesh* mesh, Ng_LocalH* localh);

Ng_Result Ng_GenerateBoundaryLayer(Ng_Mesh* mesh,
    int* surfid_arr, int surfid_count,
    double* heights_arr, int heights_count);


Ng_Result Ng_GenerateBoundaryLayer2(Ng_Mesh* mesh,
    int dom_nr, double* heights_arr, int heights_count);

void Ng_AddFaceDescriptor(Ng_Mesh* mesh, int faceId, int dominId, int domOutId, int TLOface);

void Ng_AddEdgeDescriptor(Ng_Mesh* mesh, int edgeId);

void Ng_AddSurfaceElement(Ng_Mesh* mesh, Ng_Surface_Element_Type et, int* pi, int surfIndx);

void Ng_AddVolumeElement(Ng_Mesh* mesh, Ng_Volume_Element_Type et, int* pi, int volIndx);

// **********************************************************
// **   OpenCascade Geometry / Meshing Utilities           **
// **********************************************************

//CENOS
Ng_OCC_Geometry* Ng_OCC_ShapeToGeometry(Ng_TopoDS_Shape* shape);

// Set the local mesh size based on geometry / topology
Ng_Result Ng_SetLocalMeshSize(Ng_OCC_Geometry* geom,
    Ng_Mesh* mesh,
    Ng_Meshing_Parameters* mp);

// Set the mesh geometry / topology
void Ng_SetGeometry(Ng_OCC_Geometry* geom, Ng_Mesh* mesh);


// Mesh Edges only
Ng_Result Ng_DivideEdges(Ng_OCC_Geometry* geom,
    Ng_Mesh* mesh,
    Ng_Meshing_Parameters* mp);

// Mesh the edges and add Face descriptors to prepare for surface meshing
Ng_Result Ng_GenerateEdgeMesh(Ng_OCC_Geometry* geom,
    Ng_Mesh* mesh,
    Ng_Meshing_Parameters* mp);

// Mesh the surfaces of an OCC geometry
Ng_Result Ng_GenerateSurfaceMesh(Ng_OCC_Geometry* geom,
    Ng_Mesh* mesh,
    Ng_Meshing_Parameters* mp);

Ng_Result Ng_GenerateBoundaryLayer2(Ng_Mesh* mesh,
    int dom_nr, double* heights_arr, int heights_count);

void Ng_Refine(Ng_Mesh* mesh);

void Ng_SetElementRefinement(Ng_Mesh* mesh, int el_index, bool b);

Ng_OCC_Geometry* Ng_OCC_ShapeToGeometry(Ng_TopoDS_Shape* shape);
void Ng_RedirectCout(void* ptr_filestream);


void Cenos_ExportMeshToGmesh2(Ng_Mesh* mesh, const char* filename);

void Cenos_GenerateBoundaryLayer(Ng_Mesh* mesh,
    int* surfid_arr, int surfid_count,
    double* heights_arr, int heights_count);

Ng_Result Cenos_OCC_GetSoMap(Ng_OCC_Geometry* geom,
    Ng_OCC_TopTools_IndexedMapOfShape* SoMap);

Ng_Result Cenos_OCC_GetEdgeMap(Ng_OCC_Geometry* geom,
    Ng_OCC_TopTools_IndexedMapOfShape* EdgeMap);

int Cenos_GetEdgeElementIndex(Ng_Mesh* mesh, int num);

int Cenos_GetSurfaceElementIndex(Ng_Mesh* mesh, int num);

int Cenos_GetVolumeElementIndex(Ng_Mesh* mesh, int num);

void Cenos_RedirectCout(void* ptr_filestream);

void Cenos_DumpSegments(Ng_Mesh* mesh, void* ptr_filestream);

void Cenos_AddSegmentElement(Ng_Mesh* mesh, int pi1, int pi2, int edgeIndex, double* zeroNode);

void Cenos_AddSurfaceElementUV(Ng_Mesh* mesh, Ng_Surface_Element_Type et,
    int* pi, int surfIndx, double* uv1, double* uv2, double* uv3);

void Cenos_AddSurfaceElement(Ng_Mesh* mesh, Ng_Surface_Element_Type et, int* pi, int surfIndx);

void Cenos_AddVolumeElement(Ng_Mesh* mesh, Ng_Volume_Element_Type et, int* pi, int volIndx);

Ng_Mesh* Cenos_NewMesh();

void Cenos_AddFaceDescriptor(Ng_Mesh* mesh, int faceId, int dominId, int domOutId, int TLOface);

void Cenos_AddEdgeDescriptor(Ng_Mesh* mesh, int edgeId);

void Cenos_WriteGmsh2Format(const netgen::Mesh& mesh, const string& filename);

void Cenos_DivideEdge(TopoDS_Edge& edge, netgen::NgArray<netgen::MeshPoint>& ps, netgen::NgArray<double>& params, netgen::Mesh& mesh, const netgen::MeshingParameters& mparam);

#endif // CNGLIB
