#ifdef HAVE_CONFIG_H
#  include "config.h"
#endif

#include <errno.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "pio_util.h"
#include "cdi.h"

static
char commands[][13] = { "FINALIZE\0",
                        "RESOURCES\0",
                        "WINCREATE\0",
                        "WRITETS\0"};


void pcdiAssert(bool assumption,
                const char * filename, const char * functionname, int line )
{
  if ( !assumption )
    {
#ifdef USE_MPI
      int rank = -1, init_flag = 0;
      if (MPI_Initialized(&init_flag) == MPI_SUCCESS && init_flag)
        MPI_Comm_rank ( MPI_COMM_WORLD, &rank );
      fprintf ( stderr, "ERROR, FALSE ASSUMPTION: PE%d in %s, %s, line %d\n",
                rank, functionname, filename, line );
      fflush ( stderr );
      if (init_flag)
        MPI_Abort ( MPI_COMM_WORLD, 1 );
      else
        abort();
#else
      fprintf ( stderr, "ERROR, FALSE ASSUMPTION, %s, %s, line %d\n",
                functionname, filename, line );
      fflush ( stderr );
      abort();
#endif
    }
}

/****************************************************/

void pcdiAbortC(const char * caller, const char * filename, const char *functionname, int line,
                const char * errorString, ... )
{
  va_list ap;
  va_start(ap, errorString);
#ifdef USE_MPI
  {
    int rank;
    MPI_Comm_rank ( MPI_COMM_WORLD, &rank );
    fprintf(stderr, "ERROR, pe%d in %s, %s, line %d, called from %s\nerrorString: \"",
            rank, functionname, filename, line, caller);
  }
  vfprintf(stderr, errorString, ap);
  fputs("\"\n", stderr);
  MPI_Abort ( MPI_COMM_WORLD, 1 );
#else
  fprintf(stderr, "ERROR, %s, %s, line %d, called from %s\nerrorString: \"",
          functionname, filename, line, caller);
  vfprintf(stderr, errorString, ap);
  fputs("\"\n", stderr);
#endif
  exit ( EXIT_FAILURE );
  va_end(ap);
}

void pcdiAbort(const char * filename, const char *functionname, int line,
               const char * errorString, ... )
{
  va_list ap;
  va_start(ap, errorString);
#ifdef USE_MPI
  {
    int rank;
    MPI_Comm_rank ( MPI_COMM_WORLD, &rank );
    fprintf(stderr, "ERROR, pe%d in %s, %s, line %d, errorString: \"",
            rank, functionname, filename, line);
  }
  vfprintf(stderr, errorString, ap);
  fputs("\"\n", stderr);
  MPI_Abort ( MPI_COMM_WORLD, 1 );
#else
  fprintf(stderr, "ERROR, %s, %s, line %d, errorString: \"",
          functionname, filename, line);
  vfprintf(stderr, errorString, ap);
  fputs("\"\n", stderr);
#endif
  exit ( EXIT_FAILURE );
  va_end(ap);
}

/*****************************************************************************/

void * pcdiXmalloc ( size_t size, const char *filename, const char *functionname,
		     int line )
{
  void * value = calloc (1, size );

  if ( value == NULL )
    pcdiAbort (filename, functionname, line, "malloc failed: %s",
               strerror(errno));

  return value;
}

void * pcdiXcalloc ( size_t nmemb, size_t size, const char *filename,
		     const char *functionname, int line )
{
  void * value = calloc ( nmemb, size );

  if ( value == NULL )
    pcdiAbort (filename, functionname, line, "calloc failed: %s",
               strerror(errno) );

  return value;
}

void * pcdiXrealloc ( void *p, size_t size, const char *functionname,
		      const char *filename, int line )
{
  void * value = realloc ( p, size );

  if ( value == NULL )
    pcdiAbort(filename, functionname, line, "realloc failed: %s",
              strerror(errno));

  return value;
}

/***************************************************************/

#ifdef USE_MPI
void pcdiXMPI ( int iret, const char *filename, int line )
{
  char errorString[2][MPI_MAX_ERROR_STRING + 1];
  int len, errorClass, rank;

  if ( iret != MPI_SUCCESS )
    {
      MPI_Comm_rank ( MPI_COMM_WORLD, &rank );

      MPI_Error_class ( iret, &errorClass );
      MPI_Error_string ( errorClass, errorString[0], &len );
      errorString[0][len] = '\0';
      MPI_Error_string ( iret, errorString[1], &len);
      errorString[1][len] = '\0';

      fprintf ( stderr, "MPI ERROR, pe%d, %s, line %d,"
                "errorClass: \"%s\""
                "errorString: \"%s\"\n",
                rank, filename, line,
                errorString[0], errorString[1]);

      MPI_Abort ( MPI_COMM_WORLD, iret );
    }
}

/*****************************************************************************/

void pcdiXMPIStat ( int iret, const char *filename, int line, MPI_Status *status )
{
  char errorString[MPI_MAX_ERROR_STRING + 1];
  int len, rank;

  MPI_Comm_rank ( MPI_COMM_WORLD, &rank );

  if ( iret == MPI_ERR_IN_STATUS )
    {
      switch ( status->MPI_ERROR )
        {
          fprintf ( stderr, "------- checking error in request ----------\n" );
        case MPI_SUCCESS :
          fprintf ( stderr, "-------- mpi_success -----------\n" );
          break;
        case MPI_ERR_PENDING:
          fprintf ( stderr, "-------- mpi_err_pending ----------\n");
          break;
        default:
          MPI_Error_string ( status->MPI_ERROR, errorString, &len );
          errorString[len] = '\0';
          fprintf ( stderr,"MPI ERROR in request, pe%d, %s, line %d,"
                    "return value: %d, error_string: %s\n",
                    rank, filename, line, iret, errorString );
          MPI_Abort ( MPI_COMM_WORLD, iret );
        }
    }
  else
    xmpi ( iret );

  return;
}
#endif

/****************************************************/

#ifdef USE_MPI
void pcdiDebugComm ( const char *filename, const char *functionname, int line, MPI_Comm *comm )
{
  int rank, size, len, rankGlob;
  char *name;

  name = ( char * ) xmalloc ( MPI_MAX_OBJECT_NAME );
  memset ( name, 0, ( MPI_MAX_OBJECT_NAME ) * sizeof ( char ));
  MPI_Comm_get_name ( * comm, name, &len );
  MPI_Comm_size ( * comm, &size );
  MPI_Comm_rank ( * comm, &rank );
  MPI_Comm_rank ( MPI_COMM_WORLD, &rankGlob );
  fprintf ( stdout,
            "pe%d in %s, %s, line %d: comm: name=%s, size=%d, rank=%d\n",
            rankGlob, functionname, filename, line,
            name, size, rank );
  free ( name );

}
#endif

/****************************************************/

#ifdef USE_MPI
void pcdiDebugMsg ( const char * cdiPioDebugString, const char *filename,
                    const char *functionname, int line, int tag, int source,
                    int nfinished )
{
  int rank;

  MPI_Comm_rank ( MPI_COMM_WORLD, &rank );

  fprintf ( stdout,
            "%s pe%d in %s, %s, line %d: command %s, source %d, finalized=%d\n",
            cdiPioDebugString, rank, functionname, filename, line,
            &commands[tag][0], source, nfinished );
}
#endif
/****************************************************/

#ifdef USE_MPI
void pcdiDebugMsg2 ( const char *filename, const char *functionname, int line,
                   int tag, int source, char * text )
{
  int rank;

  MPI_Comm_rank ( MPI_COMM_WORLD, &rank );

  fprintf ( stdout,
            "pe%d in %s, %s, line %d: command %s, source %d, %s\n",
            rank, functionname, filename, line,
            &commands[tag][0], source, text );
}
#endif


/****************************************************/


int xmaxInt ( int a, int b )
{
  return a >= b ? a : b;
}


/****************************************************/


int xminInt ( int a, int b )
{
  return a <= b ? a : b;
}


/****************************************************/


int xsum ( int n, int * argarray )
{
  int i, sum = 0;

  for ( i = 0; i < n; i++ )
    sum += * ( argarray + i );

  return sum;
}


/****************************************************/


double xchecksum ( int type, int count, void * buffer )
{
  return 0.0;
}


/****************************************************/

void printArray ( const char * cdiPioDebugString, char * ps, const void * array, int n,
                  int datatype, const char * funname, const char * filename, int line )
{
  int i, rank;
  int * iArray;
  double * dArray;

#ifdef USE_MPI
  {
    int init_flag;
    if (MPI_Initialized(&init_flag) == MPI_SUCCESS && init_flag)
      MPI_Comm_rank ( MPI_COMM_WORLD, &rank );
    else
      rank = -1;
  }
  fprintf ( stdout, "%s pe%d in %s, %s, line %d: %s = ",
	    cdiPioDebugString, rank, funname, filename, line, ps );
#else
  fprintf ( stdout, "%s %s, %s, line %d: %s = ",
	    cdiPioDebugString, funname, filename, line, ps );
#endif

  switch ( datatype )
    {
    case DATATYPE_INT:
      iArray = ( int * ) array;
      for ( i = 0; i < n-1; i++ )
	fprintf ( stdout, "%d ", * ( iArray + i ));
      fprintf ( stdout, "%d\n", * ( iArray + n - 1 ));
      break;
    case DATATYPE_FLT:
      dArray = ( double * ) array;
      for ( i = 0; i < n-1; i++ )
	fprintf ( stdout, "%.2f ", * ( dArray + i ));
      fprintf ( stdout, "%.2f\n", * ( dArray + n-1 ));
      break;
    default:
      fprintf ( stdout, " ... no datatype defined\n" );
    }

  return;
}

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