#include <petsc/private/ftnimpl.h>
#include <petscsys.h>
#include <petscviewer.h>

#if defined(PETSC_HAVE_FORTRAN_CAPS)
  #define petscpusherrorhandler_           PETSCPUSHERRORHANDLER
  #define petsctracebackerrorhandler_      PETSCTRACEBACKERRORHANDLER
  #define petscaborterrorhandler_          PETSCABORTERRORHANDLER
  #define petscreturnerrorhandler_         PETSCRETURNERRORHANDLER
  #define petscemacsclienterrorhandler_    PETSCEMACSCLIENTERRORHANDLER
  #define petscattachdebuggererrorhandler_ PETSCATTACHDEBUGGERERRORHANDLER
  #define petscerror_                      PETSCERROR
  #define petscerrorf_                     PETSCERRORF
  #define petscerrormpi_                   PETSCERRORMPI
#elif !defined(PETSC_HAVE_FORTRAN_UNDERSCORE)
  #define petscpusherrorhandler_           petscpusherrorhandler
  #define petsctracebackerrorhandler_      petsctracebackerrorhandler
  #define petscaborterrorhandler_          petscaborterrorhandler
  #define petscreturnerrorhandler_         petscreturnerrorhandler
  #define petscemacsclienterrorhandler_    petscemacsclienterrorhandler
  #define petscattachdebuggererrorhandler_ petscattachdebuggererrorhandler
  #define petscerror_                      petscerror
  #define petscerrorf_                     petscerrorf
  #define petscerrormpi_                   petscerrormpi
#endif

static void (*f2)(MPI_Comm *comm, int *, const char *, const char *, PetscErrorCode *, PetscErrorType *, const char *, void *, PetscErrorCode *, PETSC_FORTRAN_CHARLEN_T len1, PETSC_FORTRAN_CHARLEN_T len2, PETSC_FORTRAN_CHARLEN_T len3);

/* These are not extern C because they are passed into non-extern C user level functions */
static PetscErrorCode ourerrorhandler(MPI_Comm comm, int line, const char *fun, const char *file, PetscErrorCode n, PetscErrorType p, const char *mess, PetscCtx ctx)
{
  PetscErrorCode ierr = PETSC_SUCCESS;
  size_t         len1, len2, len3;

  ierr = PetscStrlen(fun, &len1);
  ierr = PetscStrlen(file, &len2);
  ierr = PetscStrlen(mess, &len3);

  ierr = PETSC_SUCCESS;
  (*f2)(&comm, &line, fun, file, &n, &p, mess, ctx, &ierr, (PETSC_FORTRAN_CHARLEN_T)len1, (PETSC_FORTRAN_CHARLEN_T)len2, (PETSC_FORTRAN_CHARLEN_T)len3);
  return ierr;
}

/*
        These are not usually called from Fortran but allow Fortran users
   to transparently set these monitors from .F code
*/
PETSC_EXTERN void petsctracebackerrorhandler_(MPI_Comm *comm, int *line, const char *fun, const char *file, PetscErrorCode *n, PetscErrorType *p, const char *mess, PetscCtx ctx, PetscErrorCode *ierr);
PETSC_EXTERN void petscaborterrorhandler_(MPI_Comm *comm, int *line, const char *fun, const char *file, PetscErrorCode *n, PetscErrorType *p, const char *mess, PetscCtx ctx, PetscErrorCode *ierr);
PETSC_EXTERN void petscattachdebuggererrorhandler_(MPI_Comm *comm, int *line, const char *fun, const char *file, PetscErrorCode *n, PetscErrorType *p, const char *mess, PetscCtx ctx, PetscErrorCode *ierr);
PETSC_EXTERN void petscemacsclienterrorhandler_(MPI_Comm *comm, int *line, const char *fun, const char *file, PetscErrorCode *n, PetscErrorType *p, const char *mess, PetscCtx ctx, PetscErrorCode *ierr);
PETSC_EXTERN void petscreturnerrorhandler_(MPI_Comm *comm, int *line, const char *fun, const char *file, PetscErrorCode *n, PetscErrorType *p, const char *mess, PetscCtx ctx, PetscErrorCode *ierr);

PETSC_EXTERN void petscpusherrorhandler_(void (*handler)(MPI_Comm *, int *, const char *, const char *, PetscErrorCode *, PetscErrorType *, const char *, void *, PetscErrorCode *, PETSC_FORTRAN_CHARLEN_T len1, PETSC_FORTRAN_CHARLEN_T len2, PETSC_FORTRAN_CHARLEN_T len3), PetscCtx ctx, PetscErrorCode *ierr)
{
  if ((PetscFortranCallbackFn *)handler == (PetscFortranCallbackFn *)petsctracebackerrorhandler_) *ierr = PetscPushErrorHandler(PetscTraceBackErrorHandler, NULL);
  else {
    f2    = handler;
    *ierr = PetscPushErrorHandler(ourerrorhandler, ctx);
  }
}

PETSC_EXTERN void petscerror_(MPI_Fint *comm, PetscErrorCode *number, PetscErrorType *p, char *message, PETSC_FORTRAN_CHARLEN_T len)
{
  PetscErrorCode nierr, *ierr = &nierr;
  char          *t1;
  FIXCHAR(message, len, t1);
  nierr = PetscError(MPI_Comm_f2c(*(comm)), 0, NULL, NULL, *number, *p, "%s", t1);
  FREECHAR(message, t1);
}

#if defined(PETSC_HAVE_FORTRAN_FREE_LINE_LENGTH_NONE)
PETSC_EXTERN void petscerrorf_(PetscErrorCode *err, int *line, char *file, PETSC_FORTRAN_CHARLEN_T len)
{
  char          *tfile;
  PetscErrorCode ierr[] = {PETSC_SUCCESS}; /* needed by FIXCHAR */

  FIXCHAR(file, len, tfile);
  *err = PetscError(PETSC_COMM_SELF, *line, NULL, tfile, *err, PETSC_ERROR_REPEAT, NULL);
  FREECHAR(file, tfile);
}

PETSC_EXTERN void petscerrormpi_(PetscErrorCode *err, int *line, char *file, PETSC_FORTRAN_CHARLEN_T len)
{
  char           errorstring[2 * MPI_MAX_ERROR_STRING];
  char          *tfile;
  PetscErrorCode ierr[] = {PETSC_SUCCESS}; /* needed by FIXCHAR */

  FIXCHAR(file, len, tfile);
  PetscMPIErrorString(*err, 2 * MPI_MAX_ERROR_STRING, errorstring);
  *err = PetscError(PETSC_COMM_SELF, *line, NULL, file, PETSC_ERR_MPI, PETSC_ERROR_INITIAL, "MPI error %d %s", *err, errorstring);
  FREECHAR(file, tfile);
  *err = PETSC_ERR_MPI;
}
#else
PETSC_EXTERN void petscerrorf_(PetscErrorCode *err)
{
  *err = PetscError(PETSC_COMM_SELF, 0, NULL, NULL, *err, PETSC_ERROR_REPEAT, NULL);
}

PETSC_EXTERN void petscerrormpi_(PetscErrorCode *err)
{
  char errorstring[2 * MPI_MAX_ERROR_STRING];

  PetscMPIErrorString(*err, 2 * MPI_MAX_ERROR_STRING, errorstring);
  *err = PetscError(PETSC_COMM_SELF, 0, NULL, NULL, PETSC_ERR_MPI, PETSC_ERROR_INITIAL, "MPI error %d %s", *err, errorstring);
  *err = PETSC_ERR_MPI;
}
#endif
