#ifndef PIO_UTIL_
#define PIO_UTIL_

#include <stdbool.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#ifdef USE_MPI
#include "mpi.h"
#endif

#ifndef _ERROR_H
#include "error.h"
#endif

#define MAXDEBUG           3

#define ddebug             0

#define debugString "#####"

#ifdef USE_MPI
void
cdiAbortC_MPI(const char * caller, const char * filename,
              const char *functionname, int line,
              const char * errorString, va_list ap)
  __attribute__((noreturn));
#endif

#ifdef USE_MPI
static inline int
callsToMPIAreAllowed()
{
  int init_flag = 0, finished_flag = 0;
  return MPI_Initialized(&init_flag) == MPI_SUCCESS && init_flag
    && MPI_Finalized(&finished_flag) == MPI_SUCCESS && !finished_flag;
}

static inline int
getMPICommWorldRank()
{
  int rank = -1;
  if (callsToMPIAreAllowed())
    MPI_Comm_rank(MPI_COMM_WORLD, &rank);
  return rank;
}
#endif

#ifdef USE_MPI
#define xdebug(fmt, ...)                                                \
  if ( ddebug ){                                                        \
    int rank = getMPICommWorldRank();                                   \
    fprintf ( stderr, "%s pe%d in %s, %s, line %d: " fmt "\n",          \
              debugString, rank,  __func__, __FILE__,  __LINE__,        \
              __VA_ARGS__ );                                            \
  }

#else
#define xdebug(fmt, ...)                                           \
  if ( ddebug ){                                                   \
    fprintf ( stderr, "%s %s, %s, line %d: " fmt "\n",             \
              debugString, __func__, __FILE__,  __LINE__,          \
              __VA_ARGS__ );                                       \
  }
#endif


#ifdef USE_MPI
#define xdebug3(fmt, ...)                                               \
  if ( ddebug == MAXDEBUG ){                                            \
    int rank = getMPICommWorldRank();                                   \
    fprintf ( stderr, "pe%d in %s, %s, line %d: " fmt "\n",             \
              rank,  __func__, __FILE__,  __LINE__,                     \
              __VA_ARGS__ );                                            \
  }

#else
#define xdebug3(fmt, ...)					\
  if ( ddebug == MAXDEBUG ){                                    \
    fprintf ( stderr, "%s, %s, line %d: " fmt "\n",             \
              __func__, __FILE__,  __LINE__,                    \
              __VA_ARGS__ );                                 \
  }
#endif

#ifdef USE_MPI
#define xwarning(fmt, ...)						\
  if ( ddebug ){							\
    int rank = getMPICommWorldRank();                                   \
    fprintf ( stderr, "WARNING: pe%d in %s, %s, line %d: " fmt "\n",	\
              rank,  __func__, __FILE__,  __LINE__,			\
              __VA_ARGS__ );						\
  }
#else
#define xwarning(fmt, ...)					\
  if ( ddebug ){                                                \
    fprintf ( stderr, "WARNING: %s, %s, line %d: " fmt "\n",    \
              __func__, __FILE__,  __LINE__,                    \
              __VA_ARGS__ );                                 \
  }
#endif

void * pcdiXmalloc ( size_t, const char *, const char *, int );
#define xmalloc(size) pcdiXmalloc ( size, __FILE__, __func__,  __LINE__ )

void * pcdiXcalloc ( size_t, size_t, const char *, const char *, int );
#define xcalloc(nmemb,size) pcdiXcalloc(nmemb, size,            \
                                        __FILE__, __func__, __LINE__)

void * pcdiXrealloc ( void *, size_t, const char *, const char *, int );
#define xrealloc(p,size) pcdiXrealloc(p, size,            \
                                      __FILE__, __func__, __LINE__)

void pcdiXMPI(int iret, const char *, int);
#define xmpi(ret) do {                                  \
    int tmpIRet = (ret);                                   \
    if (tmpIRet != MPI_SUCCESS)                            \
      pcdiXMPI(tmpIRet, __FILE__, __LINE__ );              \
  } while(0)

#ifdef USE_MPI
void pcdiXMPIStat ( int, const char *, int, MPI_Status * );
#define xmpiStat(ret,stat) pcdiXMPIStat ( ret, __FILE__, __LINE__, stat )

void pcdiDebugComm ( const char *filename, const char *functionname, int line, \
                     MPI_Comm *comm );
#define xdebugComm(comm)\
  if ( ddebug ) pcdiDebugComm (  __FILE__, __func__, __LINE__, comm )
#endif

void pcdiDebugMsg ( const char * cdiDebugString, const char *filename, const char *functionname, int line, \
                    int tag, int source, int nfinished );
#define xdebugMsg(tag,source,nfinished) \
  if ( ddebug ) \
      pcdiDebugMsg ( debugString, __FILE__, __func__, __LINE__, tag, source, nfinished )

void pcdiDebugMsg2 ( const char *filename, const char *functionname, int line, \
                    int tag, int source, char * text );
#define xdebugMsg2(tag,source,text) \
  if ( ddebug ) pcdiDebugMsg ( __FILE__, __func__,  __LINE__, tag, source, text )

int xmaxInt ( int, int );
int xminInt ( int, int );
int xsum ( int, int * );

double xchecksum ( int, int, void * );
 
void printArray ( const char *, char *, const void *, int, int, const char *, const char *, int );
#define xprintArray(ps,array,n,datatype)                                \
  if ( ddebug )                                                         \
      printArray ( debugString, ps, array, n, datatype,  __func__, __FILE__, __LINE__ )
 
#define xprintArray3(ps,array,n,datatype)                                \
  if ( ddebug == MAXDEBUG )                                                         \
      printArray ( debugString, ps, array, n, datatype,  __func__, __FILE__, __LINE__ )

/**
 * @return number of dimensions
 */
int
cdiPioQueryVarDims(int varShape[3], int vlistID, int varID);

#endif
/*
 * Local Variables:
 * c-file-style: "Java"
 * c-basic-offset: 2
 * indent-tabs-mode: nil
 * show-trailing-whitespace: t
 * require-trailing-newline: t
 * End:
 */
