#include <petsc/private/fortranimpl.h>
#include <petscsnes.h>

#if defined(PETSC_HAVE_FORTRAN_CAPS)
  #define sneslinesearchgettype_      SNESLINESEARCHGETTYPE
  #define sneslinesearchsettype_      SNESLINESEARCHSETTYPE
  #define sneslinesearchsetprecheck_  SNESLINESEARCHSETPRECHECK
  #define sneslinesearchgetprecheck_  SNESLINESEARCHGETPRECHECK
  #define sneslinesearchsetpostcheck_ SNESLINESEARCHSETPOSTCHECK
  #define sneslinesearchgetpostcheck_ SNESLINESEARCHGETPOSTCHECK
#elif !defined(PETSC_HAVE_FORTRAN_UNDERSCORE)
  #define sneslinesearchgettype_      sneslinesearchgettype
  #define sneslinesearchsettype_      sneslinesearchsettype
  #define sneslinesearchsetprecheck_  sneslinesearchsetprecheck
  #define sneslinesearchgetprecheck_  sneslinesearchgetprecheck
  #define sneslinesearchsetpostcheck_ sneslinesearchsetpostcheck
  #define sneslinesearchgetpostcheck_ sneslinesearchgetpostcheck

#endif

/* fortranpointers go: shell, precheck, postcheck */

static PetscErrorCode oursneslinesearchprecheck(SNESLineSearch linesearch, Vec X, Vec Y, PetscBool *changed, void *ctx)
{
  PetscFunctionBegin;
  PetscCallFortranVoidFunction((*(void (*)(SNESLineSearch *, Vec *, Vec *, PetscBool *, void *, PetscErrorCode *))(((PetscObject)linesearch)->fortran_func_pointers[1]))(&linesearch, &X, &Y, changed, ctx, &ierr));
  PetscFunctionReturn(PETSC_SUCCESS);
}

static PetscErrorCode oursneslinesearchpostcheck(SNESLineSearch linesearch, Vec X, Vec Y, Vec W, PetscBool *changed_Y, PetscBool *changed_W, void *ctx)
{
  PetscFunctionBegin;
  PetscCallFortranVoidFunction((*(void (*)(SNESLineSearch *, Vec *, Vec *, Vec *, PetscBool *, PetscBool *, void *, PetscErrorCode *))(((PetscObject)linesearch)->fortran_func_pointers[2]))(&linesearch, &X, &Y, &W, changed_Y, changed_W, ctx, &ierr));
  PetscFunctionReturn(PETSC_SUCCESS);
}

PETSC_EXTERN void sneslinesearchgettype_(SNESLineSearch *linesearch, char *name, PetscErrorCode *ierr, PETSC_FORTRAN_CHARLEN_T len)
{
  const char *tname;

  *ierr = SNESLineSearchGetType(*linesearch, &tname);
  *ierr = PetscStrncpy(name, tname, len);
  if (*ierr) return;
  FIXRETURNCHAR(PETSC_TRUE, name, len);
}

PETSC_EXTERN void sneslinesearchsettype_(SNESLineSearch *linesearch, char *type, PetscErrorCode *ierr, PETSC_FORTRAN_CHARLEN_T len)
{
  char *t;

  FIXCHAR(type, len, t);
  *ierr = SNESLineSearchSetType(*linesearch, t);
  if (*ierr) return;
  FREECHAR(type, t);
}

PETSC_EXTERN void sneslinesearchsetprecheck_(SNESLineSearch *linesearch, void (*func)(SNESLineSearch *, Vec *, Vec *, PetscBool *, PetscErrorCode *), void *ctx, PetscErrorCode *ierr)
{
  PetscObjectAllocateFortranPointers(*linesearch, 3);
  ((PetscObject)*linesearch)->fortran_func_pointers[1] = (PetscVoidFn *)func;

  *ierr = SNESLineSearchSetPreCheck(*linesearch, oursneslinesearchprecheck, ctx);
}

PETSC_EXTERN void sneslinesearchsetpostcheck_(SNESLineSearch *linesearch, void (*func)(SNESLineSearch *, Vec *, Vec *, Vec *, PetscBool *, PetscBool *, PetscErrorCode *, void *), void *ctx, PetscErrorCode *ierr)
{
  PetscObjectAllocateFortranPointers(*linesearch, 3);
  ((PetscObject)*linesearch)->fortran_func_pointers[2] = (PetscVoidFn *)func;

  *ierr = SNESLineSearchSetPostCheck(*linesearch, oursneslinesearchpostcheck, ctx);
}
