/* BEGIN software license
 *
 * msXpertSuite - mass spectrometry software suite
 * -----------------------------------------------
 * Copyright (C) 2009--2020 Filippo Rusconi
 *
 * http://www.msxpertsuite.org
 *
 * This file is part of the msXpertSuite project.
 *
 * The msXpertSuite project is the successor of the massXpert project. This
 * project now includes various independent modules:
 *
 * - massXpert, model polymer chemistries and simulate mass spectrometric data;
 * - mineXpert, a powerful TIC chromatogram/mass spectrum viewer/miner;
 *
 * 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


/////////////////////// Qt includes
#include <QWidget>

/////////////////////// QCustomPlot


/////////////////////// pappsomspp includes
#include <pappsomspp/gui/plotwidget/baseplotwidget.h>
#include <pappsomspp/core/processing/combiners/mzintegrationparams.h>

#include <MsXpS/libXpertMassGui/ManagedQDoubleSpinBox.hpp>

/////////////////////// Local includes
#include "../nongui/DiscoveriesPreferences.hpp"
#include "../nongui/MsRunDataSet.hpp"
#include "../nongui/ProcessingFlow.hpp"
#include "MsFragmentationSpec.hpp"
#include "MzIntegrationParamsDlg.hpp"

namespace Ui
{
class BasePlotCompositeWidget;
}

namespace MsXpS
{
namespace MineXpert
{

class BasePlotWnd;
class MsFragmentationSpecDlg;
class ProcessingFlowViewerDlg;

enum class NewTraceHandlingMethod
{
  NOT_SET = 0,
  NEW_PLOT,
  REPLACE,
  OVERLAY,
  PLUS_COMBINE,
  MINUS_COMBINE,
  LAST,
};

enum class TraceCombinationSign
{
  NOT_SET = 0,
  MINUS,
  PLUS,
};

// The BasePlotCompositeWidget class is aimed at implementing all the basic
// features that are required to plot any kind of QCustomPlot-based plots, like
// QCPGraph_s or QCPColorMap_s. Since all these plots all derive from
// QCPAbstractPlottable, we can develop a plot widget hierarchy that manages a
// lot of these plots' functionalities into the base class.
class BasePlotCompositeWidget: public QWidget
{
  Q_OBJECT

  friend class BasePlotWnd;

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

  virtual ~BasePlotCompositeWidget();

  BasePlotWnd *getParentWnd();
  BasePlotWnd *getParent();

  virtual void
  msFragmentationSpecReady(const MsFragmentationSpec &ms_fragmentation_spec);
  void setMzIntegrationParams(
    const pappso::MzIntegrationParams &mz_integration_params);

  pappso::FilterNameInterfaceCstSPtr filter(const QString filter_name,
                                            const QCPAbstractPlottable *plottable_p) const;

  bool isPinnedDown() const;

  virtual const pappso::BasePlotWidget *getPlotWidget() const;

  virtual const ProcessingFlow *getCstProcessingFlowPtrForPlottable(
      const QCPAbstractPlottable *plottable_p) const;
  virtual ProcessingFlow *getProcessingFlowPtrForPlottable(const QCPAbstractPlottable *plottable_p);

  virtual void removePlottableProcessingFlowMapItem(const QCPAbstractPlottable *plottable_p);

  virtual MsRunDataSetCstSPtr getMsRunDataSetCstSPtrForPlottable(
      const QCPAbstractPlottable *plottable_p) const;

  QCPAbstractPlottable *plottableToBeUsedAsIntegrationSource() const;
  QCPAbstractPlottable *firstPlottable() const;
  QList<QCPAbstractPlottable *> plottablesToBeUsedAsIntegrationDestination() const;

  virtual bool isFocussed() const;
  void resizeEvent(QResizeEvent *event) override;
  void updateSummedUpIntensitiesWidgetPosition();

  public slots:

  virtual void lastCursorHoveredPoint(const QPointF &pointf);

  virtual void plotRangesChanged(const pappso::BasePlotContext &context);
  void xAxisMeasurement(const pappso::BasePlotContext &context);

  virtual void plotWidgetKeyPressEvent(const pappso::BasePlotContext &context);
  virtual void
  plotWidgetKeyReleaseEvent(const pappso::BasePlotContext &context);

  virtual void
  plotWidgetMousePressEvent(QMouseEvent *event,
                            const pappso::BasePlotContext &context);
  virtual void
  plotWidgetMouseReleaseEvent(QMouseEvent *event,
                              const pappso::BasePlotContext &context);

  virtual void plottableSelectionChanged(QCPAbstractPlottable *plottable_p,
                                         bool is_selected);

  void mzIntegrationParamsChanged(
    pappso::MzIntegrationParams &mz_integration_params);

  virtual void newTicIntensityCalculated(double tic_intensity,
                                         const QColor &color) const;
  virtual void resetTicIntensity();

  pappso::MzIntegrationParams * /*heap-allocated*/
  getSensibleMzIntegrationParams(const QCPAbstractPlottable *plottable_p);

  void msFragmentationSpecChanged(MsFragmentationSpec ms_fragmentation_spec);

  void pinDown(bool push_pinned);

  void showMsFragmentationSpecDlg();
  void showMzIntegrationParamsDlg();

  void redrawBackground(bool has_focus);

  bool savePlotToPdfFile(const QString &file_name,
                         bool no_cosmetic_pen,
                         int width,
                         int height,
                         const QString &pdf_creator,
                         const QString &title) const;

  bool savePlotToSvgFile(const QString &file_name);

  bool saveSvgPlotToClipboard();
  bool saveRasterPlotToClipboard();

  bool savePlotToPngFile(const QString &file_name,
                         int width,
                         int height,
                         int scale,
                         int quality = 100) const;

  bool savePlotToJpgFile(const QString &file_name,
                         int width,
                         int height,
                         int scale,
                         int quality = 100) const;

  std::pair<int, int> computePlotSize(std::pair<int, int> requested,
                                      std::pair<int, int> actual) const;

  void showProcessingFlow();

  void resetPlotRangesHistory();

  QString getAnalysisStanza();
  QString craftAnalysisStanza(const pappso::BasePlotContext &context);
  QString craftAnalysisStanzaOld(const pappso::BasePlotContext &context);

  void setPlottingColor(QCPAbstractPlottable *plottable_p, const QColor &color);
  void setSampleNameColor(const QColor &color);

  void msLevelValueChanged(int value);

  void summmedUpIntensitiesCheckBoxStateChanged(Qt::CheckState check_state);

  signals:

  void plotWidgetMousePressEventSignal(const pappso::BasePlotContext &context);
  void plotWidgetMouseReleaseEventSignal(QMouseEvent *event);

  void plotRangesChangedSignal(const pappso::BasePlotContext &context);

  protected:
  NewTraceHandlingMethod m_newTraceHandlingMethod =
    NewTraceHandlingMethod::NEW_PLOT;

  // This is the plot widget that is immediately derived from QCustomPlot, it is
  // not a composite widget it is a QCustomPlot-derived widget in which I coded
  // all the specific behaviour we need to work with trace of different types:
  // tic chromatograms, mass spectra, drift spectra...

  // This widget is not instantiated in this class, it is instantiated in each
  // one of the specialied composite widget classes
  // (MassSpecTracePlotCompositeWidget:102, for example)
  pappso::BasePlotWidget *mp_plotWidget = nullptr;

  BasePlotWnd *mp_parentWnd = nullptr;

  QString m_axisLabelX;
  QString m_axisLabelY;

  mutable double m_lastTicIntensity = qQNaN();

  // Managed because we capture keyboard key strokes so that they do not
  // bubble up to the composite widget.
  libXpertMassGui::ManagedQDoubleSpinBox *mp_summedUpIntensitiesWidget =
    nullptr;

  MsFragmentationSpec m_msFragmentationSpec;
  MsFragmentationSpecDlg *mp_msFragmentationSpecifDlg = nullptr;

  MzIntegrationParamsDlg *mp_mzIntegrationParamsDlg = nullptr;

  ProcessingFlowViewerDlg *mp_processingFlowViewerDlg = nullptr;

  ::Ui::BasePlotCompositeWidget *m_ui;

  //! 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);

  bool m_isFocussed = false;

  //! Key code of the last keyboard key being pressed.
  int m_plotWidgetPressedKeyCode;

  //! Key code of the last keyboard key being released.
  int m_plotWidgetReleasedKeyCode;

  // We can have as many graphs (plottable) as necessary in the
  // QCustomPlot-derived plot widget. Each graph will have its own "raison
  // d'être", that is, its own ProcessingFlow. To connect back any graph to the
  // matching MsRunDataSet, the ProcessingFlow instance that is related to it
  // via this map contains a pointer to that ms run data set, which in turn
  // contains all that is necessary to get to the original file, the ms run id
  // and the ms run reader.
  std::map<QCPAbstractPlottable *, ProcessingFlow *>
    m_plottableProcessingFlowMap;

  QMenu *mp_mainMenu         = nullptr;
  QMenu *mp_plotGraphicsMenu = nullptr;
  QMenu *mp_integrationsMenu = nullptr;

  virtual void setupWidget() = 0;
  virtual void createMainMenu();
  QFile *getAnalysisFilePtr();
  QSharedPointer<DiscoveriesPreferences> getDiscoveriesPreferences();

  QString m_analysisStanza;
};


} // namespace MineXpert

} // namespace MsXpS
