// Copyright 2021 Filippo Rusconi
// GPL3+

#include "baseplotcontext.h"
#include "../../processing/combiners/integrationscope.h"
#include "../../processing/combiners/integrationscoperect.h"
#include "../../processing/combiners/integrationscoperhomb.h"

namespace pappso
{

BasePlotContext::BasePlotContext()
{
  // So we know it is never nullptr.
  msp_integrationScope = std::make_shared<IntegrationScopeBase>();
}


BasePlotContext::BasePlotContext(const BasePlotContext &other)
{
  // qDebug() << "Constructing BasePlotContext by copy.";

  m_dataKind = other.m_dataKind;

  m_isMouseDragging  = other.m_isMouseDragging;
  m_wasMouseDragging = other.m_wasMouseDragging;

  m_isKeyBoardDragging            = other.m_isKeyBoardDragging;
  m_isLeftPseudoButtonKeyPressed  = other.m_isLeftPseudoButtonKeyPressed;
  m_isRightPseudoButtonKeyPressed = other.m_isRightPseudoButtonKeyPressed;
  m_wassKeyBoardDragging          = other.m_wassKeyBoardDragging;

  m_startDragPoint         = other.m_startDragPoint;
  m_currentDragPoint       = other.m_currentDragPoint;
  m_lastCursorHoveredPoint = other.m_lastCursorHoveredPoint;

  m_selectionPolygon            = other.m_selectionPolygon;
  msp_integrationScope          = other.msp_integrationScope;
  m_integrationScopeRhombWidth  = other.m_integrationScopeRhombWidth;
  m_integrationScopeRhombHeight = other.m_integrationScopeRhombHeight;

  // The effective range of the axes.
  m_xRange = other.m_xRange;
  m_yRange = other.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).
  m_wasClickOnXAxis = other.m_wasClickOnXAxis;
  m_wasClickOnYAxis = other.m_wasClickOnYAxis;

  m_isMeasuringDistance = other.m_isMeasuringDistance;

  // 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.
  m_xRegionRangeStart = other.m_xRegionRangeStart;
  m_xRegionRangeEnd   = other.m_xRegionRangeEnd;

  m_yRegionRangeStart = other.m_yRegionRangeStart;
  m_yRegionRangeEnd   = other.m_yRegionRangeEnd;

  m_xDelta = other.m_xDelta;
  m_yDelta = other.m_yDelta;

  m_pressedKeyCode  = other.m_pressedKeyCode;
  m_releasedKeyCode = other.m_releasedKeyCode;

  m_keyboardModifiers = other.m_keyboardModifiers;

  m_lastPressedMouseButton  = other.m_lastPressedMouseButton;
  m_lastReleasedMouseButton = other.m_lastReleasedMouseButton;

  m_pressedMouseButtons = other.m_pressedMouseButtons;

  m_mouseButtonsAtMousePress   = other.m_mouseButtonsAtMousePress;
  m_mouseButtonsAtMouseRelease = other.m_mouseButtonsAtMouseRelease;
}

BasePlotContext::~BasePlotContext()
{
}

void
BasePlotContext::updateIntegrationScope()
{
  // qDebug();

  // By essence, IntegrationScope is 1D scope. The point of the scope is the
  // left bottom point, and then we document the width.

  double x_range_start = std::min(m_currentDragPoint.x(), m_startDragPoint.x());
  double x_range_end   = std::max(m_currentDragPoint.x(), m_startDragPoint.x());

  double y_position = m_startDragPoint.y();

  QPointF point(x_range_start, y_position);
  double width = x_range_end - x_range_start;

  // qDebug() << "Going to create an integration scope with point:"
  // << point << "and width:" << width;
  msp_integrationScope = std::make_shared<IntegrationScope>(point, width);
}


void
BasePlotContext::updateIntegrationScopeRect()
{
  // qDebug();

  // By essence, IntegrationScopeRect is a squared rectangle scope. The point of
  // the scope is the left bottom point, and then we document the width and the
  // height.

  /* Like this:
   *
  +---------------------------+  -
  |                           |  |
  |                           |  |
  |                           |  m_height
  |                           |  |
  |                           |  |
  P---------------------------+  -

  |--------- m_width ---------|

  */

  // We need to find the point that is actually the left bottom point.

  QPointF point;
  double width = 0;
  double height = 0;

  if(static_cast<int>(m_dragDirections) &
       static_cast<int>(DragDirections::LEFT_TO_RIGHT) &&
     static_cast<int>(m_dragDirections) &
       static_cast<int>(DragDirections::BOTTOM_TO_TOP))
    {
      point.rx() = m_startDragPoint.x();
      point.ry() = m_startDragPoint.y();
      width      = m_currentDragPoint.x() - point.rx();
      height     = m_currentDragPoint.y() - point.ry();
      // qDebug() << "left to right - bottom to top";
    }

  if(static_cast<int>(m_dragDirections) &
       static_cast<int>(DragDirections::RIGHT_TO_LEFT) &&
     static_cast<int>(m_dragDirections) &
       static_cast<int>(DragDirections::BOTTOM_TO_TOP))
    {
      point.rx() = m_currentDragPoint.x();
      point.ry() = m_currentDragPoint.y();
      width      = m_startDragPoint.x() - m_currentDragPoint.x();
      height     = m_startDragPoint.y() - m_currentDragPoint.y();
      // qDebug() << "right to left - bottom to top";
    }

  if(static_cast<int>(m_dragDirections) &
       static_cast<int>(DragDirections::LEFT_TO_RIGHT) &&
     static_cast<int>(m_dragDirections) &
       static_cast<int>(DragDirections::TOP_TO_BOTTOM))
    {
      point.rx() = m_startDragPoint.x();
      point.ry() = m_currentDragPoint.y();
      width      = m_currentDragPoint.x() - m_startDragPoint.x();
      height     = m_startDragPoint.y() - m_currentDragPoint.y();
      // qDebug() << "left to right - top to bottom";
    }

  if(static_cast<int>(m_dragDirections) &
       static_cast<int>(DragDirections::RIGHT_TO_LEFT) &&
     static_cast<int>(m_dragDirections) &
       static_cast<int>(DragDirections::TOP_TO_BOTTOM))
    {
      point.rx() = m_currentDragPoint.x();
      point.ry() = m_currentDragPoint.y();
      width      = m_startDragPoint.x() - m_currentDragPoint.x();
      height     = m_startDragPoint.y() - m_currentDragPoint.y();
      // qDebug() << "right to left - top to bottom";
    }

  // qDebug() << "The data used to update the integration scope:";
  // qDebug() << "Point:" << point << "width:" << width << "height:" << height;
  //
  // qDebug() << "The integration scope before update:" << mpa_integrationScope;
  //
  // qDebug() << "Will update IntegrationScopeRect with:" << point << "width"
  //          << width << "height" << height;
  msp_integrationScope =
    std::make_shared<IntegrationScopeRect>(point, width, height);


  // if(typeid(*mpa_integrationScope) == typeid(IntegrationScopeInterface))
  //   qDebug() << "The pointer is of type IntegrationScopeInterface";
  // if(typeid(*mpa_integrationScope) == typeid(IntegrationScope))
  //   qDebug() << "The pointer is of type IntegrationScope";
  // if(typeid(*mpa_integrationScope) == typeid(IntegrationScopeRect))
  //   qDebug() << "The pointer is of type IntegrationScopeRect";
  // if(typeid(*mpa_integrationScope) == typeid(IntegrationScopeRhomb))
  //   qDebug() << "The pointer is of type IntegrationScopeRhomb";
  //
  // qDebug() << "The integration scope right after update:"
  //          << mpa_integrationScope;
  //
  // if(!mpa_integrationScope->getPoint(point))
  //   qFatal("Could not get point.");
  // qDebug() << "The point:" << point;
  // if(!mpa_integrationScope->getWidth(width))
  //   qFatal("Oh no!!!! width");
  // if(!mpa_integrationScope->getWidth(height))
  //   qFatal("Oh no!!!! height");
}


void
BasePlotContext::updateIntegrationScopeRhombHorizontal()
{
  // qDebug() << toString();
  
  /*
                           4+----------+3
                            |          |
                           |          |
                          |          |
                         |          |
                        |          |
                       |          |
                      |          |
                     1+----------+2
                      ----width---
  */

  // As visible here, the fixed size of the rhomboid (using the S key in the
  // plot widget) is the horizontal side.

  // The points are numbered in a counterclockwise manner, starting from the
  // starting drag point. The width side is right of the start drag point if
  // the user drags from left to right and left of the start drag point if
  // the user drags from left to right. In the figure above, the user
  // has dragged the mouse from point 1 and to the right and upwards.
  // Thus the width side is right of point 1. Because the numbering
  // is counterclockwise, that point happens to be numbered 2.

  // If the user had draggged the mouse starting at point 3 and to the left
  // and to the bottom, then point 3 above would be point 1, point 4
  // would be point 2 because the width side is left of the start
  // drag point; point 1 would be point 3 and finally the last point
  // would be at point 2.

  // Sanity check
  if(m_integrationScopeRhombWidth == 0)
    qFatal(
      "The m_integrationScopeRhombWidth of the fixed rhomboid side cannot be "
      "0.");

  QPointF point;
  std::vector<QPointF> points;

  // Fill-in the points in the vector in the order they are created
  // while drawing the rhomboid shape. Thus, the first point (start of the
  // mouse click & drag operation is always the same.

  point.rx() = m_startDragPoint.x();
  point.ry() = m_startDragPoint.y();
  points.push_back(point);
  // qDebug() << "Start point:" << point;

  if(static_cast<int>(m_dragDirections) &
       static_cast<int>(DragDirections::LEFT_TO_RIGHT) &&
     static_cast<int>(m_dragDirections) &
       static_cast<int>(DragDirections::BOTTOM_TO_TOP))
    {
      // Second point.
      point.rx() = m_startDragPoint.x() + m_integrationScopeRhombWidth;
      point.ry() = m_startDragPoint.y();
      points.push_back(point);
      // qDebug() << "Second point:" << point;

      // Third point.
      point.rx() = m_currentDragPoint.rx() + m_integrationScopeRhombWidth;
      point.ry() = m_currentDragPoint.ry();
      points.push_back(point);
      // qDebug() << "Third point:" << point;

      // Fourth point.
      point.rx() = m_currentDragPoint.rx();
      point.ry() = m_currentDragPoint.ry();
      points.push_back(point);
      // qDebug() << "Last point:" << point;
    }

  if(static_cast<int>(m_dragDirections) &
       static_cast<int>(DragDirections::RIGHT_TO_LEFT) &&
     static_cast<int>(m_dragDirections) &
       static_cast<int>(DragDirections::BOTTOM_TO_TOP))
    {
      // Second point.
     point.rx() = m_currentDragPoint.rx();
     point.ry() = m_currentDragPoint.ry();
      points.push_back(point);
      // qDebug() << "Second point:" << point;

      // Third point.
      point.rx() = m_currentDragPoint.rx() - m_integrationScopeRhombWidth;
      point.ry() = m_currentDragPoint.ry();
      points.push_back(point);
      // qDebug() << "Third point:" << point;

      // Fourth point.
      point.rx() = m_startDragPoint.rx() - m_integrationScopeRhombWidth;
      point.ry() = m_startDragPoint.ry();
      points.push_back(point);
      // qDebug() << "Last point:" << point;
    }

  if(static_cast<int>(m_dragDirections) &
       static_cast<int>(DragDirections::LEFT_TO_RIGHT) &&
     static_cast<int>(m_dragDirections) &
       static_cast<int>(DragDirections::TOP_TO_BOTTOM))
    {
      // Second point.
      point.rx() = m_currentDragPoint.rx();
      point.ry() = m_currentDragPoint.ry();
      points.push_back(point);
      // qDebug() << "Second point:" << point;

      // Third point.
      point.rx() = m_currentDragPoint.rx() + m_integrationScopeRhombWidth;
      point.ry() = m_currentDragPoint.ry();
      points.push_back(point);
      // qDebug() << "Third point:" << point;

      // Fourth point.
      point.rx() = m_startDragPoint.x() + m_integrationScopeRhombWidth;
      point.ry() = m_startDragPoint.y();
      points.push_back(point);
      // qDebug() << "Last point:" << point;
    }

  if(static_cast<int>(m_dragDirections) &
       static_cast<int>(DragDirections::RIGHT_TO_LEFT) &&
     static_cast<int>(m_dragDirections) &
       static_cast<int>(DragDirections::TOP_TO_BOTTOM))
    {
      // Second point.
      point.rx() = m_startDragPoint.x() - m_integrationScopeRhombWidth;
      point.ry() = m_startDragPoint.y();
      points.push_back(point);
      // qDebug() << "Second point:" << point;

      // Third point.
      point.rx() = m_currentDragPoint.rx() - m_integrationScopeRhombWidth;
      point.ry() = m_currentDragPoint.ry();
      points.push_back(point);
      // qDebug() << "Third point:" << point;

      // Fourth point.
      point.rx() = m_currentDragPoint.rx();
      point.ry() = m_currentDragPoint.ry();
      points.push_back(point);
      // qDebug() << "Last point:" << point;
    }


  msp_integrationScope = std::make_shared<IntegrationScopeRhomb>(points);

  // qDebug() << "Created an integration scope horizontal rhomboid with"
  //          << points.size() << "points:" << msp_integrationScope->toString();
}


void
BasePlotContext::updateIntegrationScopeRhombVertical()
{
  // qDebug() << toString();

  /*
   *                                       +3
   *                                     . |
   *                                   .   |
   *                                 .     |
   *                               .       +2
   *                             .       .
   *                           .       .
   *                         .       .
   *                      4+       .
   *                     | |     .
   *            height   | |   .
   *                     | | .
   *                      1+
   *
   */

  // As visible here, the fixed size of the rhomboid (using the S key in the
  // plot widget) is the vertical side.

  // The points are numbered in a counterclockwise manner, starting from the
  // starting drag point. The height side is below the start drag point if
  // the user drags from top to bottom and above the start drag point if
  // the user drags from bottom to top. In the figure above, the user
  // has dragged the mouse from point 1 and to the right and upwards.
  // Thus the height side is above the point 1. Because the numbering
  // is counterclockwise, that point happens to be numbered 4.

  // If the user had draggged the mouse starting at point 3 and to the left
  // and to the bottom, then point 3 above would be point 1, point 4
  // would be ponit 2, point 1 would be point 3 and finally, because
  // the dragging is from top to bottom, the last point would be at point 2
  // above, because the height side of the rhomboid is below the start
  // drag point.

  // Sanity check
  if(m_integrationScopeRhombHeight == 0)
    qFatal("The height of the fixed rhomboid side cannot be 0.");

  QPointF point;
  std::vector<QPointF> points;

  // Fill-in the points in the vector in the order they are created
  // while drawing the rhomboid shape. Thus, the first point (start of the
  // mouse click & drag operation is always the same, the leftmost bottom point
  // of the drawing above (point 1).

  point.rx() = m_startDragPoint.x();
  point.ry() = m_startDragPoint.y();
  points.push_back(point);
  qDebug() << "Start point:" << point;

  if(static_cast<int>(m_dragDirections) &
       static_cast<int>(DragDirections::LEFT_TO_RIGHT) &&
     static_cast<int>(m_dragDirections) &
       static_cast<int>(DragDirections::BOTTOM_TO_TOP))
    {
      // Second point.
      point.rx() = m_currentDragPoint.rx();
      point.ry() = m_currentDragPoint.ry();
      points.push_back(point);
      // qDebug() << "Second point:" << point;

      // Third point.
      point.rx() = m_currentDragPoint.rx();
      point.ry() = m_currentDragPoint.ry() + m_integrationScopeRhombHeight;
      points.push_back(point);
      // qDebug() << "Third point:" << point;

      // Fourth point.
      point.rx() = m_startDragPoint.x();
      point.ry() = m_startDragPoint.y() + m_integrationScopeRhombHeight;
      points.push_back(point);
      // qDebug() << "Last point:" << point;
    }

  if(static_cast<int>(m_dragDirections) &
       static_cast<int>(DragDirections::RIGHT_TO_LEFT) &&
     static_cast<int>(m_dragDirections) &
       static_cast<int>(DragDirections::BOTTOM_TO_TOP))
    {
      // Second point.
      point.rx() = m_startDragPoint.rx();
      point.ry() = m_startDragPoint.ry() + m_integrationScopeRhombHeight;
      points.push_back(point);
      // qDebug() << "Second point:" << point;

      // Third point.
      point.rx() = m_currentDragPoint.rx();
      point.ry() = m_currentDragPoint.ry() + m_integrationScopeRhombHeight;
      points.push_back(point);
      // qDebug() << "Third point:" << point;

      // Fourth point.
      point.rx() = m_currentDragPoint.x();
      point.ry() = m_currentDragPoint.y();
      points.push_back(point);
      // qDebug() << "Last point:" << point;
    }

  if(static_cast<int>(m_dragDirections) &
       static_cast<int>(DragDirections::LEFT_TO_RIGHT) &&
     static_cast<int>(m_dragDirections) &
       static_cast<int>(DragDirections::TOP_TO_BOTTOM))
    {
      // Second point.
      point.rx() = m_startDragPoint.x();
      point.ry() = m_startDragPoint.y() - m_integrationScopeRhombHeight;
      points.push_back(point);
      // qDebug() << "Second point:" << point;

      // Third point.
      point.rx() = m_currentDragPoint.rx();
      point.ry() = m_currentDragPoint.ry() - m_integrationScopeRhombHeight;
      points.push_back(point);
      // qDebug() << "Third point:" << point;

      // Fourth point.
      point.rx() = m_currentDragPoint.rx();
      point.ry() = m_currentDragPoint.ry();
      points.push_back(point);
      // qDebug() << "Last point:" << point;
    }

  if(static_cast<int>(m_dragDirections) &
       static_cast<int>(DragDirections::RIGHT_TO_LEFT) &&
     static_cast<int>(m_dragDirections) &
       static_cast<int>(DragDirections::TOP_TO_BOTTOM))
    {
      // Second point.
      point.rx() = m_currentDragPoint.rx();
      point.ry() = m_currentDragPoint.ry();
      points.push_back(point);
      // qDebug() << "Second point:" << point;

      // Third point.
      point.rx() = m_currentDragPoint.rx();
      point.ry() = m_currentDragPoint.ry() - m_integrationScopeRhombHeight;
      points.push_back(point);
      // qDebug() << "Third point:" << point;

      // Fourth point.
      point.rx() = m_startDragPoint.rx();
      point.ry() = m_startDragPoint.ry() - m_integrationScopeRhombHeight;
      points.push_back(point);
      // qDebug() << "Last point:" << point;
    }

  msp_integrationScope = std::make_shared<IntegrationScopeRhomb>(points);

  // qDebug() << "Created an integration scope vertical rhomboid with"
  //          << points.size() << "points:" << msp_integrationScope->toString();
}


void
BasePlotContext::updateIntegrationScopeRhomb()
{
  // qDebug() << toString();

  // By essence, IntegrationScopeRhomb is a rhomboid polygon. Just set the
  // points. There are two kinds of rhomboid integration scopes: horizontal and
  // vertical.

  /*
                            +----------+
                            |          |
                           |          |
                          |          |
                         |          |
                        |          |
                       |          |
                      |          |
                      +----------+
                      ----width---
  */

  // As visible here, the fixed size of the rhomboid (using the S key in the
  // plot widget) is the *horizontal* side (that is, the rhomboid has a non-0
  // width)..

  // However, it might be useful to be able to draw rhomboid integration scopes
  // like this, that would correspond to the rhomboid above after a transpose
  // operation.

  /*
                                       +
                                     . |
                                   .   |
                                 .     |
                               .       +
                             .       .
                           .       .
                         .       .
                       +       .
                     | |     .
            height   | |   .
                     | | .
                       +

*/

  // As visible here, the fixed size of the rhomboid (using the S key in the
  // plot widget) is the vertical side (that is, the rhomboid has a non-0
  // height).

  // The general rule is thus that when the m_integrationScopeRhombWidth is
  // not-0, then the first shape is considered, while when the
  // m_integrationScopeRhombHeight is non-0, then the second shape is
  // considered.

  // This function is called when the user has dragged the cursor (left or right
  // button, not for or for integration, respectively) with the 'Alt' modifier
  // key pressed, so that they want to perform a rhomboid integration scope
  // calculation.

  // Of course, the integration scope in the context might not be a rhomboid
  // scope, because we might enter this function as a very firt switch from
  // scope or scopeRect to scopeRhomb. The only indication we have to direct the
  // creation of a horizontal or vertical rhomboid is the
  // m_integrationScopeRhombWidth/m_integrationScopeRhombHeight recorded in the
  // plot widget that owns this plot context.

  // qDebug() << "In updateIntegrationScopeRhomb, m_integrationScopeRhombWidth:"
  //          << m_integrationScopeRhombWidth
  //          << "and m_integrationScopeRhombHeight:"
  //          << m_integrationScopeRhombHeight;

  if(!m_integrationScopeRhombWidth && !m_integrationScopeRhombHeight)
    qFatal(
      "Both m_integrationScopeRhombWidth and m_integrationScopeRhombHeight of "
      "rhomboid integration scope cannot be 0.");

  if(m_integrationScopeRhombWidth != 0)
    return updateIntegrationScopeRhombHorizontal();
  else if(m_integrationScopeRhombHeight != 0)
    return updateIntegrationScopeRhombVertical();
}

BasePlotContext &
BasePlotContext::operator=(const BasePlotContext &other)
{
  if(this == &other)
    return *this;

  m_dataKind = other.m_dataKind;

  m_isMouseDragging  = other.m_isMouseDragging;
  m_wasMouseDragging = other.m_wasMouseDragging;

  m_isKeyBoardDragging            = other.m_isKeyBoardDragging;
  m_isLeftPseudoButtonKeyPressed  = other.m_isLeftPseudoButtonKeyPressed;
  m_isRightPseudoButtonKeyPressed = other.m_isRightPseudoButtonKeyPressed;
  m_wassKeyBoardDragging          = other.m_wassKeyBoardDragging;

  m_startDragPoint         = other.m_startDragPoint;
  m_currentDragPoint       = other.m_currentDragPoint;
  m_lastCursorHoveredPoint = other.m_lastCursorHoveredPoint;

  m_selectionPolygon            = other.m_selectionPolygon;
  msp_integrationScope          = other.msp_integrationScope;
  m_integrationScopeRhombWidth  = other.m_integrationScopeRhombWidth;
  m_integrationScopeRhombHeight = other.m_integrationScopeRhombHeight;

  // The effective range of the axes.
  m_xRange = other.m_xRange;
  m_yRange = other.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).
  m_wasClickOnXAxis = other.m_wasClickOnXAxis;
  m_wasClickOnYAxis = other.m_wasClickOnYAxis;

  m_isMeasuringDistance = other.m_isMeasuringDistance;

  // 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.
  m_xRegionRangeStart = other.m_xRegionRangeStart;
  m_xRegionRangeEnd   = other.m_xRegionRangeEnd;

  m_yRegionRangeStart = other.m_yRegionRangeStart;
  m_yRegionRangeEnd   = other.m_yRegionRangeEnd;

  m_xDelta = other.m_xDelta;
  m_yDelta = other.m_yDelta;

  m_pressedKeyCode  = other.m_pressedKeyCode;
  m_releasedKeyCode = other.m_releasedKeyCode;

  m_keyboardModifiers = other.m_keyboardModifiers;

  m_lastPressedMouseButton  = other.m_lastPressedMouseButton;
  m_lastReleasedMouseButton = other.m_lastReleasedMouseButton;

  m_pressedMouseButtons = other.m_pressedMouseButtons;

  m_mouseButtonsAtMousePress   = other.m_mouseButtonsAtMousePress;
  m_mouseButtonsAtMouseRelease = other.m_mouseButtonsAtMouseRelease;

  return *this;
}

DragDirections
BasePlotContext::recordDragDirections()
{
  int drag_directions = static_cast<int>(DragDirections::NOT_SET);

  if(m_currentDragPoint.x() > m_startDragPoint.x())
    drag_directions |= static_cast<int>(DragDirections::LEFT_TO_RIGHT);
  else
    drag_directions |= static_cast<int>(DragDirections::RIGHT_TO_LEFT);

  if(m_currentDragPoint.y() > m_startDragPoint.y())
    drag_directions |= static_cast<int>(DragDirections::BOTTOM_TO_TOP);
  else
    drag_directions |= static_cast<int>(DragDirections::TOP_TO_BOTTOM);

  // qDebug() << "DragDirections:" << drag_directions;

  m_dragDirections = static_cast<DragDirections>(drag_directions);

  return static_cast<DragDirections>(drag_directions);
}


QString
BasePlotContext::toString() const
{
  QString text("Context:");

  text += QString("data kind: %1").arg(static_cast<int>(m_dataKind));

  text += QString(" isMouseDragging: %1 -- wasMouseDragging: %2")
            .arg(m_isMouseDragging ? "true" : "false")
            .arg(m_wasMouseDragging ? "true" : "false");

  text += QString(" -- startDragPoint : (%1, %2)")
            .arg(m_startDragPoint.x())
            .arg(m_startDragPoint.y());

  text += QString(" -- currentDragPoint : (%1, %2)")
            .arg(m_currentDragPoint.x())
            .arg(m_currentDragPoint.y());

  text += QString(" -- lastCursorHoveredPoint : (%1, %2)")
            .arg(m_lastCursorHoveredPoint.x())
            .arg(m_lastCursorHoveredPoint.y());

  // Document how the mouse cursor is being dragged.
  if(m_isMouseDragging)
    {
      if(static_cast<int>(m_dragDirections) &
         static_cast<int>(DragDirections::LEFT_TO_RIGHT))
        text += " -- dragging from left to right";
      else if(static_cast<int>(m_dragDirections) &
              static_cast<int>(DragDirections::RIGHT_TO_LEFT))
        text += " -- dragging from right to left";
      if(static_cast<int>(m_dragDirections) &
         static_cast<int>(DragDirections::TOP_TO_BOTTOM))
        text += " -- dragging from top to bottom";
      if(static_cast<int>(m_dragDirections) &
         static_cast<int>(DragDirections::BOTTOM_TO_TOP))
        text += " -- dragging from bottom to top";
    }

  // The integration scope
  text += " -- ";
  text += msp_integrationScope->toString();

  text +=
    QString(" -- xRange: (%1, %2)").arg(m_xRange.lower).arg(m_xRange.upper);

  text +=
    QString(" -- yRange: (%1, %2)").arg(m_yRange.lower).arg(m_yRange.upper);

  text += QString(" -- wasClickOnXAxis: %1")
            .arg(m_wasClickOnXAxis ? "true" : "false");
  text += QString(" -- wasClickOnYAxis: %1")
            .arg(m_wasClickOnYAxis ? "true" : "false");
  text += QString(" -- isMeasuringDistance: %1")
            .arg(m_isMeasuringDistance ? "true" : "false");

  text += QString(" -- xRegionRangeStart: %1 -- xRegionRangeEnd: %2")
            .arg(m_xRegionRangeStart)
            .arg(m_xRegionRangeEnd);

  text += QString(" -- yRegionRangeStart: %1 -- yRegionRangeEnd: %2")
            .arg(m_yRegionRangeStart)
            .arg(m_yRegionRangeEnd);

  text += QString(" -- xDelta: %1 -- yDelta: %2").arg(m_xDelta).arg(m_yDelta);

  text += QString(" -- pressedKeyCode: %1").arg(m_pressedKeyCode);

  text += QString(" -- keyboardModifiers: %1").arg(m_keyboardModifiers);

  text +=
    QString(" -- lastPressedMouseButton: %1").arg(m_lastPressedMouseButton);

  text +=
    QString(" -- lastReleasedMouseButton: %1").arg(m_lastReleasedMouseButton);

  text += QString(" -- pressedMouseButtons: %1").arg(m_pressedMouseButtons);

  text +=
    QString(" -- mouseButtonsAtMousePress: %1").arg(m_mouseButtonsAtMousePress);

  text += QString(" -- mouseButtonsAtMouseRelease: %1")
            .arg(m_mouseButtonsAtMouseRelease);

  return text;
}


} // namespace pappso
