// SPDX-FileCopyrightText: Copyright (c) Kitware Inc.
// SPDX-License-Identifier: BSD-3-Clause
/**
 * @class vtkInSituInitializationHelper
 * @brief initialization helper for in situ environments.
 * @ingroup Insitu
 *
 * vtkInSituInitializationHelper used by ParaView-Catalyst to initialize the
 * ParaView engine. It simply setups up defaults for suitable for in situ
 * use-cases. Internally, it relies on vtkInitializationHelper to do the actual
 * initialization.
 *
 * vtkInSituInitializationHelper is also intended to be used by other in situ
 * codes includes custom Catalyst API implementations or other in situ
 * frameworks.
 *
 * @sa vtkInitializationHelper
 *
 * @defgroup Insitu ParaView In Situ
 *
 * The ParaView In Situ components include classes that can be used when
 * implementing custom in situ libraries that use ParaView for
 * data analysis and visualization. [ParaView-Catalyst](@ref ParaViewCatalyst),
 * ParaView implementation for the Catalyst API, uses these classes internally.
 * Simulation developers using the Catalyst API generally don't need to worry
 * about these. These will be of interest to those who want to develop a custom
 * Catalyst API implementation that uses ParaView or when incorporating ParaView
 * in a different in situ framework.
 */

#ifndef vtkInSituInitializationHelper_h
#define vtkInSituInitializationHelper_h

#include "vtkObject.h"
#include "vtkPVInSituModule.h" // For windows import/export of shared libraries

class vtkCPCxxHelper;
class vtkInSituPipeline;
class vtkSMProxy;
class vtkSMSourceProxy;
// forward declare conduit_node and its typedef.
// it has to be identical to the one in conduit.hpp
struct conduit_node_impl;
typedef struct conduit_node_impl conduit_node;

#include <string> // for std::string
#include <vector> // for std::vector

class VTKPVINSITU_EXPORT vtkInSituInitializationHelper : public vtkObject
{
public:
  vtkTypeMacro(vtkInSituInitializationHelper, vtkObject);
  void PrintSelf(ostream& os, vtkIndent indent) override;

  /**
   * Initializes the engine. This can only be called once in the lifetime of the
   * process. Calling this after the first call has no effect.
   *
   * 'comm' is used to determine the 'MPI_Comm' handle to be used for ParaView.
   * In non-MPI builds of ParaView, this argument is simply ignored. In
   * MPI-enabled builds of ParaView, this argument must be 'MPI_Fint' that
   * represents the Fortran handle for MPI_Comm to use. The Fortran handle is
   * an integer and hence we use 'vtkTypeUInt64' as a wide-enough container for
   * the Fortran handle without having to include 'mpi.h' in this public header.
   * The Fortran MPI communicator handle can be obtained from a `MPI_Comm` using
   * `MPI_Comm_c2f()`. Refer to MPI specification for details.
   *
   * 'python_paths' is used to update the PYTHONPATH visible to catalyst scripts. In
   * particular, in Python-enabled builds, the `python_paths` are prepended
   * into the current path of the vtkPythonInterpreter that executes the
   * catalyst script. This allows to add paths to python packages  that a
   * catalyst script can use. A prime example for this is path to custom
   * scripts or CATALYST_PYTHONPATH which at the time of writing holds the
   * necessary package to access the conduit node used in each of the
   * `catalyst_*1 calls i.e. `node = info.catalyst_params`.
   */
  static void Initialize(vtkTypeUInt64 comm, const std::vector<std::string>& python_paths);

  /**
   * Finalizes the engine. This can be called only once in the lifetime of the
   * progress, and that too only after `Initialize` has been called.
   */
  static void Finalize();

  /**
   * Add Python analysis scripts or packages. This should be called after
   * `Initialize` but before `Finalize` to add analysis pipelines. The `path`
   * can point to a Python script, a directory containing a Python package or a
   * zip-file which containing a Python package.
   */
  static vtkInSituPipeline* AddPipeline(const std::string& pipeline_name, const std::string& path);

  /**
   * Add a vtkInSituPipeline instance.
   */
  static void AddPipeline(vtkInSituPipeline* pipeline);

  /**
   * Specify the algorithm or data-producer proxy that produces the mesh for named
   * catalyst channel. This cannot be changed once specified.
   *
   * On success, this registers the `producer` with the session proxy manager using
   * the same name as the channelName.
   */
  static void SetProducer(const std::string& channelName, vtkSMSourceProxy* producer);

  /**
   * Provides access to the producer proxy for a given channel, if any.
   */
  static vtkSMSourceProxy* GetProducer(const std::string& channelName);

  /**
   * Convenience method to call Update/UpdatePipeline on all known producers.
   */
  static void UpdateAllProducers(double time);

  ///@{
  /**
   * This is provided as a convenience to indicate a particular producer has
   * been modified or has new data for current timestep.
   */
  static void MarkProducerModified(const std::string& channelName);
  static void MarkProducerModified(vtkSMSourceProxy* producer);
  ///@}

  /**
   * Executes pipelines.
   */
  static bool ExecutePipelines(int timestep, double time,
    const std::vector<std::string>& pipelines = {},
    const std::vector<std::string>& parameters = {});

  // This overload accept the "catalyst/state" node of the ParaView Blueprint.
  // When using this overload the conduit node will become available also via the catalyst script
  static bool ExecutePipelines(const conduit_node* catalyst_state);

  /**
   * Call Results() on all pipelines.
   */
  static bool GetResultsFromPipelines(conduit_node* catalyst_params);

  ///@{
  /**
   * Provides access to current time and timestep during `ExecutePipelines`
   * call. The value is not valid outside `ExecutePipelines`.
   */
  static int GetTimeStep();
  static double GetTime();
  ///@}

  /**
   * Provides access to the conduit node that the simulation passed as argument
   * to "catalyst_execute" for this timestep.  This should be treated as
   * READ-ONLY since any change could directly modify simulation data.  The
   * value is not valid outside `ExecutePipelines`.
   */
  static conduit_node* GetCatalystParameters();

  /**
   * Returns true if vtkInSituInitializationHelper has been initialized; which
   * means that ParaView is operating in in situ mode.
   */
  static bool IsInitialized() { return vtkInSituInitializationHelper::Internals != nullptr; }

  /**
   * Returns true if ParaView is built with Python support enabled.
   */
  static bool IsPythonSupported();

  /**
   * Fill the vector with all steerable proxies and extracts associated to the pipeline.
   */
  static void GetSteerableProxies(std::vector<std::pair<std::string, vtkSMProxy*>>& proxies);

  /**
   * Update the internal list of steerable proxies or extracts by adding the proxy passed in
   * parameters.
   */
  static void UpdateSteerableParameters(
    vtkSMProxy* steerableProxy, const char* steerableSourceName);

protected:
  vtkInSituInitializationHelper();
  ~vtkInSituInitializationHelper() override;

private:
  vtkInSituInitializationHelper(const vtkInSituInitializationHelper&) = delete;
  void operator=(const vtkInSituInitializationHelper&) = delete;

  static void UpdateSteerableProxies();
  static int GetAttributeTypeFromString(const std::string& associationString);

  static int WasInitializedOnce;
  static int WasFinalizedOnce;

  class vtkInternals;
  static vtkInternals* Internals;
};

#endif
