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

  Program:   ParaView
  Module:    vtkPVSynchronizedRenderWindows.h

  Copyright (c) Kitware, Inc.
  All rights reserved.
  See Copyright.txt or http://www.paraview.org/HTML/Copyright.html 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.

=========================================================================*/
/**
 * @class   vtkPVSynchronizedRenderWindows
 * @brief   synchronizes render-windows among
 * processes in ParaView configurations.
 *
 * vtkPVSynchronizedRenderWindows is the class used to synchronize render
 * windows in ParaView. This class can be instantiated on all processes in all
 * modes, it automatically discovers the configuration and adapts its behavior
 * accordingly. The role of this class is to set up the render windows on all
 * processes and then synchronize renders. It does not manage compositing or
 * image delivery. All it does is synchronize render windows and their layouts
 * among processes.
 *
 * If the application is managing calling of vtkRenderWindow::Render() on all
 * processes, then one should disable RenderEventPropagation flag.
*/

#ifndef vtkPVSynchronizedRenderWindows_h
#define vtkPVSynchronizedRenderWindows_h

#include "vtkMultiProcessController.h" // for vtkRMIFunctionType
#include "vtkObject.h"
#include "vtkPVClientServerCoreRenderingModule.h" //needed for exports
#include "vtkWeakPointer.h"                       // for vtkWeakPointer.

class vtkDataObject;
class vtkMultiProcessController;
class vtkMultiProcessStream;
class vtkPVSession;
class vtkRenderer;
class vtkRenderWindow;
class vtkSelection;

class VTKPVCLIENTSERVERCORERENDERING_EXPORT vtkPVSynchronizedRenderWindows : public vtkObject
{
public:
  /**
   * if session==NULL, then active session is used. If no active session is
   * present, then it's a critical error and this method will return NULL.
   */
  static vtkPVSynchronizedRenderWindows* New(vtkPVSession* session = NULL);

  vtkTypeMacro(vtkPVSynchronizedRenderWindows, vtkObject);
  void PrintSelf(ostream& os, vtkIndent indent) VTK_OVERRIDE;

  /**
   * Returns a render window for use (possibly new).
   */
  virtual vtkRenderWindow* NewRenderWindow();

  //@{
  /**
   * Register/UnRegister a window.
   */
  virtual void AddRenderWindow(unsigned int id, vtkRenderWindow*);
  virtual void RemoveRenderWindow(unsigned int id);
  vtkRenderWindow* GetRenderWindow(unsigned int id);
  //@}

  //@{
  /**
   * Register/UnRegister the renderers. One can add multiple renderers for the
   * same id. The id must be same as that specified when adding the
   * corresponding render window.
   */
  virtual void AddRenderer(unsigned int id, vtkRenderer*);
  virtual void RemoveAllRenderers(unsigned int id);
  virtual void AddRenderer(unsigned int id, vtkRenderer*, const double viewport[4]);
  virtual bool UpdateRendererViewport(unsigned int id, vtkRenderer*, const double viewport[4]);
  //@}

  //@{
  /**
   * The views are not supposed to updated the render window position or size
   * directly. They should always go through this API to update the window sizes
   * and positions. This makes it possible to provide a consistent API
   * irrespective of the mode ParaView is running in.
   * These methods only need to be called on the "driver" node. (No harm in
   * calling on all nodes). By driver node, we mean the CLIENT in
   * client-server mode and the root node in batch mode.
   */
  virtual void SetWindowSize(unsigned int id, int width, int height);
  virtual void SetWindowPosition(unsigned int id, int posx, int posy);
  virtual const int* GetWindowSize(unsigned int id);
  virtual const int* GetWindowPosition(unsigned int id);
  //@}

  //@{
  /**
   * Enable/Disable parallel rendering.
   */
  vtkSetMacro(Enabled, bool);
  vtkGetMacro(Enabled, bool);
  vtkBooleanMacro(Enabled, bool);
  //@}

  //@{
  /**
   * Enable/Disable propagation of the render event. This is typically true,
   * unless the application is managing calling Render() on all processes
   * involved.
   */
  vtkSetMacro(RenderEventPropagation, bool);
  vtkGetMacro(RenderEventPropagation, bool);
  vtkBooleanMacro(RenderEventPropagation, bool);
  //@}

  /**
   * This method returns true if the local process is the 'driver' process. In
   * client-server configurations, client is the driver. In batch
   * configurations, root-node is the driver. In built-in mode, this always
   * returns true.
   */
  bool GetLocalProcessIsDriver();

  //@{
  /**
   * vtkPVSynchronizedRenderWindows encapsulates a whole lot of logic for
   * communication between processes. Given the ton of information this class
   * keeps, it can easily aid vtkViews to synchronize information such as bounds/
   * data-size among all processes efficiently. This can be achieved by using
   * these methods.
   * Note that these methods should be called on all processes at the same time
   * otherwise we will have deadlocks.
   * We may make this API generic in future, for now this works.
   */
  bool SynchronizeBounds(double bounds[6]);
  bool SynchronizeSize(double& size);
  bool SynchronizeSize(unsigned int& size);
  bool BroadcastToDataServer(vtkSelection* selection);
  bool BroadcastToRenderServer(vtkDataObject*);
  //@}

  enum StandardOperations
  {
    MAX_OP = vtkCommunicator::MAX_OP,
    MIN_OP = vtkCommunicator::MIN_OP,
    SUM_OP = vtkCommunicator::SUM_OP
  };
  bool Reduce(vtkIdType& value, StandardOperations operation);

  //@{
  /**
   * Convenience method to trigger an RMI call from the client/root node.
   */
  void TriggerRMI(vtkMultiProcessStream& stream, int tag);
  unsigned long AddRMICallback(vtkRMIFunctionType, void* localArg, int tag);
  bool RemoveRMICallback(unsigned long id);
  //@}

  enum
  {
    SYNC_MULTI_RENDER_WINDOW_TAG = 15002,
    GET_ZBUFFER_VALUE_TAG = 15003,
    SYNC_TILE_DISPLAY_PARAMATERS = 15004
  };

  // Internal-callback-method
  void Render(unsigned int);
  void OnGetZBufferValue(unsigned int, int, int);

  //@{
  vtkGetObjectMacro(ParallelController, vtkMultiProcessController);
  vtkGetObjectMacro(ClientServerController, vtkMultiProcessController);
  vtkGetObjectMacro(ClientDataServerController, vtkMultiProcessController);
  //@}

  //@{
  /**
   * By default, this class uses the same render window for all views on the
   * server processes and then arranges the renderers by adjusting their
   * viewports. However, this does not work well when doing image capture with
   * magnification. In those cases, you can force this class to simply render
   * the active view as the sole view in the window on the server side by
   * setting this flag to true.
   */
  vtkSetMacro(RenderOneViewAtATime, bool);
  vtkGetMacro(RenderOneViewAtATime, bool);
  vtkBooleanMacro(RenderOneViewAtATime, bool);
  //@}

  /**
   * Called before starting render. This is needed in batch mode since all views
   * share the same render window.
   */
  void BeginRender(unsigned int id);

  /**
   * Returns true when in Cave mode.
   */
  bool GetIsInCave();

  /**
   * This method should only be called on RENDER_SERVER or BATCH processes.
   * Returns true if in tile display mode and fills up tile_dims with the tile
   * dimensions.
   */
  bool GetTileDisplayParameters(int tile_dims[2], int tile_mullions[2]);

  /**
   * Returns true if in tile display mode.
   */
  bool GetIsInTileDisplay()
  {
    int not_used[2];
    return this->GetTileDisplayParameters(not_used, not_used);
  }
  /**
   * Returns the z-buffer value at the given location. \c id is the view id
   * used in AddRenderWindow()/AddRenderer() etc.
   * \note CallOnClientOnly
   */
  double GetZbufferDataAtPoint(int x, int y, unsigned int id);

  enum ModeEnum
  {
    INVALID,
    BUILTIN,
    CLIENT,
    RENDER_SERVER,
    DATA_SERVER,
    BATCH
  };

  /**
   * Streaming uses this class as a conduit for messaging.
   * Need mode to use it correctly.
   */
  ModeEnum GetMode() { return this->Mode; };

  /**
   * Provides access to the session.
   */
  vtkPVSession* GetSession();

  //@{
  /**
   * Use this to indicate that the process should use
   * vtkGenericOpenGLRenderWindow rather than vtkRenderWindow in
   * NewRenderWindow.
   */
  static void SetUseGenericOpenGLRenderWindow(bool val);
  static bool GetUseGenericOpenGLRenderWindow();
  //@}
protected:
  vtkPVSynchronizedRenderWindows(vtkPVSession*);
  ~vtkPVSynchronizedRenderWindows() override;

  /**
   * Set/Get the controller used for communication among parallel processes.
   */
  void SetParallelController(vtkMultiProcessController*);

  /**
   * Set/Get the controller used for client-server communication.
   */
  void SetClientServerController(vtkMultiProcessController*);

  /**
   * Set/Get the controller used for client-data-server communication.
   */
  void SetClientDataServerController(vtkMultiProcessController*);

  //@{
  /**
   * Saves the information about all the windows known to this class and how
   * they are laid out. For this to work as expected, it is essential that the
   * client sets the WindowSize and WindowPosition correctly for all the render
   * windows using the API on this class. It also saves some information about
   * the active render window.
   */
  void SaveWindowAndLayout(vtkRenderWindow*, vtkMultiProcessStream& stream);
  void LoadWindowAndLayout(vtkRenderWindow*, vtkMultiProcessStream& stream);
  //@}

  /**
   * Using the meta-data saved about the render-windows and their positions and
   * sizes, this updates the renderers/window-sizes etc. This have different
   * response on different processes types.
   */
  void UpdateWindowLayout();

  /**
   * Ensures that only the renderer assigned to the given id are enabled, all
   * others are disabled. This is especially necessary on processes where the
   * render window is shared.
   */
  void UpdateRendererDrawStates(unsigned int id);

  // These methods are called on all processes as a consequence of corresponding
  // events being called on the render window.
  virtual void HandleStartRender(vtkRenderWindow*);
  virtual void HandleEndRender(vtkRenderWindow*);
  virtual void HandleAbortRender(vtkRenderWindow*) {}

  virtual void ClientStartRender(vtkRenderWindow*);
  virtual void RootStartRender(vtkRenderWindow*);
  virtual void SatelliteStartRender(vtkRenderWindow*);

  /**
   * Shrinks gaps between views, rather grows the views to reduce gaps. Only
   * used in tile-display mode to avoid gaps on the server side.
   */
  void ShinkGaps();

  ModeEnum Mode;
  vtkMultiProcessController* ParallelController;
  vtkMultiProcessController* ClientServerController;
  vtkMultiProcessController* ClientDataServerController;
  unsigned long ClientServerRMITag;
  unsigned long ClientServerGetZBufferValueRMITag;
  unsigned long ParallelRMITag;
  bool Enabled;
  bool RenderEventPropagation;
  bool RenderOneViewAtATime;

  vtkWeakPointer<vtkPVSession> Session;

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

  class vtkInternals;
  vtkInternals* Internals;

  class vtkObserver;
  vtkObserver* Observer;

  template <class T>
  bool ReduceTemplate(T& size, StandardOperations operation);

  static bool UseGenericOpenGLRenderWindow;
  vtkRenderWindow* NewRenderWindowInternal();
};

#endif

// VTK-HeaderTest-Exclude: vtkPVSynchronizedRenderWindows.h
