/* =============================================================================
**  This file is part of the mmg software package for the tetrahedral
**  mesh modification.
**  Copyright (c) Bx INP/CNRS/Inria/UBordeaux/UPMC, 2004-
**
**  mmg is free software: you can redistribute it and/or modify it
**  under the terms of the GNU Lesser General Public License as published
**  by the Free Software Foundation, either version 3 of the License, or
**  (at your option) any later version.
**
**  mmg is distributed in the hope that it will be useful, but WITHOUT
**  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
**  FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
**  License for more details.
**
**  You should have received a copy of the GNU Lesser General Public
**  License and of the GNU General Public License along with mmg (in
**  files COPYING.LESSER and COPYING). If not, see
**  <http://www.gnu.org/licenses/>. Please read their terms carefully and
**  use this copy of the mmg distribution only if you accept them.
** =============================================================================
*/

/**
 * \file common/API_functions.c
 * \brief C API functions definitions for MMG library.
 * \author Charles Dapogny (UPMC)
 * \author Cécile Dobrzynski (Bx INP/Inria/UBordeaux)
 * \author Pascal Frey (UPMC)
 * \author Algiane Froehly (Inria/UBordeaux)
 * \version 5
 * \date 03 2014
 * \copyright GNU Lesser General Public License.
 *
 * \note This file contains some internal functions for the API, see the \a
 * common/libmmgcommon_private.h, \a mmgs/libmmgs.h and \a mmg3d/libmmg3d.h header files
 * for the documentation of all the usefull user's API functions.
 *
 * C API for MMG library.
 *
 */

#include "mmgcommon_private.h"

/**
 * \param mesh pointer to the mesh structure.
 *
 * Initialization of the input parameters.
 *
 */
void MMG5_Init_parameters(MMG5_pMesh mesh) {

  memset(&mesh->info,0, sizeof(MMG5_Info));

  /* default values for integers */
  /* [-1..10], Tune level of imprim */
  mesh->info.imprim   =  1;
  /* [0/1]    ,Turn on/off levelset meshing */
  mesh->info.iso      =  MMG5_OFF;
  /* [0/1]    ,Turn on/off levelset meshing */
  mesh->info.isosurf  =  MMG5_OFF;
  /* [n/10]   ,Value for isosurface boundary reference */
  mesh->info.isoref   =  MG_ISO;
  /* [n/-1]   ,Set memory size to n Mbytes/keep the default value */
  mesh->info.mem      = MMG5_NONSET_MEM;
  /* [0/1]    ,Turn on/off debug mode */
  mesh->info.ddebug   =  MMG5_OFF;
  /* [n]      ,number of local parameters */
  mesh->info.npar     =  MMG5_OFF;
  /* [0/1]    ,avoid/allow point insertion/deletion */
  mesh->info.noinsert =  MMG5_OFF;
  /* [0/1]    ,avoid/allow edge or face flipping */
  mesh->info.noswap   =  MMG5_OFF;
  /* [0/1]    ,avoid/allow point relocation */
  mesh->info.nomove   =  MMG5_OFF;
  /* [n]    ,number of user-defined references */
  mesh->info.nmat = MMG5_OFF;
  /* [-1/val]    ,Turn off/on the removal of small bubles in levelset meshing */
  mesh->info.rmc      =  MMG5_NONSET;
  /* [0/1]    ,avoid/allow  */
  mesh->info.nosizreq =  MMG5_OFF;


  /* default values for doubles */
  /* angle detection */
  mesh->info.dhd      = MMG5_ANGEDG;
  /* minimal mesh size */
  mesh->info.hmin     = MMG5_NONSET_HMIN;
  /* maximal mesh size */
  mesh->info.hmax     = MMG5_NONSET_HMAX;
  /* constant mesh size */
  mesh->info.hsiz     = MMG5_NONSET_HSIZ;
  /* control Hausdorff */
  mesh->info.hausd    = MMG5_HAUSD;
  /* control gradation */
  mesh->info.hgrad    = MMG5_HGRAD;
  /* control gradation on required entities */
  mesh->info.hgradreq = MMG5_HGRADREQ;
  /* xreg relaxation parameter value */
  mesh->info.lxreg    = MMG5_XREG;

  /* default values for pointers */
  /* list of user-defined references */
  mesh->info.mat = NULL;

  /** MMG3D_IPARAM_lag is used by mmg3d only but need to be negative in the
   * scaleMesh function */
  mesh->info.lag      = MMG5_LAG;

  /* initial value for memMax and gap */
  mesh->gap = MMG5_GAP;
  mesh->memMax = MMG5_memSize();
  if ( mesh->memMax ) {
    /* maximal memory = 50% of total physical memory */
    mesh->memMax = (size_t)(mesh->memMax*MMG5_MEMPERCENT);
  } else {
    /* default value = 800 MB */
    printf("  Maximum memory set to default value: %d MB.\n",MMG5_MEMMAX);
    mesh->memMax = MMG5_MEMMAX << MMG5_BITWIZE_MB_TO_B;
  }

}


/**
 * \param mesh pointer to the mesh structure.
 * \param sol pointer to the sol structure.
 *
 * Initialize file names to their default values.
 *
 */
void MMG5_Init_fileNames(MMG5_pMesh mesh,MMG5_pSol sol
  ) {
  MMG5_Set_inputMeshName(mesh,"");
  MMG5_Set_outputMeshName(mesh,"");

  if ( sol ) {
    MMG5_Set_inputSolName(mesh,sol,"");
    MMG5_Set_outputSolName(mesh,sol,"");
  }

  return;
}

/**
 * \param mesh pointer to the mesh structure.
 * \param meshin input mesh name.
 * \return 1 if success, 0 if fail
 *
 * Set the name of input mesh.
 *
 */
int MMG5_Set_inputMeshName(MMG5_pMesh mesh, const char* meshin) {

  if ( mesh->namein ){
    MMG5_DEL_MEM(mesh,mesh->namein);
  }

  if ( meshin && strlen(meshin) ) {
    MMG5_ADD_MEM(mesh,(strlen(meshin)+1)*sizeof(char),"input mesh name",
                  fprintf(stderr,"  Exit program.\n");
                  return 0);
    MMG5_SAFE_CALLOC(mesh->namein,strlen(meshin)+1,char,return 0);
    strcpy(mesh->namein,meshin);
  }
  else {
    MMG5_ADD_MEM(mesh,10*sizeof(char),"input mesh name",
                  fprintf(stderr,"  Exit program.\n");
                  return 0);
    MMG5_SAFE_CALLOC(mesh->namein,10,char,return 0);
    strcpy(mesh->namein,"mesh.mesh");
    if ( (mesh->info.imprim > 5) || mesh->info.ddebug ) {
      fprintf(stderr,"\n  ## Warning: %s: no name given for input mesh.\n",__func__);
      fprintf(stderr,"              Use of default value \"mesh.mesh\".\n");
    }
  }
  return 1;
}

/**
 * \param mesh pointer to the mesh structure.
 * \param sol pointer to the sol structure.
 * \param solin name of the input solution file.
 * \return 1 if success, 0 if fail
 *
 * Set the name of input solution file.
 *
 */
int MMG5_Set_inputSolName(MMG5_pMesh mesh,MMG5_pSol sol, const char* solin) {
  char *ptr;

  if ( sol->namein )
    MMG5_DEL_MEM(mesh,sol->namein);

  if ( solin && strlen(solin) ) {
    MMG5_ADD_MEM(mesh,(strlen(solin)+1)*sizeof(char),"input sol name",
                  fprintf(stderr,"  Exit program.\n");
                  return 0);
    MMG5_SAFE_CALLOC(sol->namein,strlen(solin)+1,char,return 0);
    strcpy(sol->namein,solin);
  }
  else {
    if ( mesh->namein && strlen(mesh->namein) ) {
      int mesh_len = strlen(mesh->namein)+1;
      MMG5_SAFE_CALLOC(sol->namein,mesh_len,char,return 0);
      strcpy(sol->namein,mesh->namein);

      /* Get last dot character to avoid issues with <basename>.mesh.mesh files */
      char *dot = strrchr(sol->namein,'.');
      ptr = NULL;
      if ( dot) {
        ptr = strstr(dot,".mesh");
      }
      if ( ptr ) {
        /* the sol file is renamed concatening the mesh basename and the sol extension */
        *ptr = '\0';
      }
      MMG5_SAFE_REALLOC(sol->namein,mesh_len,(strlen(sol->namein)+5),char,
                        "input sol name",return 0);

      MMG5_ADD_MEM(mesh,(strlen(sol->namein)+5)*sizeof(char),"input sol name",
                    fprintf(stderr,"  Exit program.\n");
                    return 0);
      strcat(sol->namein,".sol");
    }
    else {
      MMG5_ADD_MEM(mesh,9*sizeof(char),"input sol name",
                    fprintf(stderr,"  Exit program.\n");
                    return 0);
      MMG5_SAFE_CALLOC(sol->namein,9,char,return 0);
      strcpy(sol->namein,"mesh.sol");
    }
  }
  return 1;
}

/**
 * \param mesh pointer to the mesh structure.
 * \param fparamin name of the input solution file.
 * \return 1 if success, 0 if fail
 *
 * Set the name of input parameter file.
 *
 */
int MMG5_Set_inputParamName(MMG5_pMesh mesh, const char* fparamin) {

  if ( mesh->info.fparam )
    MMG5_DEL_MEM(mesh,mesh->info.fparam);

  if ( fparamin && strlen(fparamin) ) {
    MMG5_ADD_MEM(mesh,(strlen(fparamin)+1)*sizeof(char),"input param name",
                  fprintf(stderr,"  Exit program.\n");
                  return 0);
    MMG5_SAFE_CALLOC(mesh->info.fparam,strlen(fparamin)+1,char,return 0);
    strcpy(mesh->info.fparam,fparamin);
  }
  else {
    fprintf(stderr,"\n  ## Warning: %s: no name given for the parameter file.\n",__func__);
    fprintf(stderr,"              We should have never end here.\n");
    return 0;
  }
  return 1;
}

/**
 * \param mesh pointer to the mesh structure.
 * \param meshout name of the output mesh file.
 * \return 1 if success, 0 if fail.
 *
 * Set the name of output mesh file.
 *
 */
int MMG5_Set_outputMeshName(MMG5_pMesh mesh, const char* meshout) {
  int  fmt = MMG5_FMT_MeditASCII,fmtin;
  char *ptr,*ptrin;

  if ( mesh->nameout )
    MMG5_DEL_MEM(mesh,mesh->nameout);

  if ( meshout && strlen(meshout) ) {
    ptr   = strrchr(meshout, '.');

    MMG5_ADD_MEM(mesh,(strlen(meshout)+7)*sizeof(char),"output mesh name",
                  fprintf(stderr,"  Exit program.\n");
                  return 0);
    MMG5_SAFE_CALLOC(mesh->nameout,strlen(meshout)+7,char,return 0);
    strcpy(mesh->nameout,meshout);

    if ( ( ptr && MMG5_Get_format(ptr,0)==MMG5_FMT_Unknown ) || (!ptr) || ptr == meshout ) {
      /* No extension */
      ptrin   = MMG5_Get_filenameExt(mesh->namein);
      fmtin   = MMG5_Get_format(ptrin,MMG5_FMT_MeditASCII);
      fmt     = MMG5_FMT_Unknown;
    }
    strcpy(mesh->nameout,meshout);

    if ( fmt == MMG5_FMT_Unknown ) {
      /* No extension */
      switch ( fmtin ) {
      case ( MMG5_FMT_GmshASCII ):
        strcat(mesh->nameout,".msh");
        break;
      case ( MMG5_FMT_GmshBinary ):
        strcat(mesh->nameout,".mshb");
        break;
      case ( MMG5_FMT_VtkVtu ):
        strcat(mesh->nameout,".vtu");
        break;
      case ( MMG5_FMT_VtkVtp ):
        strcat(mesh->nameout,".vtp");
        break;
      case ( MMG5_FMT_VtkVtk ):
        strcat(mesh->nameout,".vtk");
        break;
      case ( MMG5_FMT_MeditBinary ):
        strcat(mesh->nameout,".meshb");
        break;
      case ( MMG5_FMT_MeditASCII ): default:
        strcat(mesh->nameout,".mesh");
        break;
      }
    }
  }
  else {
    if ( mesh->namein && strlen(mesh->namein) ) {
      MMG5_ADD_MEM(mesh,(strlen(mesh->namein)+9)*sizeof(char),"output mesh name",
                    fprintf(stderr,"  Exit program.\n");
                    return 0);
      MMG5_SAFE_CALLOC(mesh->nameout,strlen(mesh->namein)+9,char,return 0);
      strcpy(mesh->nameout,mesh->namein);

      ptr   = MMG5_Get_filenameExt(mesh->nameout);
      fmt   = MMG5_Get_format(ptr,MMG5_FMT_MeditASCII);

      if ( ptr ) *ptr = '\0';

      switch ( fmt ) {

      case ( MMG5_FMT_GmshASCII ):
        strcat(mesh->nameout,".o.msh");
        break;
      case ( MMG5_FMT_GmshBinary ):
        strcat(mesh->nameout,".o.mshb");
        break;
      case ( MMG5_FMT_VtkVtu ):
        strcat(mesh->nameout,".o.vtu");
        break;
      case ( MMG5_FMT_VtkVtp ):
        strcat(mesh->nameout,".o.vtp");
        break;
      case ( MMG5_FMT_VtkVtk ):
        strcat(mesh->nameout,".o.vtk");
        break;
      case ( MMG5_FMT_MeditBinary ):
        strcat(mesh->nameout,".o.meshb");
        break;
      case ( MMG5_FMT_MeditASCII ): default:
        strcat(mesh->nameout,".o.mesh");
        break;
      }
    }
    else {
      MMG5_ADD_MEM(mesh,12*sizeof(char),"output mesh name",
                    fprintf(stderr,"  Exit program.\n");
                    return 0);
      MMG5_SAFE_CALLOC(mesh->nameout,12,char,return 0);
      if ( (mesh->info.imprim > 5) || mesh->info.ddebug ) {
        fprintf(stderr,"\n  ## Warning: %s: no name given for output mesh.\n",
                __func__);
        fprintf(stderr,"              Use of default value \"mesh.o.mesh\".\n");
      }
      strcpy(mesh->nameout,"mesh.o.mesh");
    }
  }
  return 1;
}


/**
 * \param mesh pointer to the mesh structure.
 * \param sol pointer to the sol structure.
 * \param solout name of the output solution file.
 * \return 0 if failed, 1 otherwise.
 *
 *  Set the name of output solution file.
 *
 */
int MMG5_Set_outputSolName(MMG5_pMesh mesh,MMG5_pSol sol, const char* solout) {
  char *ptr;
  int oldsize;

  if ( sol->nameout )
    MMG5_DEL_MEM(mesh,sol->nameout);

  if ( solout && strlen(solout) ) {
    MMG5_ADD_MEM(mesh,(strlen(solout)+1)*sizeof(char),"output sol name",
                  fprintf(stderr,"  Exit program.\n");
                  return 0);
    MMG5_SAFE_CALLOC(sol->nameout,strlen(solout)+1,char,return 0);
    strcpy(sol->nameout,solout);
  }
  else {
    if ( mesh->nameout && strlen(mesh->nameout) ) {
      /* Get last dot character to avoid issues with <basename>.mesh.mesh files */
      char *dot = strrchr(mesh->nameout,'.');
      ptr = NULL;
      if ( dot) {
        ptr = strstr(dot,".mesh");
      }
      if ( ptr ) {
        MMG5_SAFE_CALLOC(sol->nameout,strlen(mesh->nameout)+1,char,return 0);
        oldsize = strlen(mesh->nameout)+1;
      }
      else {
        MMG5_SAFE_CALLOC(sol->nameout,strlen(mesh->nameout)+6,char,return 0);
        oldsize = strlen(mesh->nameout)+6;
      }
      strcpy(sol->nameout,mesh->nameout);
      dot = strrchr(sol->nameout,'.');
      ptr = NULL;
      if ( dot) {
        ptr = strstr(dot,".mesh");
      }
      if ( ptr )
        /* the sol file is renamed with the meshfile basename and .sol ext */
        *ptr = '\0';

      MMG5_ADD_MEM(mesh,(strlen(sol->nameout)+5)*sizeof(char),"output sol name",
                    fprintf(stderr,"  Exit program.\n");
                    return 0);
      MMG5_SAFE_REALLOC(sol->nameout,oldsize,(strlen(sol->nameout)+5),char,
                         "output sol name",return 0);
      strcat(sol->nameout,".sol");

    }
    else {
      fprintf(stderr,"\n  ## Error: %s: no name for output mesh. please, use",
              __func__);
      fprintf(stderr," the MMG5_Set_outputMeshName to set the mesh name.\n");
      return 0;
    }
  }
  return 1;
}

void MMG5_Set_constantSize(MMG5_pMesh mesh,MMG5_pSol met,double hsiz) {
  MMG5_pPoint ppt;
  MMG5_int    k,iadr;

  if ( met->size == 1 ) {
    for (k=1; k<=mesh->np; k++) {
      ppt = &mesh->point[k];
      if ( !MG_VOK(ppt) ) continue;
      met->m[k] = hsiz;
    }
  }
  else {
    hsiz    = 1./(hsiz*hsiz);

    if ( mesh->dim==2 ) {
      for (k=1; k<=mesh->np; k++) {
        ppt = &mesh->point[k];
        if ( !MG_VOK(ppt) ) continue;

        iadr           = 3*k;
        met->m[iadr]   = hsiz;
        met->m[iadr+1] = 0.;
        met->m[iadr+2] = hsiz;
      }
    }
    else {
      assert ( mesh->dim==3 );
      for (k=1; k<=mesh->np; k++) {
        ppt = &mesh->point[k];
        if ( !MG_VOK(ppt) ) continue;

        iadr           = 6*k;
        met->m[iadr]   = hsiz;
        met->m[iadr+1] = 0.;
        met->m[iadr+2] = 0.;
        met->m[iadr+3] = hsiz;
        met->m[iadr+4] = 0.;
        met->m[iadr+5] = hsiz;
      }
    }
  }

  return;
}

int MMG5_Free_allSols(MMG5_pMesh mesh,MMG5_pSol *sol) {
  int i;

  if ( sol ) {
    if ( mesh->nsols ) {
      for ( i=0; i<mesh->nsols; ++i ) {
        MMG5_DEL_MEM(mesh,(*sol)[i].m);
      }
    }
    MMG5_DEL_MEM(mesh,(*sol));
  }

  return 1;
}

/**
 * \param mesh pointer to the mesh structure.
 * \param sol pointer to the sol structure.
 *
 * Structures unallocation before return (common structures between all codes).
 *
 */
void MMG5_Free_structures(MMG5_pMesh mesh,MMG5_pSol sol){

  if ( mesh->point )
    MMG5_DEL_MEM(mesh,mesh->point);

  if ( mesh->xpoint )
    MMG5_DEL_MEM(mesh,mesh->xpoint);

  if ( mesh->edge )
    MMG5_DEL_MEM(mesh,mesh->edge);

  if ( mesh->adja )
    MMG5_DEL_MEM(mesh,mesh->adja);

  if ( mesh->tria )
    MMG5_DEL_MEM(mesh,mesh->tria);

  if ( mesh->adjt )
    MMG5_DEL_MEM(mesh,mesh->adjt);

  /* sol */
  if ( sol && sol->m )
    MMG5_DEL_MEM(mesh,sol->m);

  /* mesh->info */
  if ( mesh->info.npar && mesh->info.par )
    MMG5_DEL_MEM(mesh,mesh->info.par);

  if ( mesh->info.nmat ) {
    if( mesh->info.mat )
      MMG5_DEL_MEM(mesh,mesh->info.mat);
    if( mesh->info.invmat.lookup )
      MMG5_DEL_MEM(mesh,mesh->info.invmat.lookup);
  }

  if ( mesh->info.imprim>5 || mesh->info.ddebug ) {
    printf("  MEMORY USED AT END (Bytes) %zu\n",mesh->memCur);
  }

  return;
}

/**
 * \param mesh pointer to the mesh structure.
 * \param met pointer to the sol structure.
 *
 * File name deallocations before return.
 *
 */
void MMG5_mmgFree_names(MMG5_pMesh mesh,MMG5_pSol met){

  /* mesh */
  if ( mesh->nameout ) {
    MMG5_DEL_MEM(mesh,mesh->nameout);
  }

  if ( mesh->namein ) {
    MMG5_DEL_MEM(mesh,mesh->namein);
  }

  /* met */
  if ( met ) {
    if ( met->namein ) {
      MMG5_DEL_MEM(mesh,met->namein);
    }

    if ( met->nameout ) {
      MMG5_DEL_MEM(mesh,met->nameout);
    }
  }
}

inline
int MMG5_Set_defaultTruncatureSizes(MMG5_pMesh mesh,int8_t sethmin,int8_t sethmax) {

  if ( !sethmin ) {
    if ( sethmax ) {
      mesh->info.hmin  = MG_MIN ( MMG5_HMINCOE, MMG5_HMINCOE * mesh->info.hmax);
    } else {
      mesh->info.hmin  = MMG5_HMINCOE;
    }
  }

  if ( !sethmax ) {
    if ( sethmin ) {
      mesh->info.hmax = MG_MAX ( MMG5_HMAXCOE, 1./MMG5_HMINCOE * mesh->info.hmin);
    }
    else {
      mesh->info.hmax  = MMG5_HMAXCOE;
    }
  }

  if ( mesh->info.hmax < mesh->info.hmin ) {
    assert ( sethmin && sethmax );
    fprintf(stderr,"\n  ## Error: %s: Mismatched options:"
            " minimal mesh size larger than maximal one.\n",__func__);
    return 0;
  }

  if ( mesh->info.ddebug ) {
    /* print unscaled values for debug purpose */
    fprintf(stdout,"     After truncature computation:   hmin %lf (user setted %d)\n"
            "                                     hmax %lf (user setted %d)\n",
            mesh->info.delta * mesh->info.hmin,mesh->info.sethmin,
            mesh->info.delta * mesh->info.hmax,mesh->info.sethmax);
  }

  return 1;
}

int MMG5_Compute_constantSize(MMG5_pMesh mesh,MMG5_pSol met,double *hsiz) {

  if ( mesh->info.hmin > mesh->info.hsiz ) {
    fprintf(stderr,"\n  ## Error: %s: Mismatched options: hmin (%e) is greater"
            " than hsiz (%e). Exit Program.\n",__func__,
            mesh->info.hmin,mesh->info.hsiz);
    return 0;
  }

  if ( mesh->info.hmax > 0. && mesh->info.hmax < mesh->info.hsiz ) {
    fprintf(stderr,"\n  ## Error: %s: Mismatched options: hmax (%e) is lower"
            " than hsiz (%e). Exit Program.\n",__func__,
            mesh->info.hmax,mesh->info.hsiz);
    return 0;
  }

  *hsiz = mesh->info.hsiz;

  if ( !MMG5_check_setted_hminhmax(mesh) ) {
    return 0;
  }

  if ( mesh->info.sethmin ) {
    *hsiz    =  MG_MAX(mesh->info.hmin,*hsiz);
  }

  if ( mesh->info.sethmax ) {
    *hsiz    = MG_MIN(mesh->info.hmax,*hsiz);
  }

  /* Set hmin */
  if ( !mesh->info.sethmin ) {
    if ( mesh->info.sethmax ) {
      mesh->info.hmin  = MG_MIN(0.1*(*hsiz),0.1*mesh->info.hmax);
    } else {
      mesh->info.hmin  = 0.1*(*hsiz);
    }
  }

  /* Set hmax */
  if ( !mesh->info.sethmax ) {
    if ( mesh->info.sethmin ) {
      mesh->info.hmax  = MG_MAX(10.*(*hsiz),10.*mesh->info.hmin);
    } else {
      mesh->info.hmax  = 10.*(*hsiz);
    }
  }

  if ( mesh->info.ddebug ) {
    /* print unscaled values for debug purpose */
    fprintf(stdout,"     After hsiz computation:   hmin %lf (user setted %d)\n"
            "                               hmax %lf (user setted %d)\n",
            mesh->info.delta * mesh->info.hmin,mesh->info.sethmin,
            mesh->info.delta * mesh->info.hmax,mesh->info.sethmax);
  }

  return 1;
}

/* Useful tools to manage C strings */
char *MMG5_Get_basename(char *path) {
  char *s = strrchr(path, '/');

  if (!s)
    return strdup(path);
  else
    return strdup(s + 1);
}


const char* MMG5_Get_entitiesName(enum MMG5_entities ent)
{
  switch (ent)
  {
  case MMG5_Noentity:
    return "MMG5_Noentity";
    break;
  case MMG5_Vertex:
    return "MMG5_Vertex";
    break;
  case MMG5_Edg:
    return "MMG5_Edg";
    break;
  case MMG5_Triangle:
    return "MMG5_Triangle";
    break;
  case MMG5_Tetrahedron:
    return "MMG5_Tetrahedron";
    break;
  default:
    return"MMG5_Unknown";
  }
}
const char* MMG5_Get_typeName(enum MMG5_type typ)
{
  switch (typ)
  {
  case MMG5_Notype:
    return "MMG5_Notype";
    break;
  case MMG5_Scalar:
    return "MMG5_Scalar";
    break;
  case MMG5_Vector:
    return "MMG5_Vector";
    break;
  case MMG5_Tensor:
    return "MMG5_Tensor";
    break;
  default:
    return "MMG5_Unknown";
  }
}

const char* MMG5_Get_tagName(uint16_t tag)
{
  static char tags_name[1024];

  if ( !tag )
  {
    return "No tag";
  }

  if ( tag & MG_NUL ) {
    return "Removed";
  }

  strcpy(tags_name, "\0");

  if ( tag & MG_REF ) {
    strcat(tags_name,"Reference ");
  }

  if ( tag & MG_GEO) {
    strcat(tags_name,"Ridge ");
  }

  if ( tag & MG_REQ) {
    strcat(tags_name,"Required ");
      }

  if ( tag & MG_NOM) {
    strcat(tags_name,"Non-manifold ");
      }

  if ( tag & MG_BDY) {
    strcat(tags_name,"Boundary ");
      }

  if ( tag & MG_CRN) {
    strcat(tags_name,"Corner ");
    }

  if ( tag & MG_NOSURF) {
    strcat(tags_name,"Nosurf ");
  }

  if ( tag & MG_OPNBDY) {
    strcat(tags_name,"Opnbdy ");
  }

  if ( tag & MG_OLDPARBDY) {
    strcat(tags_name,"Old-parbdy ");
  }

  if ( tag & MG_PARBDYBDY) {
    strcat(tags_name,"Parbdybdy ");
  }

  if ( tag & MG_PARBDY) {
    strcat(tags_name,"Parbdy ");
  }

  if ( tag & MG_OVERLAP) {
    strcat(tags_name,"Overlap ");
  }

  strcat(tags_name,"tag(s).");

  return tags_name;
}

/**
 * \param ptr pointer to the file extension (dot included)
 * \param fmt default file format.
 *
 * \return and index associated to the file format detected from the extension.
 *
 * Get the wanted file format from the mesh extension. If \a fmt is provided, it
 * is used as default file format (\a ptr==NULL), otherwise, the default file
 * format is the medit one.
 *
 */
int MMG5_Get_format( char *ptr, int fmt ) {
  /* Default is the Medit file format or a format given as input */
  int defFmt = fmt;

  if ( !ptr ) return defFmt;

  if ( !strncmp ( ptr,".meshb",strlen(".meshb") ) ) {
    return MMG5_FMT_MeditBinary;
  }
  else if ( !strncmp( ptr,".mesh",strlen(".mesh") ) ) {
    return MMG5_FMT_MeditASCII;
  }
  else if ( !strncmp( ptr,".mshb",strlen(".mshb") ) ) {
    return MMG5_FMT_GmshBinary;
  }
  else if ( !strncmp( ptr,".msh",strlen(".msh") ) ) {
    return MMG5_FMT_GmshASCII;
  }
  else if ( !strncmp ( ptr,".pvtu",strlen(".pvtu") ) ) {
    return MMG5_FMT_VtkPvtu;
  }
  else if ( !strncmp ( ptr,".vtu",strlen(".vtu") ) ) {
    return MMG5_FMT_VtkVtu;
  }
  else if ( !strncmp ( ptr,".pvtp",strlen(".pvtu") ) ) {
    return MMG5_FMT_VtkPvtp;
  }
  else if ( !strncmp ( ptr,".vtp",strlen(".vtp") ) ) {
    return MMG5_FMT_VtkVtp;
  }
  else if ( !strncmp ( ptr,".vtk",strlen(".vtk") ) ) {
    return MMG5_FMT_VtkVtk;
  }
  else if ( !strncmp ( ptr,".node",strlen(".node") ) ) {
    return MMG5_FMT_Tetgen;
  }

  return defFmt;
}

/**
 * \param fmt file format.
 *
 * \return The name of the file format in a string.
 *
 * Print the name of the file format associated to \a fmt.
 *
 */
const char* MMG5_Get_formatName(enum MMG5_Format fmt)
{
  switch (fmt)
  {
  case MMG5_FMT_MeditASCII:
    return "MMG5_FMT_MeditASCII";
    break;
  case MMG5_FMT_MeditBinary:
    return "MMG5_FMT_MeditBinary";
    break;
  case MMG5_FMT_VtkVtu:
    return "MMG5_FMT_VtkVtu";
    break;
  case MMG5_FMT_VtkVtp:
    return "MMG5_FMT_VtkVtp";
    break;
  case MMG5_FMT_VtkPvtu:
    return "MMG5_FMT_VtkPvtu";
    break;
  case MMG5_FMT_VtkPvtp:
    return "MMG5_FMT_VtkPvtp";
    break;
  case MMG5_FMT_VtkVtk:
    return "MMG5_FMT_VtkVtk";
    break;
  case MMG5_FMT_GmshASCII:
    return "MMG5_FMT_GmshASCII";
    break;
  case MMG5_FMT_GmshBinary:
    return "MMG5_FMT_GmshBinary";
    break;
  case MMG5_FMT_Tetgen:
    return "MMG5_FMT_Tetgen";
    break;
  default:
    return "MMG5_Unknown";
  }
}

/**
 * \param filename string containing a filename
 *
 * \return pointer to the filename extension or toward the end of the string
 * if no extension have been founded
 *
 * Get the extension of the filename string. Do not consider '.o' as an extension.
 *
 */
char *MMG5_Get_filenameExt( char *filename ) {
  const char pathsep='/';
  char       *dot,*lastpath;

  if ( !filename ) {
    return NULL;
  }

  dot = strrchr(filename, '.');
  lastpath = (pathsep == 0) ? NULL : strrchr (filename, pathsep);

  if ( (!dot) || dot == filename || (lastpath>dot) || (!strcmp(dot,".o")) ) {
    /* No extension */
    return filename + strlen(filename);
  }

  return dot;
}

/**
 * \param path string containing a filename and its path
 *
 * \return a pointer to the path allocated here
 *
 * Remove filename from a path and return the path in a newly allocated string.
 *
 */
char *MMG5_Get_path(char *path) {
  char *lastpath,*retpath;
  int len;

  if ( path == NULL) return NULL;

  lastpath = (MMG5_PATHSEP == 0) ? NULL : strrchr (path, MMG5_PATHSEP);

  if ( !lastpath ) {
    return NULL;
  }


  len = 0;
  while ( path+len != lastpath ) {
    ++len;
  }

  MMG5_SAFE_MALLOC(retpath,len+1,char,return NULL);

  /* Copy the string without the extension and add \0 */
  strncpy ( retpath, path, len );
  retpath[len] = '\0';

  return retpath;
}

/**
 * \param path path from which we want to remove the extension.
 *
 * \return allocated string or NULL if the allocation fail.
 *
 * Allocate a new string and copy \a path without extension in it.
 *
 */
char *MMG5_Remove_ext (char* path,char *ext) {
  int        len;
  char       *retpath, *lastext, *lastpath;
  char       *extloc;

  /* Default extension if not provided */
  if ( (!ext) || !*ext ) {
    extloc = ".";
  }
  else {
    extloc = ext;
  }

  /* Error checks and string allocation. */
  if ( path == NULL) return NULL;

  /* Find the relevant characters and the length of the string without
   * extension */
  lastext = strstr (path, extloc);
  lastpath = (MMG5_PATHSEP == 0) ? NULL : strrchr (path, MMG5_PATHSEP);

  if ( lastext == NULL || (lastpath != NULL && lastpath > lastext) ) {
    /* No extension or the extension is left from a separator (i.e. it is not an
     * extension) */
    len = strlen(path);
  }
  else {
    /* An extension is found */
    len = 0;
    while ( path+len != lastext ) {
      ++len;
    }
  }

  MMG5_SAFE_MALLOC(retpath,len+1,char,return NULL);

  /* Copy the string without the extension and add \0 */
  strncpy ( retpath, path, len );
  retpath[len] = '\0';

  return retpath;
}