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

  Program:   Visualization Toolkit
  Module:    vtkPTSActiveObjects.h

  Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
  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.

=========================================================================*/
/**
 * @class vtkPTSActiveObjects
 * @brief A class to track/change active objects
 *
 * vtkPTSActiveObjects can be track and change active objects.
 *
 */

#ifndef vtkPTSActiveObjects_h
#define vtkPTSActiveObjects_h

#include "vtkObject.h"
#include "vtkRemotingApplicationModule.h" // for exports
#include "vtkSmartPointer.h"              // for vtkSmartPointer

#include "vtkPythonObservableWrapper.h" // for VTK_REMOTING_MAKE_PYTHON_OBSERVABLE
#include "vtkSMProxy.h" // due to vtkPythonObservableWrapper limitations full definition is required

#include <memory> // for std::unique_ptr

#include "vtk_rxcpp.h" // for rxcpp
// clang-format off
// ideally, we include rx-lite.hpp here.
#include VTK_REMOTING_RXCPP(rx.hpp)
// clang-format on

class vtkClientSession;

class VTKREMOTINGAPPLICATION_EXPORT vtkPTSActiveObjects : public vtkObject
{
public:
  static vtkPTSActiveObjects* New();
  vtkTypeMacro(vtkPTSActiveObjects, vtkObject);
  void PrintSelf(ostream& os, vtkIndent indent) override;

  /**
   * Returns an observable for the current object.
   *
   * The return value is a cold observable i.e. it does not start emitting
   * values until something subscribes to it.
   *
   * @section Triggers Triggers
   *
   * * on_next: every time the current is changed
   *
   * * on_error: never
   *
   * * on_completed: when this instance is destroyed.
   */
  rxcpp::observable<vtkSmartPointer<vtkSMProxy>> GetCurrentObservable() const;
  VTK_REMOTING_MAKE_PYTHON_OBSERVABLE(vtkSmartPointer<vtkObject>, GetCurrentObservable() const);

  /**
   * Set current proxy. \c command is used to control how selections are affected.
   * \li NO_UPDATE: change the current without affecting the selected set of proxies.
   * \li CLEAR: clear current selection
   * \li SELECT: also select the proxy being set as current
   * \li DESELECT: deselect the proxy being set as current.
   */
  void SetCurrentProxy(vtkSMProxy* proxy, int command);

  /**
   * Returns an observable for the selection.
   *
   * The return value is a cold observable i.e. it does not start emitting
   * values until something subscribes to it.
   *
   * @section Triggers Triggers
   *
   * * on_next: every time the selection is changed
   *
   * * on_error: never
   *
   * * on_completed: when this instance is destroyed.
   */
  rxcpp::observable<std::vector<vtkSmartPointer<vtkSMProxy>>> GetSelectionObservable() const;
  VTK_REMOTING_MAKE_PYTHON_OBSERVABLE(
    std::vector<vtkSmartPointer<vtkObject>>, GetSelectionObservable() const);

  ///@{
  /**
   * Update the selected set of proxies. \c command affects how the selection is
   * updated.
   * \li NO_UPDATE: don't affect the selected set of proxies.
   * \li CLEAR: clear selection
   * \li SELECT: add the proxies to selection
   * \li DESELECT: deselect the proxies
   * \li CLEAR_AND_SELECT: clear selection and then add the specified proxies as
   * the selection.
   */
  void Select(const std::vector<vtkSmartPointer<vtkSMProxy>>& proxies, int command);
  void Select(vtkSMProxy* proxy, int command);
  ///@}

  /**
   * Emit a completion signal to all observables created through
   * `GetSelectionObservable()` and `GetCurrentObservable()`. This will cause
   * all the subscriptions to complete. In Python API all async iterators will
   * also complete. This instance of vtkPTSActiveObjects cannot be used
   * anymore. Attempting to use `GetCurrentObservable`/
   * `GetSelectionObservable` will raise a std::runtime exception.
   */
  void UnsubscribeAll();

  ///@{
  /**
   * Get/set the session. Changing the session will cause the current/selection to
   * change and hence trigger `on_next` call on the subscribed observables, if any.
   */
  void SetSession(vtkClientSession* session);
  vtkClientSession* GetSession() const;
  ///@}

  ///@{
  /**
   * Get/Set the selection model name. The name is used to maintain different
   * active object categories in the application.
   */
  void SetModelName(const std::string& name);
  const std::string& GetModelName() const;
  ///@}

protected:
  vtkPTSActiveObjects();
  ~vtkPTSActiveObjects() override;

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

  class vtkInternals;
  std::unique_ptr<vtkInternals> Internals;
};

#endif
