// Copyright (c) 2021 OPEN CASCADE SAS
//
// This file is part of Open CASCADE Technology software library.
//
// This library is free software; you can redistribute it and/or modify it under
// the terms of the GNU Lesser General Public License version 2.1 as published
// by the Free Software Foundation, with special exception defined in the file
// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
// distribution for complete text of the license and disclaimer of any warranty.
//
// Alternatively, this file may be used under the terms of Open CASCADE
// commercial license or contractual agreement.

#ifndef _Aspect_WindowInputListener_HeaderFile
#define _Aspect_WindowInputListener_HeaderFile

#include <Aspect_VKeySet.hxx>
#include <Aspect_Touch.hxx>
#include <NCollection_IndexedDataMap.hxx>
#include <NCollection_Vec2.hxx>
#include <Standard_TypeDef.hxx>
#include <NCollection_Vec3.hxx>
#include <NCollection_Vec4.hxx>
#include <NCollection_Mat4.hxx>

struct Aspect_ScrollDelta;
class WNT_HIDSpaceMouse;

//! Defines a listener for window input events.
class Aspect_WindowInputListener
{
public:
  //! Destructor.
  Standard_EXPORT virtual ~Aspect_WindowInputListener();

  //! Return event time (e.g. current time).
  double EventTime() const { return myEventTimer.ElapsedTime(); }

  //! Handle expose event (window content has been invalidation and should be redrawn).
  virtual void ProcessExpose() = 0;

  //! Handle window resize event.
  virtual void ProcessConfigure(bool theIsResized) = 0;

  //! Handle window input event immediately (flush input buffer or ignore).
  virtual void ProcessInput() = 0;

  //! Handle focus event.
  virtual void ProcessFocus(bool theIsActivated) = 0;

  //! Handle window close event.
  virtual void ProcessClose() = 0;

public: //! @name keyboard input
  //! Return keyboard state.
  const Aspect_VKeySet& Keys() const { return myKeys; }

  //! Return keyboard state.
  Aspect_VKeySet& ChangeKeys() { return myKeys; }

  //! Press key.
  //! Default implementation updates internal cache.
  //! @param theKey key pressed
  //! @param theTime event timestamp
  Standard_EXPORT virtual void KeyDown(Aspect_VKey theKey,
                                       double      theTime,
                                       double      thePressure = 1.0) = 0;

  //! Release key.
  //! Default implementation updates internal cache.
  //! @param theKey key pressed
  //! @param theTime event timestamp
  Standard_EXPORT virtual void KeyUp(Aspect_VKey theKey, double theTime) = 0;

  //! Simulate key up/down events from axis value.
  //! Default implementation updates internal cache.
  Standard_EXPORT virtual void KeyFromAxis(Aspect_VKey theNegative,
                                           Aspect_VKey thePositive,
                                           double      theTime,
                                           double      thePressure) = 0;

public: //! @name mouse input
  //! Update mouse scroll event.
  //! This method is expected to be called from UI thread.
  //! @param theDelta mouse cursor position and delta
  //! @return TRUE if new event has been created or FALSE if existing one has been updated
  virtual bool UpdateMouseScroll(const Aspect_ScrollDelta& theDelta) = 0;

  //! Handle mouse button press/release event.
  //! This method is expected to be called from UI thread.
  //! @param thePoint      mouse cursor position
  //! @param theButtons    pressed buttons
  //! @param theModifiers  key modifiers
  //! @param theIsEmulated if TRUE then mouse event comes NOT from real mouse
  //!                      but emulated from non-precise input like touch on screen
  //! @return TRUE if window content should be redrawn
  virtual bool UpdateMouseButtons(const NCollection_Vec2<int>& thePoint,
                                  Aspect_VKeyMouse             theButtons,
                                  Aspect_VKeyFlags             theModifiers,
                                  bool                         theIsEmulated) = 0;

  //! Handle mouse cursor movement event.
  //! This method is expected to be called from UI thread.
  //! Default implementation does nothing.
  //! @param thePoint      mouse cursor position
  //! @param theButtons    pressed buttons
  //! @param theModifiers  key modifiers
  //! @param theIsEmulated if TRUE then mouse event comes NOT from real mouse
  //!                      but emulated from non-precise input like touch on screen
  //! @return TRUE if window content should be redrawn
  virtual bool UpdateMousePosition(const NCollection_Vec2<int>& thePoint,
                                   Aspect_VKeyMouse             theButtons,
                                   Aspect_VKeyFlags             theModifiers,
                                   bool                         theIsEmulated) = 0;

  //! Handle mouse button press event.
  //! This method is expected to be called from UI thread.
  //! Default implementation redirects to UpdateMousePosition().
  //! @param thePoint      mouse cursor position
  //! @param theButton     pressed button
  //! @param theModifiers  key modifiers
  //! @param theIsEmulated if TRUE then mouse event comes NOT from real mouse
  //!                      but emulated from non-precise input like touch on screen
  //! @return TRUE if window content should be redrawn
  bool PressMouseButton(const NCollection_Vec2<int>& thePoint,
                        Aspect_VKeyMouse             theButton,
                        Aspect_VKeyFlags             theModifiers,
                        bool                         theIsEmulated)
  {
    return UpdateMouseButtons(thePoint, myMousePressed | theButton, theModifiers, theIsEmulated);
  }

  //! Handle mouse button release event.
  //! This method is expected to be called from UI thread.
  //! Default implementation redirects to UpdateMousePosition().
  //! @param thePoint      mouse cursor position
  //! @param theButton     released button
  //! @param theModifiers  key modifiers
  //! @param theIsEmulated if TRUE then mouse event comes NOT from real mouse
  //!                      but emulated from non-precise input like touch on screen
  //! @return TRUE if window content should be redrawn
  bool ReleaseMouseButton(const NCollection_Vec2<int>& thePoint,
                          Aspect_VKeyMouse             theButton,
                          Aspect_VKeyFlags             theModifiers,
                          bool                         theIsEmulated)
  {
    Aspect_VKeyMouse aButtons = myMousePressed & (~theButton);
    return UpdateMouseButtons(thePoint, aButtons, theModifiers, theIsEmulated);
  }

  //! Return currently pressed mouse buttons.
  Aspect_VKeyMouse PressedMouseButtons() const { return myMousePressed; }

  //! Return active key modifiers passed with last mouse event.
  Aspect_VKeyFlags LastMouseFlags() const { return myMouseModifiers; }

  //! Return last mouse position.
  const NCollection_Vec2<int>& LastMousePosition() const { return myMousePositionLast; }

public: //! @name multi-touch input
  //! Return TRUE if touches map is not empty.
  bool HasTouchPoints() const { return !myTouchPoints.IsEmpty(); }

  //! Return map of active touches.
  const NCollection_IndexedDataMap<size_t, Aspect_Touch>& TouchPoints() const
  {
    return myTouchPoints;
  }

  //! Add touch point with the given ID.
  //! This method is expected to be called from UI thread.
  //! @param theId touch unique identifier
  //! @param thePnt touch coordinates
  //! @param theClearBefore if TRUE previously registered touches will be removed
  Standard_EXPORT virtual void AddTouchPoint(size_t                          theId,
                                             const NCollection_Vec2<double>& thePnt,
                                             bool theClearBefore = false);

  //! Remove touch point with the given ID.
  //! This method is expected to be called from UI thread.
  //! @param theId touch unique identifier
  //! @param theClearSelectPnts if TRUE will initiate clearing of selection points
  //! @return TRUE if point has been removed
  Standard_EXPORT virtual bool RemoveTouchPoint(size_t theId, bool theClearSelectPnts = false);

  //! Update touch point with the given ID.
  //! If point with specified ID was not registered before, it will be added.
  //! This method is expected to be called from UI thread.
  //! @param theId touch unique identifier
  //! @param thePnt touch coordinates
  Standard_EXPORT virtual void UpdateTouchPoint(size_t                          theId,
                                                const NCollection_Vec2<double>& thePnt);

public: //! @name 3d mouse input
  //! Return acceleration ratio for translation event; 2.0 by default.
  float Get3dMouseTranslationScale() const { return my3dMouseAccelTrans; }

  //! Set acceleration ratio for translation event.
  void Set3dMouseTranslationScale(float theScale) { my3dMouseAccelTrans = theScale; }

  //! Return acceleration ratio for rotation event; 4.0 by default.
  float Get3dMouseRotationScale() const { return my3dMouseAccelRotate; }

  //! Set acceleration ratio for rotation event.
  void Set3dMouseRotationScale(float theScale) { my3dMouseAccelRotate = theScale; }

  //! Return quadric acceleration flag; TRUE by default.
  bool To3dMousePreciseInput() const { return my3dMouseIsQuadric; }

  //! Set quadric acceleration flag.
  void Set3dMousePreciseInput(bool theIsQuadric) { my3dMouseIsQuadric = theIsQuadric; }

  //! Return 3d mouse rotation axes (tilt/roll/spin) ignore flag; (FALSE, FALSE, FALSE) by default.
  const NCollection_Vec3<bool>& Get3dMouseIsNoRotate() const { return my3dMouseNoRotate; }

  //! Return 3d mouse rotation axes (tilt/roll/spin) ignore flag; (FALSE, FALSE, FALSE) by default.
  NCollection_Vec3<bool>& Change3dMouseIsNoRotate() { return my3dMouseNoRotate; }

  //! Return 3d mouse rotation axes (tilt/roll/spin) reverse flag; (TRUE, FALSE, FALSE) by default.
  const NCollection_Vec3<bool>& Get3dMouseToReverse() const { return my3dMouseToReverse; }

  //! Return 3d mouse rotation axes (tilt/roll/spin) reverse flag; (TRUE, FALSE, FALSE) by default.
  NCollection_Vec3<bool>& Change3dMouseToReverse() { return my3dMouseToReverse; }

  //! Process 3d mouse input event (redirects to translation, rotation and keys).
  virtual bool Update3dMouse(const WNT_HIDSpaceMouse& theEvent) = 0;

  //! Process 3d mouse input translation event.
  Standard_EXPORT virtual bool update3dMouseTranslation(const WNT_HIDSpaceMouse& theEvent);

  //! Process 3d mouse input rotation event.
  Standard_EXPORT virtual bool update3dMouseRotation(const WNT_HIDSpaceMouse& theEvent);

  //! Process 3d mouse input keys event.
  Standard_EXPORT virtual bool update3dMouseKeys(const WNT_HIDSpaceMouse& theEvent);

protected:
  //! Empty constructor.
  Standard_EXPORT Aspect_WindowInputListener();

protected:
  OSD_Timer myEventTimer; //!< timer for timestamping events

protected:               //! @name keyboard input variables
  Aspect_VKeySet myKeys; //!< keyboard state

protected:                                   //! @name mouse input variables
  NCollection_Vec2<int> myMousePositionLast; //!< last mouse position
  Aspect_VKeyMouse      myMousePressed;      //!< active mouse buttons
  Aspect_VKeyFlags      myMouseModifiers;    //!< active key modifiers passed with last mouse event

protected:
  NCollection_IndexedDataMap<size_t, Aspect_Touch> myTouchPoints; //!< map of active touches

protected:                                         //! @name 3d mouse input variables
  bool                   my3dMouseButtonState[32]; //!< cached button state
  NCollection_Vec3<bool> my3dMouseNoRotate;        //!< ignore  3d mouse rotation axes
  NCollection_Vec3<bool> my3dMouseToReverse;       //!< reverse 3d mouse rotation axes
  float                  my3dMouseAccelTrans;      //!< acceleration ratio for translation event
  float                  my3dMouseAccelRotate;     //!< acceleration ratio for rotation event
  bool                   my3dMouseIsQuadric;       //!< quadric acceleration
};

#endif // _Aspect_WindowInputListener_HeaderFile
