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

  Program:   ParaView
  Module:    vtkRemoteObjectProvider.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 vtkRemoteObjectProvider
 * @brief provider that adds support for remote-objects
 *
 * vtkRemoteObjectProvider is the provider to add support for handling messages
 * sent by vtkSMProxy for creation / updating / deletion of remote VTK objects.
 */

#ifndef vtkRemoteObjectProvider_h
#define vtkRemoteObjectProvider_h

#include "vtkPacket.h" // for vtkPacket
#include "vtkProvider.h"
#include "vtkRemotingServerManagerCoreModule.h" // for exports

#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 vtkObjectStore;
class vtkProxyDefinitionManager;
class vtkPVInformation;

class VTKREMOTINGSERVERMANAGERCORE_EXPORT vtkRemoteObjectProvider : public vtkProvider
{
public:
  static vtkRemoteObjectProvider* New();
  vtkTypeMacro(vtkRemoteObjectProvider, vtkProvider);
  void PrintSelf(ostream& os, vtkIndent indent) override;

  ///@{
  /**
   * Get/Set the object store to use.
   */
  void SetObjectStore(vtkObjectStore* store);
  vtkSmartPointer<vtkObjectStore> GetObjectStore() const;
  ///@}

  ///@{
  /**
   * Get/Set the proxy definition manager.
   */
  void SetProxyDefinitionManager(vtkProxyDefinitionManager* mgr);
  vtkProxyDefinitionManager* GetProxyDefinitionManager() const;
  ///@}

  ///@{
  /**
   * These methods can be used to construct a message to send to provider.
   */
  static vtkPacket UpdateState(vtkTypeUInt32 gid, const vtkNJson& json);
  static vtkPacket UpdateInformation(vtkTypeUInt32 gid);
  static vtkPacket UpdatePipeline(vtkTypeUInt32 gid, double time);
  static vtkPacket DeleteObject(vtkTypeUInt32 gid);
  static vtkPacket InvokeCommand(vtkTypeUInt32 gid, const std::string& command);
  static vtkPacket ExecuteCommand(vtkTypeUInt32 gid, const std::string& command);
  static vtkPacket GatherInformation(vtkPVInformation* information, vtkTypeUInt32 targetGID);
  ///@}

  ///@{
  struct vtkProgressItem
  {
    std::string Message;
    int8_t Progress;
    vtkTypeUInt32 GlobalID;
    bool operator==(const vtkProgressItem& other) const
    {
      return this->Message == other.Message && this->Progress == other.Progress &&
        this->GlobalID == other.GlobalID;
    }
    NLOHMANN_DEFINE_TYPE_INTRUSIVE(vtkProgressItem, Message, Progress, GlobalID);
  };
  static const std::string CHANNEL_PROGRESS() { return "pipeline::progress"; }
  static vtkProgressItem ParseProgress(const vtkPacket& packet);
  ///@}

  ///@{
  /**
   * Parse GatherInformation response.
   */
  static bool ParseResponse(const vtkPacket& packet, vtkPVInformation* information);
  static bool ParseResponse(const vtkNJson& json, vtkPVInformation* information);
  ///@}

protected:
  vtkRemoteObjectProvider();
  ~vtkRemoteObjectProvider() override;

  void InitializeInternal(vtkService* service) override;

  /**
   * Preview a message. Called on the RPC thread.
   */
  virtual void Preview(const vtkPacket& packet);

  ///@{
  /**
   * Process a message. Called on the RPC thread if "run_on_rpc" is true,
   * otherwise called on the service's main thread.
   */
  virtual vtkPacket ProcessOnRPC(const vtkPacket& packet);
  virtual vtkPacket Process(const vtkPacket& packet);
  ///@}

  /**
   * Collects data information to piggy back.
   */
  void PiggybackInformation(vtkNJson& json) const;

  /**
   * Gathers information to piggy back with update calls for the given object.
   */
  virtual vtkNJson PiggybackInformation(vtkObject* object) const;

  /**
   * Gathers information.
   */
  vtkNJson GatherInformation(vtkPVInformation* info, vtkObject* target) const;

  rxcpp::subjects::subject<vtkRemoteObjectProvider::vtkProgressItem> GetProgressSubject();

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

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

#endif
