/*=========================================================================

  Module:    vtkPVOptions.cxx

  Copyright (c) Kitware, Inc.
  All rights reserved.
  See Copyright.txt or http://www.kitware.com/Copyright.htm for details.

     This software is distributed WITHOUT ANY WARRANTY; without even
     the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
     PURPOSE.  See the above copyright notice for more information.

=========================================================================*/
#include "vtkPVOptions.h"

#include "vtkLogger.h"
#include "vtkMultiProcessController.h"
#include "vtkObjectFactory.h"
#include "vtkPVOptionsXMLParser.h"
#include "vtkProcessModule.h"

#include <vtksys/CommandLineArguments.hxx>
#include <vtksys/SystemInformation.hxx>
#include <vtksys/SystemTools.hxx>

#include <algorithm>
#include <string>

//----------------------------------------------------------------------------
vtkStandardNewMacro(vtkPVOptions);

//----------------------------------------------------------------------------
vtkPVOptions::vtkPVOptions()
{
  this->SetProcessType(ALLPROCESS);

  // initialize host names
  vtksys::SystemInformation sys_info;
  sys_info.RunOSCheck();
  const char* sys_hostname = sys_info.GetHostname() ? sys_info.GetHostname() : "localhost";
  this->HostName = 0;
  this->SetHostName(sys_hostname);

  // Initialize vtksys::CommandLineArguments
  this->UseRenderingGroup = 0;
  this->ParaViewDataName = 0;
  this->ServersFileName = 0;
  this->TestPlugins = 0;
  this->TestPluginPaths = 0;
  this->SetTestPlugins("");
  this->SetTestPluginPaths("");
  this->TileDimensions[0] = 0;
  this->TileDimensions[1] = 0;
  this->TileMullions[0] = 0;
  this->TileMullions[1] = 0;
  this->ClientMode = 0;
  this->ServerMode = 0;
  this->MultiClientMode = 0;
  this->DisableFurtherConnections = 0;
  this->MultiClientModeWithErrorMacro = 0;
  this->MultiServerMode = 0;
  this->RenderServerMode = 0;
  this->SymmetricMPIMode = 0;
  this->TellVersion = 0;
  this->EnableStreaming = 0;
  this->SatelliteMessageIds = 0;
  this->PrintMonitors = 0;
  this->ServerURL = 0;
  this->ReverseConnection = 0;
  this->UseStereoRendering = 0;
  this->UseOffscreenRendering = 0;
  this->EGLDeviceIndex = -1;
  this->ConnectID = 0;
  this->LogFileName = 0;
  this->StereoType = 0;
  this->SetStereoType("Anaglyph");
  this->Timeout = 0;
  this->EnableStackTrace = 0;
  this->DisableRegistry = 0;
  this->ForceMPIInitOnClient = 0;
  this->ForceNoMPIInitOnClient = 0;
  this->DisableXDisplayTests = 0;
  this->ForceOffscreenRendering = 0;
  this->ForceOnscreenRendering = 0;
  this->CatalystLivePort = -1;
  this->LogStdErrVerbosity = vtkLogger::VERBOSITY_INVALID;
  this->DisplaysAssignmentMode = vtkPVOptions::ROUNDROBIN;
  if (this->XMLParser)
  {
    this->XMLParser->Delete();
    this->XMLParser = 0;
  }
  this->XMLParser = vtkPVOptionsXMLParser::New();
  this->XMLParser->SetPVOptions(this);
}

//----------------------------------------------------------------------------
vtkPVOptions::~vtkPVOptions()
{
  this->SetHostName(0);
  this->SetServersFileName(0);
  this->SetLogFileName(0);
  this->SetStereoType(0);
  this->SetParaViewDataName(0);
  this->SetServerURL(0);
  this->SetTestPlugins(0);
  this->SetTestPluginPaths(0);
}

//----------------------------------------------------------------------------
void vtkPVOptions::Initialize()
{
  switch (vtkProcessModule::GetProcessType())
  {
    case vtkProcessModule::PROCESS_CLIENT:
      this->SetProcessType(PVCLIENT);
      break;

    case vtkProcessModule::PROCESS_SERVER:
      this->SetProcessType(PVSERVER);
      break;

    case vtkProcessModule::PROCESS_DATA_SERVER:
      this->SetProcessType(PVDATA_SERVER);
      break;

    case vtkProcessModule::PROCESS_RENDER_SERVER:
      this->SetProcessType(PVRENDER_SERVER);
      break;

    case vtkProcessModule::PROCESS_BATCH:
      this->SetProcessType(PVBATCH);
      break;

    default:
      break;
  }

  this->AddCallback("--verbosity", "-v", &vtkPVOptions::VerbosityArgumentHandler, this,
    "Log verbosity on stderr as an integer in range [-9, 9] "
    "or INFO, WARNING, ERROR, or OFF. Defaults to INFO(0).",
    vtkPVOptions::PVCLIENT | vtkPVOptions::PVSERVER | vtkPVOptions::PVDATA_SERVER |
      vtkPVOptions::PVRENDER_SERVER | vtkPVOptions::PVBATCH);

  this->AddCallback("--log", "-l", &vtkPVOptions::LogArgumentHandler, this,
    "Addition log files to generate. Can be specified multiple times. "
    "By default, log verbosity is set to INFO(0) and may be "
    "overridden per file by adding suffix `,verbosity` where verbosity values "
    "are same as those accepted for `--verbosity` argument.",
    vtkPVOptions::ALLPROCESS);

  // On occasion, one would want to force the hostname used by a particular
  // process (overriding the default detected by making System calls). This
  // option makes it possible).
  this->AddArgument("--hostname", 0, &this->HostName,
    "Override the hostname to be used to connect to this process. "
    "By default, the hostname is determined using appropriate system calls.",
    vtkPVOptions::ALLPROCESS);

  this->AddArgument(
    "--cslog", 0, &this->LogFileName, "ClientServerStream log file.", vtkPVOptions::ALLPROCESS);

  this->AddBooleanArgument("--multi-clients", 0, &this->MultiClientMode,
    "Allow server to keep listening for several clients to"
    "connect to it and share the same visualization session.",
    vtkPVOptions::PVDATA_SERVER | vtkPVOptions::PVSERVER);

  this->AddBooleanArgument("--multi-clients-debug", 0, &this->MultiClientModeWithErrorMacro,
    "Allow server to keep listening for several clients to"
    "connect to it and share the same visualization session."
    "While keeping the error macro on the server session for debug.",
    vtkPVOptions::PVDATA_SERVER | vtkPVOptions::PVSERVER);

  this->AddBooleanArgument("--disable-further-connections", 0, &this->DisableFurtherConnections,
    "Disable further connections after the first client connects."
    "Does nothing without --multi-clients enabled.",
    vtkPVOptions::PVDATA_SERVER | vtkPVOptions::PVSERVER);

  this->AddBooleanArgument("--multi-servers", 0, &this->MultiServerMode,
    "Allow client to connect to several pvserver", vtkPVOptions::PVCLIENT);

  this->AddArgument("--data", 0, &this->ParaViewDataName,
    "Load the specified data. "
    "To specify file series replace the numeral with a '.' eg. "
    "my0.vtk, my1.vtk...myN.vtk becomes my..vtk",
    vtkPVOptions::PVCLIENT | vtkPVOptions::PARAVIEW);

  this->AddArgument("--server-url", "-url", &this->ServerURL,
    "Set the server-url to connect to when the client starts. "
    "The --server (-s) option supersedes this option, hence use only "
    "one of the two options.",
    vtkPVOptions::PVCLIENT | vtkPVOptions::PARAVIEW);

  this->AddArgument("--connect-id", 0, &this->ConnectID,
    "Set the ID of the server and client to make sure they "
    "match. 0 is reserved to imply none specified.",
    vtkPVOptions::PVCLIENT | vtkPVOptions::PVSERVER | vtkPVOptions::PVRENDER_SERVER |
      vtkPVOptions::PVDATA_SERVER);
  this->AddBooleanArgument("--use-offscreen-rendering", 0, &this->UseOffscreenRendering,
    "Render offscreen on the satellite processes."
    " This option only works with software rendering or mangled Mesa on Unix.",
    vtkPVOptions::PVRENDER_SERVER | vtkPVOptions::PVSERVER | vtkPVOptions::PVBATCH);
#ifdef VTK_OPENGL_HAS_EGL
  this->AddArgument("--egl-device-index", NULL, &this->EGLDeviceIndex,
    "Render offscreen through the Native Platform Interface (EGL) on the graphics card "
    "specified by the device index.",
    vtkPVOptions::PVRENDER_SERVER | vtkPVOptions::PVSERVER | vtkPVOptions::PVBATCH);
#endif
  this->AddBooleanArgument("--stereo", 0, &this->UseStereoRendering,
    "Tell the application to enable stereo rendering",
    vtkPVOptions::PVCLIENT | vtkPVOptions::PARAVIEW | vtkPVOptions::PVRENDER_SERVER |
      vtkPVOptions::PVSERVER | vtkPVOptions::PVBATCH);
  this->AddArgument("--stereo-type", 0, &this->StereoType,
    "Specify the stereo type. This valid only when "
    "--stereo is specified. Possible values are "
    "\"Crystal Eyes\", \"Red-Blue\", \"Interlaced\", "
    "\"Dresden\", \"Anaglyph\", \"Checkerboard\",\"SplitViewportHorizontal\"",
    vtkPVOptions::PVCLIENT | vtkPVOptions::PARAVIEW | vtkPVOptions::PVRENDER_SERVER |
      vtkPVOptions::PVSERVER | vtkPVOptions::PVBATCH);

  this->AddBooleanArgument("--reverse-connection", "-rc", &this->ReverseConnection,
    "Have the server connect to the client.",
    vtkPVOptions::PVRENDER_SERVER | vtkPVOptions::PVDATA_SERVER | vtkPVOptions::PVSERVER);

  this->AddArgument("--tile-dimensions-x", "-tdx", this->TileDimensions,
    "Size of tile display in the number of displays in each row of the display.",
    vtkPVOptions::PVRENDER_SERVER | vtkPVOptions::PVSERVER);
  this->AddArgument("--tile-dimensions-y", "-tdy", this->TileDimensions + 1,
    "Size of tile display in the number of displays in each column of the display.",
    vtkPVOptions::PVRENDER_SERVER | vtkPVOptions::PVSERVER);
  this->AddArgument("--tile-mullion-x", "-tmx", this->TileMullions,
    "Size of the gap between columns in the tile display, in pixels.",
    vtkPVOptions::PVRENDER_SERVER | vtkPVOptions::PVSERVER);
  this->AddArgument("--tile-mullion-y", "-tmy", this->TileMullions + 1,
    "Size of the gap between rows in the tile display, in pixels.",
    vtkPVOptions::PVRENDER_SERVER | vtkPVOptions::PVSERVER);

  this->AddArgument("--timeout", 0, &this->Timeout,
    "Time (in minutes) since connecting with a client "
    "after which the server may timeout. The client typically shows warning "
    "messages before the server times out.",
    vtkPVOptions::PVDATA_SERVER | vtkPVOptions::PVSERVER);

  this->AddBooleanArgument(
    "--version", "-V", &this->TellVersion, "Give the version number and exit.");

  this->AddArgument("--servers-file", 0, &this->ServersFileName,
    "Load the specified configuration servers file (.pvsc). This option replaces "
    "the default user's configuration servers file.",
    vtkPVOptions::PVCLIENT | vtkPVOptions::PARAVIEW);

  this->AddBooleanArgument("--symmetric", "-sym", &this->SymmetricMPIMode,
    "When specified, the python script is processed symmetrically on all processes.",
    vtkPVOptions::PVBATCH);

  this->AddDeprecatedArgument("--enable-streaming", 0, "DEPRECATED: Since 5.9, use "
                                                       "settings/preferences dialog to enable or "
                                                       "disable view-based streaming.",
    vtkPVOptions::ALLPROCESS);

  this->AddBooleanArgument("--enable-satellite-message-ids", "-satellite",
    &this->SatelliteMessageIds,
    "When specified, server side messages shown on client show rank of originating process",
    vtkPVOptions::PVSERVER);

  this->AddArgument("--test-plugin", 0, &this->TestPlugins,
    "DEPRECATED: Specify the name of the plugin to load for testing."
    " Use \"--test-plugins\" instead.",
    vtkPVOptions::ALLPROCESS);

  this->AddArgument("--test-plugins", 0, &this->TestPlugins,
    "Specify the names of the plugins as a comma-separated list to load for testing",
    vtkPVOptions::ALLPROCESS);

  this->AddArgument("--test-plugin-path", 0, &this->TestPluginPaths,
    "DEPRECATED: Specify the path where more plugins can be found."
    "This is typically used when testing plugins. Use \"--test-plugin-paths\" instead.",
    vtkPVOptions::ALLPROCESS);

  this->AddArgument("--test-plugin-paths", 0, &this->TestPluginPaths,
    "Specify the paths where more plugins can be found as a comma-separated list."
    "This is typically used when testing plugins.",
    vtkPVOptions::ALLPROCESS);

  this->AddBooleanArgument("--print-monitors", 0, &this->PrintMonitors,
    "Print detected monitors and exit (Windows only).");

  this->AddBooleanArgument(
    "--enable-bt", 0, &this->EnableStackTrace, "Enable stack trace signal handler.");

  this->AddBooleanArgument("--disable-registry", "-dr", &this->DisableRegistry,
    "Do not use registry when running ParaView (for testing).");

  this->AddBooleanArgument("--disable-xdisplay-test", 0, &this->DisableXDisplayTests,
    "When specified, all X-display tests and OpenGL version checks are skipped. Use this option if "
    "you are getting remote-rendering disabled errors and you are positive that "
    "the X environment is set up properly and your OpenGL support is adequate (experimental).",
    vtkPVOptions::PVSERVER | vtkPVOptions::PVRENDER_SERVER | vtkPVOptions::PVBATCH);

#if VTK_MODULE_ENABLE_VTK_ParallelMPI
  // We add these here so that "--help" on the process can print these variables
  // out. Note the code in vtkProcessModule::Initialize() doesn't really rely on
  // the vtkPVOptions parsing these arguments since vtkPVOptions is called on to
  // parse the arguments only after MPI has been initialized.
  this->AddBooleanArgument("--mpi", 0, &this->ForceMPIInitOnClient,
    "Initialize MPI on processes, if possible. "
    "Cannot be used with --no-mpi.");
  this->AddBooleanArgument("--no-mpi", 0, &this->ForceNoMPIInitOnClient,
    "Don't initialize MPI on processes. "
    "Cannot be used with --mpi.");
#endif

  this->AddBooleanArgument("--force-offscreen-rendering", nullptr, &this->ForceOffscreenRendering,
    "If supported by the build and platform, create headless (offscreen) render windows "
    "for rendering results.",
    vtkPVOptions::PVSERVER | vtkPVOptions::PVBATCH | vtkPVOptions::PVCLIENT |
      vtkPVOptions::PVRENDER_SERVER);

  this->AddBooleanArgument("--force-onscreen-rendering", nullptr, &this->ForceOnscreenRendering,
    "If supported by the build and platform, create on-screen render windows "
    "for rendering results.",
    vtkPVOptions::PVSERVER | vtkPVOptions::PVBATCH | vtkPVOptions::PVCLIENT |
      vtkPVOptions::PVRENDER_SERVER);

  this->AddCallback("--displays", nullptr, &vtkPVOptions::DisplaysArgumentHandler, this,
    "Specify a comma separated list of rendering display or device ids. For X-based "
    "systems, this can be the value to set for the DISPLAY environment. For EGL-based "
    "systems, these are the available EGL device indices. When specified these are distributed "
    "among the number of rendering ranks using the '--displays-assignment-mode=' specified.",
    vtkPVOptions::PVSERVER | vtkPVOptions::PVRENDER_SERVER | vtkPVOptions::PVBATCH);

  this->AddCallback("--displays-assignment-mode", nullptr,
    &vtkPVOptions::DisplaysAssignmentModeArgumentHandler, this,
    "Specify how to assign displays (specified using '--displays=') among rendering ranks. "
    "Supported values are 'contiguous' and 'round-robin'. Default is 'round-robin'.",
    vtkPVOptions::PVSERVER | vtkPVOptions::PVRENDER_SERVER | vtkPVOptions::PVBATCH);
}

//----------------------------------------------------------------------------
int vtkPVOptions::LogArgumentHandler(
  const char* vtkNotUsed(argument), const char* cvalue, void* call_data)
{
  if (cvalue == nullptr)
  {
    return 0;
  }

  std::string value(cvalue);
  auto verbosity = vtkLogger::VERBOSITY_INFO;
  auto separator = value.find_last_of(',');
  if (separator != std::string::npos)
  {
    verbosity = vtkLogger::ConvertToVerbosity(value.substr(separator + 1).c_str());
    if (verbosity == vtkLogger::VERBOSITY_INVALID)
    {
      // invalid verbosity specified.
      return 0;
    }
    // remove the ",..." part from filename.
    value = value.substr(0, separator);
  }

  auto pm = vtkProcessModule::GetProcessModule();
  if (pm->GetNumberOfLocalPartitions() > 1)
  {
    value += "." + std::to_string(pm->GetPartitionId());
  }

  auto self = reinterpret_cast<vtkPVOptions*>(call_data);
  self->LogFiles.push_back(std::make_pair(value, verbosity));
  return 1;
}

//----------------------------------------------------------------------------
int vtkPVOptions::VerbosityArgumentHandler(
  const char* vtkNotUsed(argument), const char* value, void* call_data)
{
  if (value != nullptr)
  {
    auto self = reinterpret_cast<vtkPVOptions*>(call_data);
    self->LogStdErrVerbosity = vtkLogger::ConvertToVerbosity(value);
    return (self->LogStdErrVerbosity != vtkLogger::VERBOSITY_INVALID);
  }
  return 0;
}

//----------------------------------------------------------------------------
int vtkPVOptions::DisplaysArgumentHandler(
  const char* vtkNotUsed(argument), const char* cvalue, void* call_data)
{
  if (cvalue == nullptr)
  {
    return 0;
  }

  auto self = reinterpret_cast<vtkPVOptions*>(call_data);
  self->Displays = vtksys::SystemTools::SplitString(cvalue, ',');
  return 1;
}

//----------------------------------------------------------------------------
int vtkPVOptions::DisplaysAssignmentModeArgumentHandler(
  const char* vtkNotUsed(argument), const char* cvalue, void* call_data)
{
  if (cvalue == nullptr)
  {
    return 0;
  }

  auto self = reinterpret_cast<vtkPVOptions*>(call_data);
  auto arg = vtksys::SystemTools::LowerCase(cvalue);
  if (arg == "contiguous")
  {
    self->DisplaysAssignmentMode = CONTIGUOUS;
    return 1;
  }
  else if (arg == "round-robin")
  {
    self->DisplaysAssignmentMode = ROUNDROBIN;
    return 1;
  }

  vtkErrorWithObjectMacro(self, "Unknown value for 'displays-assignment-mode': " << cvalue);
  return 0;
}

//----------------------------------------------------------------------------
namespace
{

static void UpdateThreadName()
{
  // set thread name based on application.
  auto pm = vtkProcessModule::GetProcessModule();
  std::string tname_suffix;
  if (pm->GetNumberOfLocalPartitions() > 1)
  {
    tname_suffix = "." + std::to_string(pm->GetPartitionId());
  }
  switch (vtkProcessModule::GetProcessType())
  {
    case vtkProcessModule::PROCESS_CLIENT:
      vtkLogger::SetThreadName("paraview" + tname_suffix);
      break;
    case vtkProcessModule::PROCESS_SERVER:
      vtkLogger::SetThreadName("pvserver" + tname_suffix);
      break;
    case vtkProcessModule::PROCESS_DATA_SERVER:
      vtkLogger::SetThreadName("pvdatserver" + tname_suffix);
      break;
    case vtkProcessModule::PROCESS_RENDER_SERVER:
      vtkLogger::SetThreadName("pvrenderserver" + tname_suffix);
      break;
    case vtkProcessModule::PROCESS_BATCH:
      vtkLogger::SetThreadName("pvbatch" + tname_suffix);
      break;
    default:
      break;
  }
}
}

//----------------------------------------------------------------------------
int vtkPVOptions::PostProcess(int argc, const char* const* argv)
{
  UpdateThreadName();
  if (this->LogStdErrVerbosity == vtkLogger::VERBOSITY_INVALID)
  {
    vtkLogger::Init(argc, const_cast<char**>(argv), nullptr);
  }
  else
  {
    vtkLogger::SetStderrVerbosity(static_cast<vtkLogger::Verbosity>(this->LogStdErrVerbosity));
    vtkLogger::Init(argc, const_cast<char**>(argv), nullptr);
  }

  for (const auto& log_pair : this->LogFiles)
  {
    vtkLogger::LogToFile(log_pair.first.c_str(), vtkLogger::TRUNCATE,
      static_cast<vtkLogger::Verbosity>(log_pair.second));
  }

  switch (this->GetProcessType())
  {
    case vtkPVOptions::PVCLIENT:
      this->ClientMode = 1;
      break;
    case vtkPVOptions::PARAVIEW:
      break;
    case vtkPVOptions::PVRENDER_SERVER:
      this->RenderServerMode = 1;
      VTK_FALLTHROUGH;
    case vtkPVOptions::PVDATA_SERVER:
    case vtkPVOptions::PVSERVER:
      this->ServerMode = 1;
      break;
    case vtkPVOptions::PVBATCH:
    case vtkPVOptions::ALLPROCESS:
      break;
  }

  if (this->TileDimensions[0] > 0 || this->TileDimensions[1] > 0)
  {
    this->TileDimensions[0] = std::max(1, this->TileDimensions[0]);
    this->TileDimensions[1] = std::max(1, this->TileDimensions[1]);
  }

#ifdef PARAVIEW_ALWAYS_SECURE_CONNECTION
  if ((this->ClientMode || this->ServerMode) && !this->ConnectID)
  {
    this->SetErrorMessage("You need to specify a connect ID (--connect-id).");
    return 0;
  }
#endif

  // do this here for simplicity since it's
  // a universal option. The current kwsys implementation
  // is for POSIX compliant OS's, and a NOOP on others
  // but passing the flag on for all ensures that when
  // implementations for non-POSIX OS's are finished they're
  // enabled as well.
  if (this->EnableStackTrace)
  {
    vtksys::SystemInformation::SetStackTraceOnError(1);
  }

  if (this->ForceOffscreenRendering && this->ForceOnscreenRendering)
  {
    vtkWarningMacro(
      "`--force-offscreen-rendering` and `--force-onscreen-rendering` cannot be specified "
      "at the same time. `--force-offscreen-rendering` will be used.");
    this->ForceOnscreenRendering = 0;
  }

  if (this->UseOffscreenRendering)
  {
    vtkWarningMacro("`--use-offscreen-rendering` is deprecated. Use `--force-offscreen-rendering` "
                    "to force offscreen if needed.");
    this->ForceOffscreenRendering = 1;
    this->ForceOnscreenRendering = 0; // just in case.
  }

  // This is just till we update all views and client code to handle the
  // "UseOffscreenRendering" properly.
  this->UseOffscreenRendering = this->ForceOffscreenRendering;

  // if we want to debug rendering visually, we need to see the windows.
  if (vtksys::SystemTools::GetEnv("PV_DEBUG_REMOTE_RENDERING"))
  {
    if (this->ForceOffscreenRendering)
    {
      vtkWarningMacro("Since `PV_DEBUG_REMOTE_RENDERING` is set in the environment "
                      "`--force-offscreen-rendering` is ignored.");
    }
    this->ForceOffscreenRendering = 0;
    this->ForceOnscreenRendering = 1;
  }

  // handle displays.
  if (!this->Displays.empty())
  {
    auto controller = vtkMultiProcessController::GetGlobalController();
    const int rank = controller->GetLocalProcessId();
    const int numRanks = controller->GetNumberOfProcesses();
    const auto display = this->GetDisplay(rank, numRanks);
    if (!display.empty())
    {
      // for X, add DISPLAY environment variable.
      vtkLogF(TRACE, "Setting environment variable 'DISPLAY=%s'", display.c_str());
      vtksys::SystemTools::PutEnv("DISPLAY=" + display);
      // for EGL, set EGLDeviceIndex, if not already specified.
      if (this->EGLDeviceIndex == -1)
      {
        try
        {
          this->EGLDeviceIndex = std::stoi(display);
          vtkLogF(TRACE, "Setting EGLDeviceIndex to %d", this->EGLDeviceIndex);
        }
        catch (std::invalid_argument&)
        {
        }
      }
    }
  }
  return 1;
}

//----------------------------------------------------------------------------
int vtkPVOptions::WrongArgument(const char* argument)
{
  if (this->Superclass::WrongArgument(argument))
  {
    return 1;
  }

  if (this->ParaViewDataName == NULL && this->GetProcessType() == PVCLIENT)
  {
    // BUG #11199. Assume it's a data file.
    this->SetParaViewDataName(argument);
    if (this->GetUnknownArgument() && strcmp(this->GetUnknownArgument(), argument) == 0)
    {
      this->SetUnknownArgument(0);
    }
    return 1;
  }
  return 0;
}

//----------------------------------------------------------------------------
int vtkPVOptions::DeprecatedArgument(const char* argument)
{
  return this->Superclass::DeprecatedArgument(argument);
}

//----------------------------------------------------------------------------
bool vtkPVOptions::GetIsInTileDisplay() const
{
  return (this->TileDimensions[0] > 0 && this->TileDimensions[1] > 0);
}

//----------------------------------------------------------------------------
bool vtkPVOptions::GetIsInCave() const
{
  return false;
}

//----------------------------------------------------------------------------
const std::string& vtkPVOptions::GetDisplay(int myrank, int num_ranks)
{
  static std::string empty_string;
  if (this->Displays.empty())
  {
    return empty_string;
  }

  const int count = static_cast<int>(this->Displays.size());
  switch (this->DisplaysAssignmentMode)
  {
    case ROUNDROBIN:
      return this->Displays[(myrank % count)];

    case CONTIGUOUS:
    {
      // same as diy::ContiguousAssigner::rank()
      const int div = num_ranks / count;
      const int mod = num_ranks % count;
      const int r = myrank / (div + 1);
      const int index = (r < mod) ? r : (mod + (myrank - (div + 1) * mod) / div);
      return this->Displays[index];
    }
    break;
    default:
      return empty_string;
  }
}

//----------------------------------------------------------------------------
void vtkPVOptions::PrintSelf(ostream& os, vtkIndent indent)
{
  this->Superclass::PrintSelf(os, indent);
  os << indent << "HostName: " << (this->HostName ? this->HostName : "(none)") << endl;

  os << indent
     << "ParaViewDataName: " << (this->ParaViewDataName ? this->ParaViewDataName : "(none)")
     << endl;

  // Everything after this line will be showned in Help/About dialog
  os << indent << "Runtime information:"
     << endl; // important please leave it here, for more info: vtkPVApplication::AddAboutText

  if (this->ClientMode)
  {
    os << indent << "Running as a client\n";
  }

  if (this->ServerMode)
  {
    os << indent << "Running as a server\n";
  }

  if (this->MultiClientMode)
  {
    os << indent << "Allow several clients to connect to a server.\n";
  }

  if (this->DisableFurtherConnections)
  {
    os << indent << "Disable further connections after the first client connects to a server..\n";
  }

  if (this->MultiServerMode)
  {
    os << indent << "Allow a client to connect to multiple servers at the same time.\n";
  }

  if (this->RenderServerMode)
  {
    os << indent << "Running as a render server\n";
  }

  if (this->ClientMode || this->ServerMode || this->RenderServerMode)
  {
    os << indent << "ConnectID is: " << this->ConnectID << endl;
    os << indent << "Reverse Connection: " << (this->ReverseConnection ? "on" : "off") << endl;
  }

  os << indent << "Timeout: " << this->Timeout << endl;
  os << indent << "Stereo Rendering: " << (this->UseStereoRendering ? "Enabled" : "Disabled")
     << endl;

  os << indent << "ForceOnscreenRendering: " << this->ForceOnscreenRendering << endl;
  os << indent << "ForceOffscreenRendering: " << this->ForceOffscreenRendering << endl;
  os << indent << "EGL Device Index: " << this->EGLDeviceIndex << endl;

  os << indent << "Tiled Display: " << (this->TileDimensions[0] ? "Enabled" : "Disabled") << endl;
  if (this->TileDimensions[0])
  {
    os << indent << "With Tile Dimensions: " << this->TileDimensions[0] << ", "
       << this->TileDimensions[1] << endl;
    os << indent << "And Tile Mullions: " << this->TileMullions[0] << ", " << this->TileMullions[1]
       << endl;
  }

  os << indent << "Using RenderingGroup: " << (this->UseRenderingGroup ? "Enabled" : "Disabled")
     << endl;

  if (this->TellVersion)
  {
    os << indent << "Running to display software version.\n";
  }

  os << indent << "ServersFileName: " << (this->ServersFileName ? this->ServersFileName : "(none)")
     << endl;
  os << indent << "LogFileName: " << (this->LogFileName ? this->LogFileName : "(none)") << endl;
  os << indent << "SymmetricMPIMode: " << this->SymmetricMPIMode << endl;
  os << indent << "ServerURL: " << (this->ServerURL ? this->ServerURL : "(none)") << endl;
  os << indent << "EnableStreaming:" << (this->EnableStreaming ? "yes" : "no") << endl;

  os << indent << "EnableStackTrace:" << (this->EnableStackTrace ? "yes" : "no") << endl;

  os << indent << "SatelliteMessageIds " << this->SatelliteMessageIds << std::endl;
  os << indent << "PrintMonitors: " << this->PrintMonitors << std::endl;
  os << indent << "DisableXDisplayTests: " << this->DisableXDisplayTests << endl;
  os << indent << "ForceNoMPIInitOnClient: " << this->ForceNoMPIInitOnClient << endl;
  os << indent << "ForceMPIInitOnClient: " << this->ForceMPIInitOnClient << endl;
  os << indent << "CatalystLivePort: " << this->CatalystLivePort << endl;
}
