#include <petsc/private/viewerimpl.h> /*I "petscviewer.h" I*/
#include <petsc/private/hashtable.h>
#if defined(PETSC_HAVE_SAWS)
  #include <petscviewersaws.h>
#endif

PetscFunctionList PetscViewerList = NULL;

PetscOptionsHelpPrinted PetscOptionsHelpPrintedSingleton = NULL;
KHASH_SET_INIT_STR(HTPrinted)
struct _n_PetscOptionsHelpPrinted {
  khash_t(HTPrinted) *printed;
  PetscSegBuffer      strings;
};

PetscErrorCode PetscOptionsHelpPrintedDestroy(PetscOptionsHelpPrinted *hp)
{
  PetscFunctionBegin;
  if (!*hp) PetscFunctionReturn(PETSC_SUCCESS);
  kh_destroy(HTPrinted, (*hp)->printed);
  PetscCall(PetscSegBufferDestroy(&(*hp)->strings));
  PetscCall(PetscFree(*hp));
  PetscFunctionReturn(PETSC_SUCCESS);
}

/*@C
  PetscOptionsHelpPrintedCreate - Creates an object used to manage tracking which help messages have
  been printed so they will not be printed again.

  Output Parameter:
. hp - the created object

  Not Collective

  Level: developer

.seealso: `PetscOptionsHelpPrintedCheck()`, `PetscOptionsHelpPrintChecked()`
@*/
PetscErrorCode PetscOptionsHelpPrintedCreate(PetscOptionsHelpPrinted *hp)
{
  PetscFunctionBegin;
  PetscCall(PetscNew(hp));
  (*hp)->printed = kh_init(HTPrinted);
  PetscCall(PetscSegBufferCreate(sizeof(char), 10000, &(*hp)->strings));
  PetscFunctionReturn(PETSC_SUCCESS);
}

/*@C
  PetscOptionsHelpPrintedCheck - Checks if a particular pre, name pair has previous been entered (meaning the help message was printed)

  Not Collective

  Input Parameters:
+ hp   - the object used to manage tracking what help messages have been printed
. pre  - the prefix part of the string, many be `NULL`
- name - the string to look for (cannot be `NULL`)

  Output Parameter:
. found - `PETSC_TRUE` if the string was already set

  Level: intermediate

.seealso: `PetscOptionsHelpPrintedCreate()`
@*/
PetscErrorCode PetscOptionsHelpPrintedCheck(PetscOptionsHelpPrinted hp, const char *pre, const char *name, PetscBool *found)
{
  size_t l1, l2;
#if !defined(PETSC_HAVE_THREADSAFETY)
  char *both;
  int   newitem;
#endif

  PetscFunctionBegin;
  PetscCall(PetscStrlen(pre, &l1));
  PetscCall(PetscStrlen(name, &l2));
  if (l1 + l2 == 0) {
    *found = PETSC_FALSE;
    PetscFunctionReturn(PETSC_SUCCESS);
  }
#if !defined(PETSC_HAVE_THREADSAFETY)
  size_t lboth = l1 + l2 + 1;
  PetscCall(PetscSegBufferGet(hp->strings, lboth, &both));
  PetscCall(PetscStrncpy(both, pre, lboth));
  PetscCall(PetscStrncpy(both + l1, name, l2 + 1));
  kh_put(HTPrinted, hp->printed, both, &newitem);
  if (!newitem) PetscCall(PetscSegBufferUnuse(hp->strings, lboth));
  *found = newitem ? PETSC_FALSE : PETSC_TRUE;
#else
  *found = PETSC_FALSE;
#endif
  PetscFunctionReturn(PETSC_SUCCESS);
}

static PetscBool noviewer = PETSC_FALSE;
static PetscBool noviewers[PETSCVIEWERGETVIEWEROFFPUSHESMAX];
static PetscInt  inoviewers = 0;

/*@
  PetscOptionsPushGetViewerOff - sets if a `PetscOptionsGetViewer()` returns a viewer.

  Logically Collective

  Input Parameter:
. flg - `PETSC_TRUE` to turn off viewer creation, `PETSC_FALSE` to turn it on.

  Level: developer

  Note:
  Calling `XXXViewFromOptions` in an inner loop can be expensive.  This can appear, for example, when using
  many small subsolves.  Call this function to control viewer creation in `PetscOptionsGetViewer()`, thus removing the expensive `XXXViewFromOptions` calls.

  Developer Notes:
  Instead of using this approach, the calls to `PetscOptionsGetViewer()` can be moved into `XXXSetFromOptions()`

.seealso: [](sec_viewers), `PetscOptionsGetViewer()`, `PetscOptionsPopGetViewerOff()`
@*/
PetscErrorCode PetscOptionsPushGetViewerOff(PetscBool flg)
{
  PetscFunctionBegin;
  PetscCheck(inoviewers < PETSCVIEWERGETVIEWEROFFPUSHESMAX, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Too many PetscOptionsPushGetViewerOff(), perhaps you forgot PetscOptionsPopGetViewerOff()?");

  noviewers[inoviewers++] = noviewer;
  noviewer                = flg;
  PetscFunctionReturn(PETSC_SUCCESS);
}

/*@
  PetscOptionsPopGetViewerOff - reset whether `PetscOptionsGetViewer()` returns a viewer.

  Logically Collective

  Level: developer

  Note:
  See `PetscOptionsPushGetViewerOff()`

.seealso: [](sec_viewers), `PetscOptionsGetViewer()`, `PetscOptionsPushGetViewerOff()`
@*/
PetscErrorCode PetscOptionsPopGetViewerOff(void)
{
  PetscFunctionBegin;
  PetscCheck(inoviewers, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Too many PetscOptionsPopGetViewerOff(), perhaps you forgot PetscOptionsPushGetViewerOff()?");
  noviewer = noviewers[--inoviewers];
  PetscFunctionReturn(PETSC_SUCCESS);
}

/*@
  PetscOptionsGetViewerOff - does `PetscOptionsGetViewer()` return a viewer?

  Logically Collective

  Output Parameter:
. flg - whether viewers are returned.

  Level: developer

  Note:
  See `PetscOptionsPushGetViewerOff()`

.seealso: [](sec_viewers), `PetscOptionsGetViewer()`, `PetscOptionsPushGetViewerOff()`, `PetscOptionsPopGetViewerOff()`
@*/
PetscErrorCode PetscOptionsGetViewerOff(PetscBool *flg)
{
  PetscFunctionBegin;
  PetscAssertPointer(flg, 1);
  *flg = noviewer;
  PetscFunctionReturn(PETSC_SUCCESS);
}

static PetscErrorCode PetscOptionsGetViewers_Single(MPI_Comm comm, const char value[], PetscViewer *viewer, PetscViewerFormat *format)
{
  char       *loc0_vtype = NULL, *loc1_fname = NULL, *loc2_fmt = NULL, *loc3_fmode = NULL;
  PetscInt    cnt;
  size_t      viewer_string_length;
  const char *viewers[] = {PETSCVIEWERASCII, PETSCVIEWERBINARY, PETSCVIEWERDRAW, PETSCVIEWERSOCKET, PETSCVIEWERMATLAB, PETSCVIEWERSAWS, PETSCVIEWERVTK, PETSCVIEWERHDF5, PETSCVIEWERGLVIS, PETSCVIEWEREXODUSII, NULL};

  PetscFunctionBegin;
  PetscCall(PetscStrlen(value, &viewer_string_length));
  if (!viewer_string_length) {
    if (format) *format = PETSC_VIEWER_DEFAULT;
    if (viewer) PetscCall(PetscViewerASCIIGetStdout(comm, viewer));
    PetscFunctionReturn(PETSC_SUCCESS);
  }

  PetscCall(PetscStrallocpy(value, &loc0_vtype));
  PetscCall(PetscStrchr(loc0_vtype, ':', &loc1_fname));
  if (loc1_fname) {
    *loc1_fname++ = 0;
    PetscCall(PetscStrchr(loc1_fname, ':', &loc2_fmt));
  }
  if (loc2_fmt) {
    *loc2_fmt++ = 0;
    PetscCall(PetscStrchr(loc2_fmt, ':', &loc3_fmode));
  }
  if (loc3_fmode) *loc3_fmode++ = 0;
  PetscCall(PetscStrendswithwhich(*loc0_vtype ? loc0_vtype : "ascii", viewers, &cnt));
  PetscCheck(cnt <= (PetscInt)sizeof(viewers) - 1, comm, PETSC_ERR_ARG_OUTOFRANGE, "Unknown viewer type: %s", loc0_vtype);
  if (viewer) {
    if (!loc1_fname) {
      switch (cnt) {
      case 0:
        PetscCall(PetscViewerASCIIGetStdout(comm, viewer));
        break;
      case 1:
        if (!(*viewer = PETSC_VIEWER_BINARY_(comm))) PetscCall(PETSC_ERR_PLIB);
        break;
      case 2:
        if (!(*viewer = PETSC_VIEWER_DRAW_(comm))) PetscCall(PETSC_ERR_PLIB);
        break;
#if defined(PETSC_USE_SOCKET_VIEWER)
      case 3:
        if (!(*viewer = PETSC_VIEWER_SOCKET_(comm))) PetscCall(PETSC_ERR_PLIB);
        break;
#endif
#if defined(PETSC_HAVE_MATLAB)
      case 4:
        if (!(*viewer = PETSC_VIEWER_MATLAB_(comm))) PetscCall(PETSC_ERR_PLIB);
        break;
#endif
#if defined(PETSC_HAVE_SAWS)
      case 5:
        if (!(*viewer = PETSC_VIEWER_SAWS_(comm))) PetscCall(PETSC_ERR_PLIB);
        break;
#endif
#if defined(PETSC_HAVE_HDF5)
      case 7:
        if (!(*viewer = PETSC_VIEWER_HDF5_(comm))) PetscCall(PETSC_ERR_PLIB);
        break;
#endif
      case 8:
        if (!(*viewer = PETSC_VIEWER_GLVIS_(comm))) PetscCall(PETSC_ERR_PLIB);
        break;
#if defined(PETSC_HAVE_EXODUSII)
      case 9:
        if (!(*viewer = PETSC_VIEWER_EXODUSII_(comm))) PetscCall(PETSC_ERR_PLIB);
        break;
#endif
      default:
        SETERRQ(comm, PETSC_ERR_SUP, "Unsupported viewer %s", loc0_vtype);
      }
    } else {
      if (loc2_fmt && !*loc1_fname && (cnt == 0)) { /* ASCII format without file name */
        PetscCall(PetscViewerASCIIGetStdout(comm, viewer));
      } else {
        PetscFileMode fmode;
        PetscBool     flag = PETSC_FALSE;

        PetscCall(PetscViewerCreate(comm, viewer));
        PetscCall(PetscViewerSetType(*viewer, *loc0_vtype ? loc0_vtype : "ascii"));
        fmode = FILE_MODE_WRITE;
        if (loc3_fmode && *loc3_fmode) { /* Has non-empty file mode ("write" or "append") */
          PetscCall(PetscEnumFind(PetscFileModes, loc3_fmode, (PetscEnum *)&fmode, &flag));
          PetscCheck(flag, comm, PETSC_ERR_ARG_UNKNOWN_TYPE, "Unknown file mode: %s", loc3_fmode);
        }
        if (loc2_fmt) {
          PetscBool tk, im;
          PetscCall(PetscStrcmp(loc1_fname, "tikz", &tk));
          PetscCall(PetscStrcmp(loc1_fname, "image", &im));
          if (tk || im) {
            PetscCall(PetscViewerDrawSetInfo(*viewer, NULL, loc2_fmt, PETSC_DECIDE, PETSC_DECIDE, PETSC_DECIDE, PETSC_DECIDE));
            *loc2_fmt = 0;
          }
        }
        PetscCall(PetscViewerFileSetMode(*viewer, flag ? fmode : FILE_MODE_WRITE));
        PetscCall(PetscViewerFileSetName(*viewer, loc1_fname));
        if (*loc1_fname) PetscCall(PetscViewerDrawSetDrawType(*viewer, loc1_fname));
        PetscCall(PetscViewerSetFromOptions(*viewer));
      }
    }
  }
  if (viewer) PetscCall(PetscViewerSetUp(*viewer));
  if (loc2_fmt && *loc2_fmt) {
    PetscViewerFormat tfmt;
    PetscBool         flag;

    PetscCall(PetscEnumFind(PetscViewerFormats, loc2_fmt, (PetscEnum *)&tfmt, &flag));
    if (format) *format = tfmt;
    PetscCheck(flag, comm, PETSC_ERR_SUP, "Unknown viewer format %s", loc2_fmt);
  } else if (viewer && (cnt == 6) && format) { /* Get format from VTK viewer */
    PetscCall(PetscViewerGetFormat(*viewer, format));
  }
  PetscCall(PetscFree(loc0_vtype));
  PetscFunctionReturn(PETSC_SUCCESS);
}

static PetscErrorCode PetscOptionsGetViewers_Internal(MPI_Comm comm, PetscOptions options, const char pre[], const char name[], PetscInt *n_max_p, PetscViewer viewer[], PetscViewerFormat format[], PetscBool *set, const char func_name[], PetscBool allow_multiple)
{
  const char *value;
  PetscBool   flag, hashelp;
  PetscInt    n_max;

  PetscFunctionBegin;
  PetscAssertPointer(name, 4);
  PetscAssertPointer(n_max_p, 5);
  n_max = *n_max_p;
  PetscCheck(n_max >= 0, comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid size %" PetscInt_FMT " of passed arrays", *n_max_p);
  *n_max_p = 0;

  if (set) *set = PETSC_FALSE;
  PetscCall(PetscOptionsGetViewerOff(&flag));
  if (flag) PetscFunctionReturn(PETSC_SUCCESS);

  PetscCall(PetscOptionsHasHelp(NULL, &hashelp));
  if (hashelp) {
    PetscBool found;

    if (!PetscOptionsHelpPrintedSingleton) PetscCall(PetscOptionsHelpPrintedCreate(&PetscOptionsHelpPrintedSingleton));
    PetscCall(PetscOptionsHelpPrintedCheck(PetscOptionsHelpPrintedSingleton, pre, name, &found));
    if (!found && viewer) {
      PetscCall((*PetscHelpPrintf)(comm, "----------------------------------------\nViewer (-%s%s) options:\n", pre ? pre : "", name + 1));
      PetscCall((*PetscHelpPrintf)(comm, "  -%s%s ascii[:[filename][:[format][:append]]]: %s (%s)\n", pre ? pre : "", name + 1, "Prints object to stdout or ASCII file", func_name));
      PetscCall((*PetscHelpPrintf)(comm, "  -%s%s binary[:[filename][:[format][:append]]]: %s (%s)\n", pre ? pre : "", name + 1, "Saves object to a binary file", func_name));
      PetscCall((*PetscHelpPrintf)(comm, "  -%s%s draw[:[drawtype][:filename|format]] %s (%s)\n", pre ? pre : "", name + 1, "Draws object", func_name));
      PetscCall((*PetscHelpPrintf)(comm, "  -%s%s socket[:port]: %s (%s)\n", pre ? pre : "", name + 1, "Pushes object to a Unix socket", func_name));
      PetscCall((*PetscHelpPrintf)(comm, "  -%s%s saws[:communicatorname]: %s (%s)\n", pre ? pre : "", name + 1, "Publishes object to SAWs", func_name));
      if (allow_multiple) PetscCall((*PetscHelpPrintf)(comm, "  -%s%s v1[,v2,...]: %s (%s)\n", pre ? pre : "", name + 1, "Multiple viewers", func_name));
    }
  }

  PetscCall(PetscOptionsFindPair(options, pre, name, &value, &flag));
  if (flag) {
    if (set) *set = PETSC_TRUE;
    if (!value) {
      PetscCheck(n_max > 0, comm, PETSC_ERR_ARG_SIZ, "More viewers (1) than max available (0)");
      if (format) *format = PETSC_VIEWER_DEFAULT;
      if (viewer) PetscCall(PetscViewerASCIIGetStdout(comm, viewer));
      *n_max_p = 1;
    } else {
      char  *loc0_viewer_string = NULL, *this_viewer_string = NULL;
      size_t viewer_string_length;

      PetscCall(PetscStrallocpy(value, &loc0_viewer_string));
      PetscCall(PetscStrlen(loc0_viewer_string, &viewer_string_length));
      this_viewer_string = loc0_viewer_string;

      do {
        PetscViewer       *this_viewer;
        PetscViewerFormat *this_viewer_format;
        char              *next_viewer_string = NULL;
        char              *comma_separator    = NULL;
        PetscInt           n                  = *n_max_p;

        PetscCheck(n < n_max, comm, PETSC_ERR_PLIB, "More viewers than max available (%d)", (int)n_max);

        PetscCall(PetscStrchr(this_viewer_string, ',', &comma_separator));
        if (comma_separator) {
          PetscCheck(allow_multiple, comm, PETSC_ERR_ARG_OUTOFRANGE, "Trying to pass multiple viewers to %s: only one allowed.  Use PetscOptionsGetViewers() instead", func_name);
          *comma_separator   = 0;
          next_viewer_string = comma_separator + 1;
        }
        this_viewer = PetscSafePointerPlusOffset(viewer, n);
        if (this_viewer) *this_viewer = NULL;
        this_viewer_format = PetscSafePointerPlusOffset(format, n);
        if (this_viewer_format) *this_viewer_format = PETSC_VIEWER_DEFAULT;
        PetscCall(PetscOptionsGetViewers_Single(comm, this_viewer_string, this_viewer, this_viewer_format));
        this_viewer_string = next_viewer_string;
        (*n_max_p)++;
      } while (this_viewer_string);
      PetscCall(PetscFree(loc0_viewer_string));
    }
  }
  PetscFunctionReturn(PETSC_SUCCESS);
}

/*@C
  PetscOptionsGetViewer - Gets a viewer appropriate for the type indicated by the user

  Collective

  Input Parameters:
+ comm    - the communicator to own the viewer
. options - options database, use `NULL` for default global database
. pre     - the string to prepend to the name or `NULL`
- name    - the options database name that will be checked for

  Output Parameters:
+ viewer - the viewer, pass `NULL` if not needed
. format - the `PetscViewerFormat` requested by the user, pass `NULL` if not needed
- set    - `PETSC_TRUE` if found, else `PETSC_FALSE`

  Level: intermediate

  Notes:
  If no value is provided ascii:stdout is used
+       ascii[:[filename][:[format][:append]]]  -  defaults to stdout - format can be one of ascii_info, ascii_info_detail, or ascii_matlab,
  for example ascii::ascii_info prints just the information about the object not all details
  unless :append is given filename opens in write mode, overwriting what was already there
.       binary[:[filename][:[format][:append]]] -  defaults to the file binaryoutput
.       draw[:drawtype[:filename]]              -  for example, draw:tikz, draw:tikz:figure.tex  or draw:x
.       socket[:port]                           -  defaults to the standard output port
-       saws[:communicatorname]                 -   publishes object to the Scientific Application Webserver (SAWs)

  Use `PetscOptionsRestoreViewer()` after using the viewer, do not use `PetscViewerDestroy()`

  You can control whether calls to this function create a viewer (or return early with *set of `PETSC_FALSE`) with
  `PetscOptionsPushGetViewerOff()`.  This is useful if calling many small subsolves, in which case XXXViewFromOptions can take
  an appreciable fraction of the runtime.

  If PETSc is configured with `--with-viewfromoptions=0` this function always returns with *set of `PETSC_FALSE`

  This routine is thread-safe for accessing predefined `PetscViewer`s like `PETSC_VIEWER_STDOUT_SELF` but not for accessing
  files by name.

.seealso: [](sec_viewers), `PetscOptionsRestoreViewer()`, `PetscOptionsGetReal()`, `PetscOptionsHasName()`, `PetscOptionsGetString()`,
          `PetscOptionsGetIntArray()`, `PetscOptionsGetRealArray()`, `PetscOptionsBool()`
          `PetscOptionsInt()`, `PetscOptionsString()`, `PetscOptionsReal()`,
          `PetscOptionsName()`, `PetscOptionsBegin()`, `PetscOptionsEnd()`, `PetscOptionsHeadBegin()`,
          `PetscOptionsStringArray()`, `PetscOptionsRealArray()`, `PetscOptionsScalar()`,
          `PetscOptionsBoolGroupBegin()`, `PetscOptionsBoolGroup()`, `PetscOptionsBoolGroupEnd()`,
          `PetscOptionsFList()`, `PetscOptionsEList()`, `PetscOptionsPushGetViewerOff()`, `PetscOptionsPopGetViewerOff()`,
          `PetscOptionsGetViewerOff()`
@*/
PetscErrorCode PetscOptionsGetViewer(MPI_Comm comm, PetscOptions options, const char pre[], const char name[], PetscViewer *viewer, PetscViewerFormat *format, PetscBool *set)
{
  PetscInt  n_max = 1;
  PetscBool set_internal;

  PetscFunctionBegin;
  if (viewer) *viewer = NULL;
  if (format) *format = PETSC_VIEWER_DEFAULT;
  PetscCall(PetscOptionsGetViewers_Internal(comm, options, pre, name, &n_max, viewer, format, &set_internal, PETSC_FUNCTION_NAME, PETSC_FALSE));
  if (set_internal) PetscAssert(n_max == 1, comm, PETSC_ERR_PLIB, "Unexpected: %d != 1 viewers set", (int)n_max);
  if (set) *set = set_internal;
  PetscFunctionReturn(PETSC_SUCCESS);
}

/*@C
  PetscOptionsRestoreViewer - restore a `PetscViewer` obtained from `PetscOptionsGetViewer()` or `PetscOptionsGetViewers()`

  Collective

  Input Parameter:
. viewer - the viewer

  Level: intermediate

  Developer Note:
  This function exists to handle persistent viewers such as `PETSC_VIEWER_STDOUT_SELF` that may be shared between threads where
  changing the reference count would not be thread safe.

.seealso: [](sec_viewers), `PetscOptionsGetViewer()`, `PetscOptionsGetReal()`, `PetscOptionsHasName()`, `PetscOptionsGetString()`,
          `PetscOptionsGetIntArray()`, `PetscOptionsGetRealArray()`, `PetscOptionsBool()`
          `PetscOptionsInt()`, `PetscOptionsString()`, `PetscOptionsReal()`,
          `PetscOptionsName()`, `PetscOptionsBegin()`, `PetscOptionsEnd()`, `PetscOptionsHeadBegin()`,
          `PetscOptionsStringArray()`, `PetscOptionsRealArray()`, `PetscOptionsScalar()`,
          `PetscOptionsBoolGroupBegin()`, `PetscOptionsBoolGroup()`, `PetscOptionsBoolGroupEnd()`,
          `PetscOptionsFList()`, `PetscOptionsEList()`, `PetscOptionsPushGetViewerOff()`, `PetscOptionsPopGetViewerOff()`,
          `PetscOptionsGetViewerOff()`
@*/
PetscErrorCode PetscOptionsRestoreViewer(PetscViewer *viewer)
{
  PetscFunctionBegin;
  if (!*viewer) PetscFunctionReturn(PETSC_SUCCESS);
  if (!((PetscObject)*viewer)->persistent) PetscCall(PetscViewerDestroy(viewer));
  else *viewer = NULL;
  PetscFunctionReturn(PETSC_SUCCESS);
}

/*@C
  PetscOptionsGetViewers - Get multiple viewers from a comma-separated list in the options database

  Collective

  Input Parameters:
+ comm    - the communicator to own the viewers
. options - options database, use `NULL` for default global database
. pre     - the string to prepend to the name or `NULL`
. name    - the options database name that will be checked for
- n_max   - on input: the maximum number of viewers; on output: the number of viewers in the comma-separated list

  Output Parameters:
+ viewers - an array to hold at least `n_max` `PetscViewer`s, or `NULL` if not needed; on output: if not `NULL`, the
             first `n_max` entries are initialized `PetscViewer`s
. formats - an array to hold at least `n_max` `PetscViewerFormat`s, or `NULL` if not needed; on output: if not
             `NULL`, the first `n_max` entries are valid `PetscViewewFormat`s
- set     - `PETSC_TRUE` if found, else `PETSC_FALSE`

  Level: intermediate

  Note:
  See `PetscOptionsGetViewer()` for how the format strings for the viewers are interpreted.  Use `PetscOptionsRestoreViewer()` on each viewer, otherwise a memory leak will occur.

  If PETSc is configured with `--with-viewfromoptions=0` this function always returns with `n_max` of 0 and `set` of `PETSC_FALSE`

.seealso: [](sec_viewers), `PetscOptionsGetViewer()`
@*/
PetscErrorCode PetscOptionsGetViewers(MPI_Comm comm, PetscOptions options, const char pre[], const char name[], PetscInt *n_max, PetscViewer viewers[], PetscViewerFormat formats[], PetscBool *set)
{
  PetscFunctionBegin;
  PetscCall(PetscOptionsGetViewers_Internal(comm, options, pre, name, n_max, viewers, formats, set, PETSC_FUNCTION_NAME, PETSC_TRUE));
  PetscFunctionReturn(PETSC_SUCCESS);
}

/*@
  PetscViewerCreate - Creates a viewing context. A `PetscViewer` represents a file, a graphical window, a Unix socket or a variety of other ways
  of viewing a PETSc object

  Collective

  Input Parameter:
. comm - MPI communicator

  Output Parameter:
. inviewer - location to put the `PetscViewer` context

  Level: advanced

.seealso: [](sec_viewers), `PetscViewer`, `PetscViewerDestroy()`, `PetscViewerSetType()`, `PetscViewerType`
@*/
PetscErrorCode PetscViewerCreate(MPI_Comm comm, PetscViewer *inviewer)
{
  PetscViewer viewer;

  PetscFunctionBegin;
  *inviewer = NULL;
  PetscCall(PetscViewerInitializePackage());
  PetscCall(PetscHeaderCreate(viewer, PETSC_VIEWER_CLASSID, "PetscViewer", "PetscViewer", "Viewer", comm, PetscViewerDestroy, PetscViewerView));
  *inviewer    = viewer;
  viewer->data = NULL;
  PetscFunctionReturn(PETSC_SUCCESS);
}

/*@C
  PetscViewerSetType - Builds `PetscViewer` for a particular implementation.

  Collective

  Input Parameters:
+ viewer - the `PetscViewer` context obtained with `PetscViewerCreate()`
- type   - for example, `PETSCVIEWERASCII`

  Options Database Key:
. -viewer_type  <type> - Sets the type; use -help for a list of available methods (for instance, ascii)

  Level: advanced

  Note:
  See `PetscViewerType` for possible values

.seealso: [](sec_viewers), `PetscViewer`, `PetscViewerCreate()`, `PetscViewerGetType()`, `PetscViewerType`, `PetscViewerPushFormat()`
@*/
PetscErrorCode PetscViewerSetType(PetscViewer viewer, PetscViewerType type)
{
  PetscBool match;
  PetscErrorCode (*r)(PetscViewer);

  PetscFunctionBegin;
  PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 1);
  PetscAssertPointer(type, 2);
  PetscCall(PetscObjectTypeCompare((PetscObject)viewer, type, &match));
  if (match) PetscFunctionReturn(PETSC_SUCCESS);

  /* cleanup any old type that may be there */
  PetscTryTypeMethod(viewer, destroy);
  viewer->ops->destroy = NULL;
  viewer->data         = NULL;

  PetscCall(PetscMemzero(viewer->ops, sizeof(struct _PetscViewerOps)));

  PetscCall(PetscFunctionListFind(PetscViewerList, type, &r));
  PetscCheck(r, PetscObjectComm((PetscObject)viewer), PETSC_ERR_ARG_UNKNOWN_TYPE, "Unknown PetscViewer type given: %s", type);

  PetscCall(PetscObjectChangeTypeName((PetscObject)viewer, type));
  PetscCall((*r)(viewer));
  PetscFunctionReturn(PETSC_SUCCESS);
}

/*@C
  PetscViewerRegister - Adds a viewer to those available for use with `PetscViewerSetType()`

  Not Collective

  Input Parameters:
+ sname    - name of a new user-defined viewer
- function - routine to create method context

  Level: developer

  Note:
  `PetscViewerRegister()` may be called multiple times to add several user-defined viewers.

  Example Usage:
.vb
   PetscViewerRegister("my_viewer_type", MyViewerCreate);
.ve

  Then, your solver can be chosen with the procedural interface via
$     PetscViewerSetType(viewer, "my_viewer_type")
  or at runtime via the option
$     -viewer_type my_viewer_type

.seealso: [](sec_viewers), `PetscViewerRegisterAll()`
 @*/
PetscErrorCode PetscViewerRegister(const char *sname, PetscErrorCode (*function)(PetscViewer))
{
  PetscFunctionBegin;
  PetscCall(PetscViewerInitializePackage());
  PetscCall(PetscFunctionListAdd(&PetscViewerList, sname, function));
  PetscFunctionReturn(PETSC_SUCCESS);
}

/*@C
  PetscViewerSetFromOptions - Sets various options for a viewer based on values in the options database.

  Collective

  Input Parameter:
. viewer - the viewer context

  Level: intermediate

  Note:
  Must be called after `PetscViewerCreate()` but before the `PetscViewer` is used.

.seealso: [](sec_viewers), `PetscViewer`, `PetscViewerCreate()`, `PetscViewerSetType()`, `PetscViewerType`
@*/
PetscErrorCode PetscViewerSetFromOptions(PetscViewer viewer)
{
  char      vtype[256];
  PetscBool flg;

  PetscFunctionBegin;
  PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 1);

  if (!PetscViewerList) PetscCall(PetscViewerRegisterAll());
  PetscObjectOptionsBegin((PetscObject)viewer);
  PetscCall(PetscOptionsFList("-viewer_type", "Type of PetscViewer", "None", PetscViewerList, (char *)(((PetscObject)viewer)->type_name ? ((PetscObject)viewer)->type_name : PETSCVIEWERASCII), vtype, 256, &flg));
  if (flg) PetscCall(PetscViewerSetType(viewer, vtype));
  /* type has not been set? */
  if (!((PetscObject)viewer)->type_name) PetscCall(PetscViewerSetType(viewer, PETSCVIEWERASCII));
  PetscTryTypeMethod(viewer, setfromoptions, PetscOptionsObject);

  /* process any options handlers added with PetscObjectAddOptionsHandler() */
  PetscCall(PetscObjectProcessOptionsHandlers((PetscObject)viewer, PetscOptionsObject));
  PetscCall(PetscViewerViewFromOptions(viewer, NULL, "-viewer_view"));
  PetscOptionsEnd();
  PetscFunctionReturn(PETSC_SUCCESS);
}

PetscErrorCode PetscViewerFlowControlStart(PetscViewer viewer, PetscInt *mcnt, PetscInt *cnt)
{
  PetscFunctionBegin;
  PetscCall(PetscViewerBinaryGetFlowControl(viewer, mcnt));
  PetscCall(PetscViewerBinaryGetFlowControl(viewer, cnt));
  PetscFunctionReturn(PETSC_SUCCESS);
}

PetscErrorCode PetscViewerFlowControlStepMain(PetscViewer viewer, PetscInt i, PetscInt *mcnt, PetscInt cnt)
{
  MPI_Comm comm;

  PetscFunctionBegin;
  PetscCall(PetscObjectGetComm((PetscObject)viewer, &comm));
  if (i >= *mcnt) {
    *mcnt += cnt;
    PetscCallMPI(MPI_Bcast(mcnt, 1, MPIU_INT, 0, comm));
  }
  PetscFunctionReturn(PETSC_SUCCESS);
}

PetscErrorCode PetscViewerFlowControlEndMain(PetscViewer viewer, PetscInt *mcnt)
{
  MPI_Comm comm;

  PetscFunctionBegin;
  PetscCall(PetscObjectGetComm((PetscObject)viewer, &comm));
  *mcnt = 0;
  PetscCallMPI(MPI_Bcast(mcnt, 1, MPIU_INT, 0, comm));
  PetscFunctionReturn(PETSC_SUCCESS);
}

PetscErrorCode PetscViewerFlowControlStepWorker(PetscViewer viewer, PetscMPIInt rank, PetscInt *mcnt)
{
  MPI_Comm comm;

  PetscFunctionBegin;
  PetscCall(PetscObjectGetComm((PetscObject)viewer, &comm));
  while (PETSC_TRUE) {
    if (rank < *mcnt) break;
    PetscCallMPI(MPI_Bcast(mcnt, 1, MPIU_INT, 0, comm));
  }
  PetscFunctionReturn(PETSC_SUCCESS);
}

PetscErrorCode PetscViewerFlowControlEndWorker(PetscViewer viewer, PetscInt *mcnt)
{
  MPI_Comm comm;

  PetscFunctionBegin;
  PetscCall(PetscObjectGetComm((PetscObject)viewer, &comm));
  while (PETSC_TRUE) {
    PetscCallMPI(MPI_Bcast(mcnt, 1, MPIU_INT, 0, comm));
    if (!*mcnt) break;
  }
  PetscFunctionReturn(PETSC_SUCCESS);
}
