/* This code comes right from the msXpertSuite software project.
 *
 * msXpertSuite - mass spectrometry software suite
 * -----------------------------------------------
 * Copyright(C) 2009,...,2018 Filippo Rusconi
 *
 * http://www.msxpertsuite.org
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 *
 * END software license
 */


#pragma once

/////////////////////// StdLib includes
#include <memory>


/////////////////////// Qt includes
#include <QObject>
#include <QString>
#include <QWidget>
#include <QBrush>
#include <QColor>
#include <QVector>


/////////////////////// QCustomPlot
#include <qcustomplot.h>


/////////////////////// Local includes
#include "../../exportinmportconfig.h"
#include "../../types.h"
#include "../../processing/combiners/selectionpolygon.h"
#include "baseplotcontext.h"


namespace pappso
{

 enum class SelectionDrawingLines
 {
  NOT_SET = 0x0000,
  
  TOP_LINE    = 1 << 0,
  BOTTOM_LINE = 1 << 1,
  
  HORIZONTAL_LINES = (TOP_LINE | BOTTOM_LINE),
  
  RIGHT_LINE = 1 << 2,
  LEFT_LINE  = 1 << 3,
  
  VERTICAL_LINES = (RIGHT_LINE | LEFT_LINE),
  
  FULL_POLYGON = (HORIZONTAL_LINES | VERTICAL_LINES)
 };
 
enum class RangeType
{
  outermost = 1,
  innermost = 2,
};

class BasePlotWidget;

typedef std::shared_ptr<BasePlotWidget> BasePlotWidgetSPtr;
typedef std::shared_ptr<const BasePlotWidget> BasePlotWidgetCstSPtr;

class PMSPP_LIB_DECL BasePlotWidget : public QCustomPlot
{
  Q_OBJECT

  public:
  explicit BasePlotWidget(QWidget *parent);
  explicit BasePlotWidget(QWidget *parent,
                          const QString &x_axis_label,
                          const QString &y_axis_label);

  virtual ~BasePlotWidget();

  virtual bool setupWidget();

  virtual void setPen(const QPen &pen);
  virtual const QPen &getPen() const;

  virtual void setPlottingColor(QCPAbstractPlottable *plottable_p,
                                const QColor &new_color);
  virtual void setPlottingColor(int index, const QColor &new_color);

  virtual QColor getPlottingColor(QCPAbstractPlottable *plottable_p) const;
  virtual QColor getPlottingColor(int index = 0) const;

  virtual void setAxisLabelX(const QString &label);
  virtual void setAxisLabelY(const QString &label);

  // AXES RANGE HISTORY-related functions
  virtual void resetAxesRangeHistory();
  virtual void updateAxesRangeHistory();
  virtual void restorePreviousAxesRangeHistory();
  virtual void restoreAxesRangeHistory(std::size_t index);
  // AXES RANGE HISTORY-related functions


  /// KEYBOARD-related EVENTS
  virtual void keyPressEvent(QKeyEvent *event);
  virtual void keyReleaseEvent(QKeyEvent *event);

  virtual void spaceKeyReleaseEvent(QKeyEvent *event);

  virtual void directionKeyPressEvent(QKeyEvent *event);
  virtual void directionKeyReleaseEvent(QKeyEvent *event);

  virtual void mousePseudoButtonKeyPressEvent(QKeyEvent *event);
  virtual void mousePseudoButtonKeyReleaseEvent(QKeyEvent *event);
  /// KEYBOARD-related EVENTS


  /// MOUSE-related EVENTS
  virtual void mousePressHandler(QMouseEvent *event);
  virtual void mouseReleaseHandler(QMouseEvent *event);
  virtual void mouseReleaseHandlerLeftButton();
  virtual void mouseReleaseHandlerRightButton();

  virtual void mouseWheelHandler(QWheelEvent *event);

  virtual void mouseMoveHandler(QMouseEvent *event);
  virtual void mouseMoveHandlerNotDraggingCursor();
  virtual void mouseMoveHandlerDraggingCursor();
  virtual void mouseMoveHandlerLeftButtonDraggingCursor();
  virtual void mouseMoveHandlerRightButtonDraggingCursor();

  virtual void axisDoubleClickHandler(QCPAxis *axis,
                                      QCPAxis::SelectablePart part,
                                      QMouseEvent *event);
  bool isClickOntoXAxis(const QPointF &mousePoint);
  bool isClickOntoYAxis(const QPointF &mousePoint);
  /// MOUSE-related EVENTS


  /// MOUSE MOVEMENTS mouse/keyboard-triggered
  int dragDirection();
  virtual void moveMouseCursorGraphCoordToGlobal(QPointF plot_coordinates);
  virtual void moveMouseCursorPixelCoordToGlobal(QPointF local_coordinates);
  virtual void horizontalMoveMouseCursorCountPixels(int pixel_count);
  virtual QPointF horizontalGetGraphCoordNewPointCountPixels(int pixel_count);
  virtual void verticalMoveMouseCursorCountPixels(int pixel_count);
  virtual QPointF verticalGetGraphCoordNewPointCountPixels(int pixel_count);
  /// MOUSE MOVEMENTS mouse/keyboard-triggered


  /// RANGE-related functions
  virtual QCPRange getRangeX(bool &found_range, int index) const;
  virtual QCPRange getRangeY(bool &found_range, int index) const;
  QCPRange getRange(Axis axis, RangeType range_type, bool &found_range) const;

  virtual QCPRange getInnermostRangeX(bool &found_range) const;
  virtual QCPRange getOutermostRangeX(bool &found_range) const;

  virtual QCPRange getInnermostRangeY(bool &found_range) const;
  virtual QCPRange getOutermostRangeY(bool &found_range) const;

  void yMinMaxOnXAxisCurrentRange(double &min,
                                  double &max,
                                  QCPAbstractPlottable *plottable_p = nullptr);
  void yMinMaxOnXAxisCurrentRange(double &min, double &max, int index);
  /// RANGE-related functions


  /// PLOTTING / REPLOTTING functions
  virtual void axisRescale();
  virtual void axisReframe();
  virtual void axisZoom();
  virtual void axisPan();

  virtual void
  replotWithAxesRanges(QCPRange xAxisRange, QCPRange yAxisRange, Axis axis);

  virtual void replotWithAxisRangeX(double lower, double upper);
  virtual void replotWithAxisRangeY(double lower, double upper);
  /// PLOTTING / REPLOTTING functions


  /// PLOT ITEMS : TRACER TEXT ITEMS...
  virtual void hideAllPlotItems();

  virtual void showTracers();
  virtual void hideTracers();

  virtual void drawXScopeSpanFeatures();
  virtual void drawYScopeSpanFeatures();

  virtual void calculateDragDeltas();

  virtual bool isVerticalDisplacementAboveThreshold();

  virtual void
  drawSelectionRectangleAndPrepareZoom(bool as_line_segment = false,
                                       bool for_integration = false);

  virtual void updateIntegrationScopeDrawing(bool as_line_segment = false,
                                        bool for_integration = false);

  virtual void resetSelectionRectangle();
  virtual void hideSelectionRectangle(bool reset_values = false);
  virtual bool isSelectionRectangleVisible();
  virtual SelectionDrawingLines whatIsVisibleOfTheSelectionRectangle();

  /// PLOT ITEMS : TRACER TEXT ITEMS...


  virtual void setFocus();

  virtual void redrawPlotBackground(QWidget *focusedPlotWidget);

  virtual void updateContextXandYAxisRanges();

  virtual const BasePlotContext &getContext() const;

  signals:

  void setFocusSignal();

  void lastCursorHoveredPointSignal(const QPointF &pointf);

  void plotRangesChangedSignal(const BasePlotContext &context);

  void xAxisMeasurementSignal(const BasePlotContext &context, bool with_delta);

  void keyPressEventSignal(const BasePlotContext &context);
  void keyReleaseEventSignal(const BasePlotContext &context);

  void mouseReleaseEventSignal(const BasePlotContext &context);

  void mouseWheelEventSignal(const BasePlotContext &context);

  void plottableSelectionChangedSignal(QCPAbstractPlottable *plottable_p,
                                       bool selected);

  void integrationRequestedSignal(const BasePlotContext &context);

  void plottableDestructionRequestedSignal(BasePlotWidget *base_plot_widget_p,
                                           QCPAbstractPlottable *plottable_p,
                                           const BasePlotContext &context);

  protected:
  //! Name of the plot widget.
  QString m_name = "NOT_SET";

  //! Description of the plot widget.
  QString m_desc = "NOT_SET";

  //! The name of the data file from which the mass data were read.
  QString m_fileName;

  QString m_axisLabelX;
  QString m_axisLabelY;

  BasePlotContext m_context;

  int m_leftMousePseudoButtonKey  = Qt::Key_Less;
  int m_rightMousePseudoButtonKey = Qt::Key_Greater;

  //! Rectangle defining the borders of zoomed-in/out data.
  // QCPItemRect *mp_zoomRectItem = nullptr;

  // The four lines that are needed to craft the selection rectangle.
  QCPItemLine *mp_selectionRectangeLine1 = nullptr;
  QCPItemLine *mp_selectionRectangeLine2 = nullptr;
  QCPItemLine *mp_selectionRectangeLine3 = nullptr;
  QCPItemLine *mp_selectionRectangeLine4 = nullptr;

  //! Text describing the x-axis delta value during a drag operation.
  QCPItemText *mp_xDeltaTextItem = nullptr;
  QCPItemText *mp_yDeltaTextItem = nullptr;

  //! Tells if the tracers should be visible.
  bool m_shouldTracersBeVisible = true;

  //! Horizontal position tracer
  QCPItemLine *mp_hPosTracerItem = nullptr;

  //! Vertical position tracer
  QCPItemLine *mp_vPosTracerItem = nullptr;

  //! Vertical selection start tracer (typically in green).
  QCPItemLine *mp_vStartTracerItem = nullptr;

  //! Vertical selection end tracer (typically in red).
  QCPItemLine *mp_vEndTracerItem = nullptr /*only vertical*/;

  //! Index of the last axis range history item.
  /*!

    Each time the user modifies the ranges (x/y axis) during panning or
    zooming of the graph, the new axis ranges are stored in a axis ranges
    history list. This index allows to point to the last range of that
    history.

*/
  std::size_t m_lastAxisRangeHistoryIndex = 0;

  //! List of x axis ranges occurring during the panning zooming actions.
  std::vector<QCPRange *> m_xAxisRangeHistory;

  //! List of y axis ranges occurring during the panning zooming actions.
  std::vector<QCPRange *> m_yAxisRangeHistory;

  //! How many mouse move events must be skipped */
  /*!

    when the data are so massive that the graph panning becomes sluggish. By
    default, the value is 10 events to be skipped before accounting one. The
    "fat data" mouse movement handler mechanism is actuated by using a
    keyboard key combination. There is no automatic shift between normal
    processing and "fat data" processing.

*/
  int m_mouseMoveHandlerSkipAmount = 10;

  //! Counter to handle the "fat data" mouse move event handling.
  /*!

    \sa m_mouseMoveHandlerSkipAmount.

*/
  int m_mouseMoveHandlerSkipCount = 0;

  // QColor m_unfocusedColor = QColor(Qt::lightGray);
  // QColor m_unfocusedColor = QColor(230, 230, 230, 255);

  //! Color used for the background of unfocused plot.
  QColor m_unfocusedColor = QColor("lightgray");
  //! Color used for the background of unfocused plot.
  QBrush m_unfocusedBrush = QBrush(m_unfocusedColor);

  //! Color used for the background of focused plot.
  QColor m_focusedColor = QColor(Qt::transparent);
  //! Color used for the background of focused plot.
  QBrush m_focusedBrush = QBrush(m_focusedColor);

  //! Pen used to draw the graph and textual elements in the plot widget.
  QPen m_pen;

  virtual void createAllAncillaryItems();
  virtual void updateIntegrationScope(bool for_integration = false);
  virtual void updateIntegrationScopeRect(bool for_integration = false);
  virtual void updateIntegrationScopeHorizontalRhomb(bool for_integration = false);
  virtual void updateIntegrationScopeVerticalRhomb(bool for_integration = false);
  virtual void updateIntegrationScopeRhomb(bool for_integration = false);
  virtual QString allLayerNamesToString() const;
  virtual QString layerableLayerName(QCPLayerable *layerable_p) const;
  virtual int layerableLayerIndex(QCPLayerable *layerable_p) const;
};


} // namespace pappso


Q_DECLARE_METATYPE(pappso::BasePlotContext);
extern int basePlotContextMetaTypeId;

Q_DECLARE_METATYPE(pappso::BasePlotContext *);
extern int basePlotContextPtrMetaTypeId;
