// SPDX-FileCopyrightText: Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
// SPDX-License-Identifier: BSD-3-Clause
/**
 * @class   vtkVRRenderWindowInteractor
 * @brief   Implements VR specific functions required by vtkRenderWindowInteractor.
 */

#ifndef vtkVRRenderWindowInteractor_h
#define vtkVRRenderWindowInteractor_h

#include "vtkEventData.h" // for ivar
#include "vtkNew.h"       // for ivar
#include "vtkRenderWindowInteractor3D.h"
#include "vtkRenderingVRModule.h" // for export macro

#include <string> // for ivar

VTK_ABI_NAMESPACE_BEGIN
class vtkMatrix4x4;
class vtkVRRenderWindow;

class VTKRENDERINGVR_EXPORT vtkVRRenderWindowInteractor : public vtkRenderWindowInteractor3D
{
public:
  vtkTypeMacro(vtkVRRenderWindowInteractor, vtkRenderWindowInteractor3D);
  void PrintSelf(ostream& os, vtkIndent indent) override;

  /**
   * Initialize the event handler.
   */
  void Initialize() override;

  /**
   * Run the event loop and return. This is provided so that you can
   * implement your own event loop but yet use the VTK event handling as well.
   */
  void ProcessEvents() override;

  /**
   * Implements the event loop.
   */
  virtual void DoOneEvent(vtkVRRenderWindow* renWin, vtkRenderer* ren) = 0;

  ///@{
  /**
   * Methods to set the default exit method for the class. These methods are
   * only used if no instance level ExitMethod has been defined. They are provided
   * as a means to control how an interactor is exited given the various
   * language bindings (Win32, etc.).
   */
  static void SetClassExitMethod(void (*f)(void*), void* arg);
  static void SetClassExitMethodArgDelete(void (*f)(void*));
  ///@}

  /**
   * This method corresponds to the Exit callback, allowing for the style to invoke it.
   */
  void ExitCallback() override;

  ///@{
  /**
   * Set/get the direction of the physical coordinate system -Z axis in world coordinates.
   */
  void SetPhysicalViewDirection(double, double, double) override;
  double* GetPhysicalViewDirection() override;
  ///@}

  ///@{
  /**
   * Set/get the direction of the physical coordinate system +Y axis in world coordinates.
   */
  void SetPhysicalViewUp(double, double, double) override;
  double* GetPhysicalViewUp() override;
  ///@}

  ///@{
  /**
   * Set/get position of the physical coordinate system origin in world coordinates.
   */
  void SetPhysicalTranslation(vtkCamera*, double, double, double) override;
  double* GetPhysicalTranslation(vtkCamera*) override;
  ///@}

  ///@{
  /**
   * Set/get the physical scale (world / physical distance ratio)
   */
  void SetPhysicalScale(double) override;
  double GetPhysicalScale() override;
  ///@}

  /*
   * Return the pointer index as a device.
   */
  vtkEventDataDevice GetPointerDevice();

  /*
   * Convert a device pose to a world coordinate position and orientation.
   * \param pos  Output world position
   * \param wxyz Output world orientation quaternion
   * \param ppos Output physical position
   * \param wdir Output world view direction (-Z)
   */
  void ConvertPoseToWorldCoordinates(vtkMatrix4x4* poseInTrackingCoordinates, double pos[3],
    double wxyz[4], double ppos[3], double wdir[3]);

  ///@{
  /**
   * Assign an event or std::function to an event path.
   *
   * \sa vtkOpenVRRenderWindowInteractor::AddAction
   * \sa vtkOpenXRRenderWindowInteractor::AddAction
   */
  virtual void AddAction(const std::string& path, const vtkCommand::EventIds&, bool isAnalog) = 0;
  virtual void AddAction(
    const std::string& path, bool isAnalog, const std::function<void(vtkEventData*)>&) = 0;
  ///@}

  ///@{
  /**
   * Set/Get the .json filename describing action bindings for events.
   * Based on https://github.com/ValveSoftware/openvr/wiki/Action-manifest
   * Default is empty.
   */
  vtkGetMacro(ActionManifestFileName, std::string);
  vtkSetMacro(ActionManifestFileName, std::string);
  ///@}

  ///@{
  /**
   * Set/Get the path to the directory to search for the ActionManifestFileName
   * Default is empty.
   */
  vtkGetMacro(ActionManifestDirectory, std::string);
  vtkSetMacro(ActionManifestDirectory, std::string);
  ///@}

  ///@{
  /**
   * Set/Get the name of the action set to use from the action manifest
   */
  vtkGetMacro(ActionSetName, std::string);
  vtkSetMacro(ActionSetName, std::string);
  ///@}

  ///@{
  /**
   * Handle complex gesture events. Complex gesture events recognition starts when
   * both buttons mapped to the ComplexGesture action are pressed.
   *
   * To differentiate the Rotate, Pinch and Pan gestures, the default implementation
   * is based on the following heuristic:
   * - Pinch is a move to/from the center point.
   * - Rotate is a move along the circumference.
   * - Pan is a move of the center point.
   *
   * After computing the distance along each of these axes in meters, the first
   * to break the hard-coded threshold wins.
   *
   * Overriding both HandleComplexGestureEvents() and RecognizeComplexGesture() allows to define
   * a different heuristic.
   *
   * \sa GetCurrentGesture(), SetCurrentGesture()
   */
  virtual void HandleComplexGestureEvents(vtkEventData* ed);
  virtual void RecognizeComplexGesture(vtkEventDataDevice3D* edata);
  ///@}

  ///@{
  /**
   * When handling complex gestures you can query this value to
   * determine how many input device are down for the gesture.
   *
   * Supported device values are vtkEventDataDevice::LeftController
   * and vtkEventDataDevice::RightController.
   *
   * \sa SetDeviceInputDownCount()
   * \sa HandleComplexGestureEvents(), RecognizeComplexGesture()
   */
  int GetDeviceInputDownCount(vtkEventDataDevice device) const;
  ///@}

  ///@{
  /**
   * You can set this value when defining a custom heuristic for recognizing
   * complex gestures,
   *
   * Supported device values are vtkEventDataDevice::LeftController
   * and vtkEventDataDevice::RightController.
   *
   * \sa GetDeviceInputDownCount()
   * \sa HandleComplexGestureEvents(), RecognizeComplexGesture()
   */
  void SetDeviceInputDownCount(vtkEventDataDevice device, int count);
  ///@}

protected:
  vtkVRRenderWindowInteractor();
  ~vtkVRRenderWindowInteractor() override;

  ///@{
  /**
   * internal timer methods. See the superclass for detailed
   * documentation.
   */
  int InternalCreateTimer(int timerId, int timerType, unsigned long duration) override;
  int InternalDestroyTimer(int platformTimerId) override;
  ///@}

  /**
   * This will start up the event loop and never return. If you call this
   * method it will loop processing events until the application is exited.
   */
  void StartEventLoop() override;

  ///@{
  /**
   * Class variables so an exit method can be defined for this class (used to set
   * different exit methods for various language bindings, i.e. Java, Win32).
   */
  static void (*ClassExitMethod)(void*);
  static void (*ClassExitMethodArgDelete)(void*);
  static void* ClassExitMethodArg;
  ///@}

  int DeviceInputDownCount[vtkEventDataNumberOfDevices];

  std::string ActionManifestFileName;
  std::string ActionManifestDirectory;

  std::string ActionSetName;

private:
  vtkVRRenderWindowInteractor(const vtkVRRenderWindowInteractor&) = delete;
  void operator=(const vtkVRRenderWindowInteractor&) = delete;
};

VTK_ABI_NAMESPACE_END
#endif
