// 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 xRegionRangeEnd MEMBER m_xRegionRangeEnd FINAL);
    Q_PROPERTY(double yRegionRangeStart MEMBER m_yRegionRangeStart FINAL);
    Q_PROPERTY(double yRegionRangeEnd MEMBER m_yRegionRangeEnd FINAL);
    Q_PROPERTY(double xDelta MEMBER m_xDelta FINAL);
    Q_PROPERTY(double yDelta MEMBER m_yDelta FINAL);
    Q_PROPERTY(double pressedKeyCode MEMBER m_pressedKeyCode FINAL);
    Q_PROPERTY(double releasedKeyCode MEMBER m_releasedKeyCode 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_xRegionRangeEnd = std::numeric_limits<double>::min();

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

    double m_xDelta = 0;
    double m_yDelta = 0;

    int m_pressedKeyCode;
    int m_releasedKeyCode;

    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
