// Copyright 2021 Filippo Rusconi
// GPL3+

#pragma once

/////////////////////// StdLib includes


/////////////////////// Qt includes
#include <QJSEngine>
#include <QPointF>
#include <QString>

/////////////////////// Local includes
#include "../../core/types.h"
#include "pappsomspp/export-import-config.h"
#include "pappsomspp/core/js_qml/jsclassregistrar.h"
#include "pappsomspp/core/processing/combiners/integrationscopebase.h"
#include "pappsomspp/core/processing/combiners/selectionpolygon.h"


////////////////////// Other includes
#include "qcustomplot.h"

namespace pappso
{

Q_NAMESPACE

enum class DragDirections
{
  NOT_SET       = 0x0000,
  LEFT_TO_RIGHT = 1 << 0,
  RIGHT_TO_LEFT = 1 << 1,
  TOP_TO_BOTTOM = 1 << 2,
  BOTTOM_TO_TOP = 1 << 3
};
Q_ENUM_NS(DragDirections)

extern std::map<Qt::MouseButton, QString> qtMouseButtonMap;
extern std::map<Qt::MouseButtons, QString> qtMouseButtonsMap;

extern std::map<Qt::KeyboardModifier, QString> qtKeyboardModifierMap;

/*  END CLASS JS REFERENCE
 *  namespace: pappso
 *  class name: BasePlotContext
 */

class PMSPP_LIB_DECL BasePlotContext : public QObject
{
  Q_OBJECT

  Q_PROPERTY(bool isMouseDragging MEMBER m_isMouseDragging)
  Q_PROPERTY(bool wasMouseDragging MEMBER m_wasMouseDragging)
  Q_PROPERTY(bool isKeyBoardDragging MEMBER m_isKeyBoardDragging)
  Q_PROPERTY(bool isLeftPseudoButtonKeyPressed MEMBER m_isLeftPseudoButtonKeyPressed)
  Q_PROPERTY(bool isRightPseudoButtonKeyPressed MEMBER m_isRightPseudoButtonKeyPressed)
  Q_PROPERTY(bool wasKeyBoardDragging MEMBER m_wasKeyBoardDragging)
  Q_PROPERTY(QPointF startDragPoint MEMBER m_startDragPoint FINAL)
  Q_PROPERTY(QPointF currentDragPoint MEMBER m_currentDragPoint FINAL)
  Q_PROPERTY(QPointF lastCursorHoveredPoint MEMBER m_lastCursorHoveredPoint FINAL);
  Q_PROPERTY(DragDirections dragDirections MEMBER m_dragDirections FINAL);
  Q_PROPERTY(double xRegionRangeStart MEMBER m_xRegionRangeStart FINAL);
  Q_PROPERTY(double xRegionRangeStop MEMBER m_xRegionRangeStop FINAL);
  Q_PROPERTY(double yRegionRangeStart MEMBER m_yRegionRangeStart FINAL);
  Q_PROPERTY(double yRegionRangeStop MEMBER m_yRegionRangeStop FINAL);
  Q_PROPERTY(double xDelta MEMBER m_xDelta FINAL);
  Q_PROPERTY(double yDelta MEMBER m_yDelta FINAL);
  Q_PROPERTY(int pressedKeyCode MEMBER m_pressedKeyCode FINAL);
  Q_PROPERTY(QString pressedKeyText MEMBER m_pressedKeyText FINAL);
  Q_PROPERTY(int releasedKeyCode MEMBER m_releasedKeyCode FINAL);
  Q_PROPERTY(QString releasedKeyText MEMBER m_releasedKeyText FINAL);
  Q_PROPERTY(Qt::KeyboardModifiers keyboardModifiers MEMBER m_keyboardModifiers FINAL);
  Q_PROPERTY(Qt::MouseButtons lastPressedMouseButton MEMBER m_lastPressedMouseButton FINAL);
  Q_PROPERTY(Qt::MouseButtons lastReleasedMouseButton MEMBER m_lastReleasedMouseButton FINAL);
  Q_PROPERTY(Qt::MouseButtons pressedMouseButtons MEMBER m_pressedMouseButtons FINAL);
  Q_PROPERTY(Qt::MouseButtons mouseButtonsAtMousePress MEMBER m_mouseButtonsAtMousePress FINAL);
  Q_PROPERTY(Qt::MouseButtons mouseButtonsAtMouseRelease MEMBER m_mouseButtonsAtMouseRelease FINAL);

  public:
  Q_INVOKABLE explicit BasePlotContext(QObject *parent = nullptr);

  virtual ~BasePlotContext();

  Q_INVOKABLE BasePlotContext *clone(QObject *parent = nullptr);
  Q_INVOKABLE void initialize(const BasePlotContext &other);

  BasePlotContext(const BasePlotContext &other)            = delete;
  BasePlotContext &operator=(const BasePlotContext &other) = delete;

  Enums::DataKind m_dataKind = Enums::DataKind::unset;

  bool m_isMouseDragging  = false;
  bool m_wasMouseDragging = false;

  bool m_isKeyBoardDragging            = false;
  bool m_isLeftPseudoButtonKeyPressed  = false;
  bool m_isRightPseudoButtonKeyPressed = false;
  bool m_wasKeyBoardDragging           = false;

  QPointF m_startDragPoint;
  QPointF m_currentDragPoint;
  QPointF m_lastCursorHoveredPoint;
  DragDirections m_dragDirections = DragDirections::NOT_SET;

  IntegrationScopeBaseCstSPtr msp_integrationScope = nullptr;
  SelectionPolygon m_selectionPolygon;
  double m_integrationScopeRhombWidth  = 0;
  double m_integrationScopeRhombHeight = 0;

  // The effective range of the axes.
  QCPRange m_xRange;
  QCPRange m_yRange;

  // Tell if the mouse move was started onto either axis, because that will
  // condition if some calculations needs to be performed or not (for example,
  // if the mouse cursor motion was started on an axis, there is no point to
  // perform deconvolutions).
  bool m_wasClickOnXAxis = false;
  bool m_wasClickOnYAxis = false;

  bool m_isMeasuringDistance = false;

  // The user-selected region over the plot.
  // Note that we cannot use QCPRange structures because these are normalized by
  // QCustomPlot in such a manner that lower is actually < upper. But we need
  // for a number of our calculations (specifically for the deconvolutions) to
  // actually have the lower value be start drag point.x even if the drag
  // direction was from right to left.
  double m_xRegionRangeStart = std::numeric_limits<double>::min();
  double m_xRegionRangeStop  = std::numeric_limits<double>::min();

  double m_yRegionRangeStart = std::numeric_limits<double>::min();
  double m_yRegionRangeStop  = std::numeric_limits<double>::min();

  double m_xDelta = 0;
  double m_yDelta = 0;

  int m_pressedKeyCode;
  QSet<int> m_pressedKeyCodes;
  QString m_pressedKeyText;
  int m_releasedKeyCode;
  QSet<int> m_releasedKeyCodes;
  QString m_releasedKeyText;

  Qt::KeyboardModifiers m_keyboardModifiers;

  Qt::MouseButtons m_lastPressedMouseButton;
  Qt::MouseButtons m_lastReleasedMouseButton;

  Qt::MouseButtons m_pressedMouseButtons;

  Qt::MouseButtons m_mouseButtonsAtMousePress;
  Qt::MouseButtons m_mouseButtonsAtMouseRelease;

  void updateIntegrationScope();
  void updateIntegrationScopeRect();
  void updateIntegrationScopeRhomb();
  void updateIntegrationScopeRhombHorizontal();
  void updateIntegrationScopeRhombVertical();

  DragDirections recordDragDirections();

  Q_INVOKABLE QString toString() const;
  Q_INVOKABLE QString dragDirectionsToString() const;

  static void registerJsConstructor(QJSEngine *engine);
};

PAPPSO_REGISTER_JS_CLASS(pappso, BasePlotContext)

/*  END CLASS JS REFERENCE
 *  namespace: pappso
 *  class name: BasePlotContext
 */

} // namespace pappso
