/* BEGIN software license
 *
 * msXpertSuite - mass spectrometry software suite
 * -----------------------------------------------
 * Copyright(C) 2009,...,2018 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
 */


#include <qcustomplot.h>

///////////////////////////// Qt include


///////////////////////////// Local includes
#include <minexpert/gui/ColorMapPlotWidget.hpp>
#include <minexpert/gui/ColorMapWnd.hpp>
#include <minexpert/nongui/AnalysisPreferences.hpp>
#include <minexpert/gui/MainWindow.hpp>
#include <minexpert/nongui/History.hpp>
#include <minexpert/nongui/MassDataIntegrator.hpp>


namespace msXpSmineXpert
{


/*/js/ Class: ColorMapPlotWidget
 * <comment>This class cannot be instantiated with the new operator. Objects
 * of this class are made available to the scripting environment under the
 * form of object variable names colorMapPlotWidget[index], with [index]
 * being 0 for the first object created and being incremented each time a new
 * ColorMapPlotWidget is created.</comment>
 */


//! Construct a ColorMapPlotWidget instance.
/*!

  The instance will be initialized with the parameters.

  \param parent parent widget.

  \param name name of this window to be used for the decoration (title)

  \param desc description of this window to be used for the plot decoration.

  \param massSpecDataSet MassSpecDataSet instance in which to get the data.
  Defaults to nullptr.

  \param fileName name of the file from which the data were initially read
  (also for decoration of the plots). Defaults to the empty string.

  \param isMultiGraph tells if the graph should be considered to be multi.
  Defaults to false.

*/
ColorMapPlotWidget::ColorMapPlotWidget(QWidget *parent,
                                       const QString &name,
                                       const QString &desc,
                                       const MassSpecDataSet *massSpecDataSet,
                                       const QString &fileName,
                                       bool isMultiGraph)
  : AbstractPlotWidget{
      parent, name, desc, massSpecDataSet, fileName, isMultiGraph}
{
  AbstractPlotWidget::registerQtKeyCode(
    Qt::Key_R,
    "Press R while right mouse click-drag to integrate to a XIC chromatogram");
  AbstractPlotWidget::registerQtKeyCode(
    Qt::Key_S,
    "Press S while right mouse click-drag to integrate to a mass spectrum");
  AbstractPlotWidget::registerQtKeyCode(
    Qt::Key_D,
    "Press D while right mouse click-drag to integrate to a drift spectrum");
  AbstractPlotWidget::registerQtKeyCode(Qt::Key_I,
                                        "Press I while right mouse click-drag "
                                        "to integrate to a TIC intensity "
                                        "value");

  // Give the axes some labels:
  xAxis->setLabel("drift time (ms)");
  yAxis->setLabel("m/z");

  clearPlottables();

  setupWidget();

  mp_colorMap = new QCPColorMap(xAxis, yAxis);
}


//! Destruct \c this ColorMapPlotWidget instance.
ColorMapPlotWidget::~ColorMapPlotWidget()
{
}


//! Return the arbitrary integration type that this plot widget can handle
int
ColorMapPlotWidget::arbitraryIntegrationType()
{
  return IntegrationType::DT_TO_XXX | IntegrationType::MZ_TO_XXX;
}


//! Setup the widgetry.
bool
ColorMapPlotWidget::setupWidget()
{
  QPen pen(m_pen);

  pen.setColor("white");
  m_zoomRect->setPen(pen);

  m_descText->setColor("white");
  m_descText->setPen(pen);

  m_hStartTracer->setPen(pen);
  m_selectLine->setPen(m_pen);
  m_xDeltaText->setColor("white");

  return true;
}


//! Manually set the data for one of the color map data points.
/*!

  Since this widget is a color map plot widget, the z component of the (x,y,z)
  triad will be converted into a color that matches the z value in a color
  scale.

*/
void
ColorMapPlotWidget::setColorMapPointData(double key, double val, double z)
{
  mp_colorMap->data()->setData(key, val, z);
}


//! Initialize the color map.
/*!

  \param keySize the number of cells on the x-axis (keys)

  \param valSize the number of cells on the y-axix (values)

  \param keyRange the borders of the x-axis

  \param valRange the borders on the y-axis

  \param fillingValue value to use to fill-in all the (\p keySize * \p
  valSize) cells of the map.

*/
void
ColorMapPlotWidget::setColorMapSizeAndRanges(int keySize,
                                             int valSize,
                                             QCPRange keyRange,
                                             QCPRange valRange,
                                             double fillingValue)
{
  mp_colorMap->data()->setSize(keySize, valSize);
  mp_colorMap->data()->setRange(keyRange, valRange);
  mp_colorMap->data()->fill(fillingValue);
}


//! Set the color to be used for decorating the color map.
/*!

  When created, a new color map must have an identifying color that matches
  the color of the initial TIC chromatogram plot so that it can be related to
  the same initial data set.

*/
void
ColorMapPlotWidget::setDecoratingColor(const QColor &color)
{
  QCPAxis *xAxis = mp_colorMap->keyAxis();
  QCPAxis *yAxis = mp_colorMap->valueAxis();

  QPen basePen;
  basePen = xAxis->basePen();
  basePen.setColor(color);

  xAxis->setBasePen(basePen);
  xAxis->setLabelColor(color);
  xAxis->setTickLabelColor(color);

  yAxis->setBasePen(basePen);
  yAxis->setLabelColor(color);
  yAxis->setTickLabelColor(color);

  // Finally store that color.
  m_plottingColor = color;
}


//! Choose the color scale (color gradient) to be used for the color map plot.
void
ColorMapPlotWidget::setColorMapGradient(QCPColorGradient colorGradient)
{
  mp_colorMap->setGradient(colorGradient);
}


//! Change the scales according to new ranges for x, y and z axes.
/*!

  This function is typically called after having manually set the cell values
  in the color map, to ensure that the map is redraw with proper scales.

*/
void
ColorMapPlotWidget::rescaleColorMapDataRange(bool recalculateDataBounds)
{
  mp_colorMap->rescaleDataRange(recalculateDataBounds);
}


//! Handle the keyboard key press along with the mouse button release event.
void
ColorMapPlotWidget::mouseReleaseHandledEvent(QMouseEvent *event)
{
  double dtRangeMin = 0;
  double dtRangeMax = 0;

  double mzRangeMin = 0;
  double mzRangeMax = 0;

  if(m_areDtKeys)
    {
      // The x axis is for the dt.

      dtRangeMin = xRangeMin();
      dtRangeMax = xRangeMax();

      mzRangeMin = yRangeMin();
      mzRangeMax = yRangeMax();
    }
  else
    {
      // The x axis is for the mz.

      mzRangeMin = xRangeMin();
      mzRangeMax = xRangeMax();

      dtRangeMin = yRangeMin();
      dtRangeMax = yRangeMax();
    }

  if(m_pressedKeyCode == Qt::Key_R && event->button() == Qt::RightButton)
    {
      // By convention, the dt is as x and mz is as y.
      integrateToRt(dtRangeMin, dtRangeMax, mzRangeMin, mzRangeMax);
    }

  if(m_pressedKeyCode == Qt::Key_S && event->button() == Qt::RightButton)
    {
      // By convention, the dt is as x and mz is as y.
      integrateToMz(dtRangeMin, dtRangeMax, mzRangeMin, mzRangeMax);
    }

  if(m_pressedKeyCode == Qt::Key_D && event->button() == Qt::RightButton)
    {
      // By convention, the dt is as x and mz is as y.
      integrateToDt(dtRangeMin, dtRangeMax, mzRangeMin, mzRangeMax);
    }

  if(m_pressedKeyCode == Qt::Key_I && event->button() == Qt::RightButton)
    {
      // By convention, the dt is as x and mz is as y.
      integrateToTicIntensity(dtRangeMin, dtRangeMax, mzRangeMin, mzRangeMax);
    }
}


//! Integrate data to a retention time (TIC/XIC) chromatogram
/*!

  The data to be integrated are bordered by the parameters that describe data
  ranges on both the x and y axes.

*/
void
ColorMapPlotWidget::integrateToRt(double dtRangeStart,
                                  double dtRangeEnd,
                                  double mzRangeStart,
                                  double mzRangeEnd)

{
  History localHistory  = m_history;
  HistoryItem *histItem = new HistoryItem;

  histItem->newIntegrationRange(
    IntegrationType::DTMZ_DT_TO_RT, dtRangeStart, dtRangeEnd);

  histItem->newIntegrationRange(
    IntegrationType::DTMZ_MZ_TO_RT, mzRangeStart, mzRangeEnd);

  // Aggreate the new item to the local history copy we made.
  localHistory.appendHistoryItem(histItem);

  emit newTicChromatogram(mp_massSpecDataSet,
                          "Calculating extracted ion chromatogram.",
                          localHistory,
                          m_plottingColor);
}


/*/js/
 * ColorMapPlotWidget.jsIntegrateToRt(dtRangeStart, dtRangeEnd, mzRangeStart,
 * mzRangeEnd)
 *
 * Starts an integration of mass spectra data to a XIC chromatogram limiting
 * the data ranges specified with the numerical arguments.
 *
 * dtRange(Start | End) drift time range values
 * mzRange(Start | End) m/z range values
 *
 * This function creates a new plot widget to display the obtained results.
 */
void
ColorMapPlotWidget::jsIntegrateToRt(double dtRangeStart,
                                    double dtRangeEnd,
                                    double mzRangeStart,
                                    double mzRangeEnd)

{
  History localHistory  = m_history;
  HistoryItem *histItem = new HistoryItem;

  histItem->newIntegrationRange(
    IntegrationType::DTMZ_DT_TO_RT, dtRangeStart, dtRangeEnd);

  histItem->newIntegrationRange(
    IntegrationType::DTMZ_MZ_TO_RT, mzRangeStart, mzRangeEnd);

  // Aggreate the new item to the local history copy we made.
  localHistory.appendHistoryItem(histItem);

  ColorMapWnd *wnd = dynamic_cast<ColorMapWnd *>(mp_parentWnd);

  MainWindow *mainWindow = static_cast<MainWindow *>(wnd->parent());

  AbstractMultiPlotWnd *targetWnd = mainWindow->mp_ticChromWnd;

  qDebug() << __FILE__ << __LINE__ << __FUNCTION__ << "()";

  static_cast<TicChromWnd *>(targetWnd)->jsNewTicChromatogram(
    this,
    mp_massSpecDataSet,
    "Calculating drift spectrum.",
    localHistory,
    m_plottingColor);
}

//! Integrate data to a mass spectrum
/*!

  The data to be integrated are bordered by the parameters that describe data
  ranges on both the x and y axes.

*/
void
ColorMapPlotWidget::integrateToMz(double dtRangeStart,
                                  double dtRangeEnd,
                                  double mzRangeStart,
                                  double mzRangeEnd)
{
  History localHistory = m_history;

  // The color map plot widget serves as a starting point for data
  // exploration exactly as the TIC chromatogram plot widget that is created
  // right after having loaded mass data. Thus, when integrating to a mass
  // spectrum, we need to comply with the mz integration parameters that
  // were set for that TIC chrom widget (so-called root widget for a given
  // mass data file). Let's get that widget and obtain from it a copy of the
  // mz integration parameters.

  ColorMapWnd *parentWnd = static_cast<ColorMapWnd *>(mp_parentWnd);
  MainWindow *mainWindow = static_cast<MainWindow *>(parentWnd->parent());

  // This is merely a holder for a result of the call below
  DataPlotWidgetRelation *plotWidgetRelation = Q_NULLPTR;

  AbstractPlotWidget *rootPlotWidget =
    mainWindow->m_dataPlotWidgetRelationer.rootPlotWidget(this,
                                                          &plotWidgetRelation);

  if(rootPlotWidget == Q_NULLPTR)
    qFatal(
      "Fatal error at %s@%d -- %s(). "
      "Cannot be that a color map widget has no root TIC chrom plot widget."
      "Program aborted.",
      __FILE__,
      __LINE__,
      __FUNCTION__);

  MzIntegrationParams params =
    static_cast<TicChromPlotWidget *>(rootPlotWidget)->mzIntegrationParams();
  localHistory.setMzIntegrationParams(params);
  HistoryItem *histItem = new HistoryItem;

  histItem->newIntegrationRange(
    IntegrationType::DTMZ_DT_TO_MZ, dtRangeStart, dtRangeEnd);

  histItem->newIntegrationRange(
    IntegrationType::DTMZ_MZ_TO_MZ, mzRangeStart, mzRangeEnd);

  // Aggreate the new item to the local history copy we made.
  localHistory.appendHistoryItem(histItem);

  emit newMassSpectrum(mp_massSpecDataSet,
                       "Calculating mass spectrum.",
                       localHistory,
                       m_plottingColor);
}


/*/js/
 * ColorMapPlotWidget.jsIntegrateToMz(dtRangeStart, dtRangeEnd, mzRangeStart,
 * mzRangeEnd)
 *
 * Starts an integration of mass spectra data to a mass spectrum limiting the
 * data ranges specified with the numerical arguments.
 *
 * dtRange(Start | End) drift time range values
 * mzRange(Start | End) m/z range values
 *
 * This function creates a new plot widget to display the obtained results.
 */
void
ColorMapPlotWidget::jsIntegrateToMz(double dtRangeStart,
                                    double dtRangeEnd,
                                    double mzRangeStart,
                                    double mzRangeEnd)
{
  History localHistory = m_history;

  // The color map plot widget serves as a starting point for data
  // exploration exactly as the TIC chromatogram plot widget that is created
  // right after having loaded mass data. Thus, when integrating to a mass
  // spectrum, we need to comply with the mz integration parameters that
  // were set for that TIC chrom widget (so-called root widget for a given
  // mass data file). Let's get that widget and obtain from it a copy of the
  // mz integration parameters.

  ColorMapWnd *parentWnd = static_cast<ColorMapWnd *>(mp_parentWnd);
  MainWindow *mainWindow = static_cast<MainWindow *>(parentWnd->parent());

  // This is merely a holder for a result of the call below
  DataPlotWidgetRelation *plotWidgetRelation = Q_NULLPTR;

  AbstractPlotWidget *rootPlotWidget =
    mainWindow->m_dataPlotWidgetRelationer.rootPlotWidget(this,
                                                          &plotWidgetRelation);

  if(rootPlotWidget == Q_NULLPTR)
    qFatal(
      "Fatal error at %s@%d -- %s(). "
      "Cannot be that a color map widget has no root TIC chrom plot widget."
      "Program aborted.",
      __FILE__,
      __LINE__,
      __FUNCTION__);

  MzIntegrationParams params =
    static_cast<TicChromPlotWidget *>(rootPlotWidget)->mzIntegrationParams();
  localHistory.setMzIntegrationParams(params);
  HistoryItem *histItem = new HistoryItem;

  histItem->newIntegrationRange(
    IntegrationType::DTMZ_DT_TO_MZ, dtRangeStart, dtRangeEnd);

  histItem->newIntegrationRange(
    IntegrationType::DTMZ_MZ_TO_MZ, mzRangeStart, mzRangeEnd);

  // Aggreate the new item to the local history copy we made.
  localHistory.appendHistoryItem(histItem);

  AbstractMultiPlotWnd *targetWnd = mainWindow->mp_massSpecWnd;

  qDebug() << __FILE__ << __LINE__ << __FUNCTION__ << "()";

  static_cast<MassSpecWnd *>(targetWnd)->jsNewMassSpectrum(
    this,
    mp_massSpecDataSet,
    "Calculating drift spectrum.",
    localHistory,
    m_plottingColor);
}


//! Integrate data to a drift spectrum
/*!

  The data to be integrated are bordered by the parameters that describe data
  ranges on both the x and y axes.
  */
void
ColorMapPlotWidget::integrateToDt(double dtRangeStart,
                                  double dtRangeEnd,
                                  double mzRangeStart,
                                  double mzRangeEnd)
{
  History localHistory  = m_history;
  HistoryItem *histItem = new HistoryItem;

  histItem->newIntegrationRange(
    IntegrationType::DTMZ_DT_TO_DT, dtRangeStart, dtRangeEnd);

  histItem->newIntegrationRange(
    IntegrationType::DTMZ_MZ_TO_DT, mzRangeStart, mzRangeEnd);

  // qDebug() << __FILE__ << __LINE__ << __FUNCTION__
  //<< "histItem:" << histItem->asText();

  // Aggreate the new item to the local history copy we made.
  localHistory.appendHistoryItem(histItem);

  emit newDriftSpectrum(mp_massSpecDataSet,
                        "Calculating drift spectrum.",
                        localHistory,
                        m_plottingColor);
}


/*/js/
 * ColorMapPlotWidget.jsIntegrateToDt(dtRangeStart, dtRangeEnd, mzRangeStart,
 * mzRangeEnd)
 *
 * Starts an integration of mass spectra data to a drift spectrum limiting the
 * data ranges specified with the numerical arguments.
 *
 * dtRange(Start | End) drift time range values
 * mzRange(Start | End) m/z range values
 *
 * This function creates a new plot widget to display the obtained results.
 */
void
ColorMapPlotWidget::jsIntegrateToDt(double dtRangeStart,
                                    double dtRangeEnd,
                                    double mzRangeStart,
                                    double mzRangeEnd)
{
  History localHistory  = m_history;
  HistoryItem *histItem = new HistoryItem;

  histItem->newIntegrationRange(
    IntegrationType::DTMZ_DT_TO_DT, dtRangeStart, dtRangeEnd);

  histItem->newIntegrationRange(
    IntegrationType::DTMZ_MZ_TO_DT, mzRangeStart, mzRangeEnd);

  // qDebug() << __FILE__ << __LINE__ << __FUNCTION__
  //<< "histItem:" << histItem->asText();

  // Aggreate the new item to the local history copy we made.
  localHistory.appendHistoryItem(histItem);

  ColorMapWnd *wnd = dynamic_cast<ColorMapWnd *>(mp_parentWnd);

  MainWindow *mainWindow = static_cast<MainWindow *>(wnd->parent());

  AbstractMultiPlotWnd *targetWnd = mainWindow->mp_driftSpecWnd;

  qDebug() << __FILE__ << __LINE__ << __FUNCTION__ << "()";

  static_cast<DriftSpecWnd *>(targetWnd)->jsNewDriftSpectrum(
    this,
    mp_massSpecDataSet,
    "Calculating drift spectrum.",
    localHistory,
    m_plottingColor);
}


//! Integrate data to a single TIC intensity value
/*!

  The data to be integrated are bordered by the parameters that describe data
  ranges on both the x and y axes.

  This is not a conventional integration, because it does not produce any
  graph, it only produces a single TIC intensity value.
  */
void
ColorMapPlotWidget::integrateToTicIntensity(double dtRangeStart,
                                            double dtRangeEnd,
                                            double mzRangeStart,
                                            double mzRangeEnd)
{
  History localHistory  = m_history;
  HistoryItem *histItem = new HistoryItem;

  histItem->newIntegrationRange(
    IntegrationType::DTMZ_DT_TO_TIC_INT, dtRangeStart, dtRangeEnd);

  histItem->newIntegrationRange(
    IntegrationType::DTMZ_MZ_TO_TIC_INT, mzRangeStart, mzRangeEnd);

  // Aggreate the new item to the local history copy we made.
  localHistory.appendHistoryItem(histItem);

  // Capture immediately the text that is located in the status bar
  // because if the computation is long that text will have disappeared
  // and we won't be able to show it in the TIC int text.

  QMainWindow *parentWnd = static_cast<QMainWindow *>(mp_parentWnd);
  QString currentMessage = parentWnd->statusBar()->currentMessage();

  if(currentMessage.isEmpty())
    currentMessage = QString("dt range [%1-%2] ; m/z range [%3-%4]")
                       .arg(dtRangeStart, 0, 'f', 3)
                       .arg(dtRangeEnd, 0, 'f', 3)
                       .arg(mzRangeStart, 0, 'f', 3)
                       .arg(mzRangeEnd, 0, 'f', 3);
  else
    currentMessage += QString(" - dt range [%1-%2] ; m/z range [%3-%4]")
                        .arg(dtRangeStart, 0, 'f', 3)
                        .arg(dtRangeEnd, 0, 'f', 3)
                        .arg(mzRangeStart, 0, 'f', 3)
                        .arg(mzRangeEnd, 0, 'f', 3);

  qDebug() << __FILE__ << __LINE__ << __FUNCTION__ << "()"
           << "Emitting ticIntensitySignal";

  emit ticIntensitySignal(mp_massSpecDataSet, currentMessage, localHistory);
}


/*/js/
 * ColorMapPlotWidget.jsIntegrateToTicIntensity(dtRangeStart, dtRangeEnd,
 * mzRangeStart, mzRangeEnd)
 *
 * Starts an integration of mass spectra data to a TIC intensity single value
 * limiting the data ranges specified with the numerical arguments.
 *
 * dtRange(Start | End) drift time range values
 * mzRange(Start | End) m/z range values
 *
 * The TIC intensity value is returned
 */
double
ColorMapPlotWidget::jsIntegrateToTicIntensity(double dtRangeStart,
                                              double dtRangeEnd,
                                              double mzRangeStart,
                                              double mzRangeEnd)
{
  History localHistory  = m_history;
  HistoryItem *histItem = new HistoryItem;

  histItem->newIntegrationRange(
    IntegrationType::DTMZ_DT_TO_TIC_INT, dtRangeStart, dtRangeEnd);

  histItem->newIntegrationRange(
    IntegrationType::DTMZ_MZ_TO_TIC_INT, mzRangeStart, mzRangeEnd);

  // Aggreate the new item to the local history copy we made.
  localHistory.appendHistoryItem(histItem);

  ColorMapWnd *parentWnd = static_cast<ColorMapWnd *>(mp_parentWnd);


  // Capture immediately the text that is located in the status bar
  // because if the computation is long that text will have disappeared
  // and we won't be able to show it in the TIC int text.

  QString currentMessage = parentWnd->statusBar()->currentMessage();

  QApplication::setOverrideCursor(QCursor(Qt::BusyCursor));

  MassDataIntegrator integrator(mp_massSpecDataSet, localHistory);

  integrator.integrateToTicIntensity();

  m_lastTicIntensity = integrator.ticIntensity();

  QApplication::restoreOverrideCursor();

  if(currentMessage.isEmpty())
    parentWnd->statusBar()->showMessage(
      QString("dt range [%1-%2] ; m/z range [%3-%4]: TIC I = %5")
        .arg(dtRangeStart, 0, 'f', 3)
        .arg(dtRangeEnd, 0, 'f', 3)
        .arg(mzRangeStart, 0, 'f', 3)
        .arg(mzRangeEnd, 0, 'f', 3)
        .arg(m_lastTicIntensity, 0, 'g', 3));
  else
    parentWnd->statusBar()->showMessage(
      currentMessage +
      QString(" ; (Tic I = %1)").arg(m_lastTicIntensity, 0, 'g', 3));

  return m_lastTicIntensity;
}


/*/js/
 * ColorMapPlotWidget.transposeAxes()
 *
 * Transpose the axes of this color map plot widget
 */
//! Transpose the axes such that the dt axis takes the place of the m/z axis.
void
ColorMapPlotWidget::transposeAxes()
{
  // qDebug() << __FILE__ << __LINE__ << __FUNCTION__ << "()" ;

  QCPColorMapData *origData = mp_colorMap->data();

  int keySize   = origData->keySize();
  int valueSize = origData->valueSize();

  // qDebug() << __FILE__ << __LINE__ << __FUNCTION__ << "()"
  //<< "Orig data size:" << keySize << valueSize;

  QCPRange keyRange   = origData->keyRange();
  QCPRange valueRange = origData->valueRange();

  // qDebug() << __FILE__ << __LINE__ << __FUNCTION__ << "()"
  //<< "Value at cell 80,650:" << origData->cell(80,650);

  // Transposed map.
  QCPColorMapData *newData =
    new QCPColorMapData(valueSize, keySize, valueRange, keyRange);

  for(int iter = 0; iter < keySize; ++iter)
    {
      for(int jter = 0; jter < valueSize; ++jter)
        {
          double cellData = origData->cell(iter, jter);

          newData->setCell(jter, iter, cellData);
        }
    }

  // qDebug() << __FILE__ << __LINE__ << __FUNCTION__ << "()"
  //<< "New data size:" << newData->keySize() << newData->valueSize();

  // At this point the transposition has been done.

  mp_colorMap->data()->clear();
  mp_colorMap->rescaleDataRange(true);

  // We must invert the bool value that indicates the orientation of the
  // keys/values.

  m_areDtKeys = !m_areDtKeys;

  // And now make sure that the axes labels are inverted.
  if(m_areDtKeys)
    {
      xAxis->setLabel("drift time (ms)");
      yAxis->setLabel("m/z");
    }
  else
    {
      xAxis->setLabel("m/z");
      yAxis->setLabel("drift time (ms)");
    }

  // qDebug() << __FILE__ << __LINE__ << __FUNCTION__ << "()"
  //<< "Value at cell 80,650:" << origData->cell(80,650);

  // QTimer::singleShot(9000, this,
  //[=]() {printf("done\n");}	);

  // Will take ownership of the newData.
  mp_colorMap->setData(newData);

  // qDebug() << __FILE__ << __LINE__ << __FUNCTION__ << "()"
  //<< "Value at cell 80,650:" << newData->cell(80,650)
  //<< "Value at cell 650, 80:" << newData->cell(650,80);

  // QCPAxis *p_keyAxis = mp_colorMap->keyAxis();
  // QCPAxis *p_valueAxis = mp_colorMap->valueAxis();

  // mp_colorMap->setKeyAxis(p_valueAxis);
  // mp_colorMap->setValueAxis(p_keyAxis);

  mp_colorMap->rescaleAxes();

  replot();
}


//! Handle the key code of the keyboard key that was released.
void
ColorMapPlotWidget::keyReleaseEvent(QKeyEvent *event)
{
  ColorMapWnd *parentWnd = static_cast<ColorMapWnd *>(mp_parentWnd);
  MainWindow *mainWindow = static_cast<MainWindow *>(parentWnd->parent());

  if(event->key() == Qt::Key_L)
    {
      // The user wants to copy the current cursor location to the console
      // window, such that it remains available after having moved the cursor.

      QString label;

      label = QString("(%1,%2)\n")
                .arg(m_lastMousedPlotPoint.x())
                .arg(m_lastMousedPlotPoint.y());

      if(label.isEmpty())
        {
          parentWnd->statusBar()->showMessage("Failed to craft a text label.");

          event->accept();
          return;
        }
      else
        {

          // Send the label to the console window, so that we can make a
          // copy paste.

          mainWindow->logConsoleMessage(label, m_plottingColor);
        }

      event->accept();

      return;
    }

  // Now let the base class do its work.
  AbstractPlotWidget::keyReleaseEvent(event);
}


//! Record the clicks of the mouse.
void
ColorMapPlotWidget::mousePressHandler(QMouseEvent *event)
{
  // qDebug() << __FILE__ << __LINE__ << __FUNCTION__ << "()";

  // Let the base class do the base work.
  AbstractPlotWidget::mousePressHandler(event);

  // Now, we have to update the m/z integration params in the corresponding
  // window, but using the integration params that are located inside of the
  // root plot widget's history object. This is because the color map plot
  // widget actually needs to behave like the TIC chromatogram widget, as
  // they both are the starting points for the exploration of mass data.

  AbstractMultiPlotWnd *parentWnd =
    dynamic_cast<AbstractMultiPlotWnd *>(mp_parentWnd);

  if(parentWnd == Q_NULLPTR)
    qFatal("Fatal error at %s@%d. Program aborted.", __FILE__, __LINE__);

  // We now need to get the root widget's m/z integrations params.

  MainWindow *mainWindow = static_cast<MainWindow *>(parentWnd->parent());

  // The plot widget relation pointer that will be set in the call below.
  DataPlotWidgetRelation *plotWidgetRelation;

  AbstractPlotWidget *rootPlotWidget =
    mainWindow->m_dataPlotWidgetRelationer.rootPlotWidget(this,
                                                          &plotWidgetRelation);

  if(rootPlotWidget == Q_NULLPTR)
    qFatal(
      "Fatal error at %s@%d -- %s(). "
      "Cannot be that a color map widget has no root TIC chrom plot widget."
      "Program aborted.",
      __FILE__,
      __LINE__,
      __FUNCTION__);

  // We want to maintain a synchrony between both TIC chrom plot widget and
  // Color Map plot widget. We need to do that, because when we'll update
  // the m/z integration params in the corresponding window, the various
  // spin boxes and checkboxes will trigger "ChangedValue" signals that will
  // automatically update the values in the currently focused plot widget.
  // If that focused widget is not the root plot widget of the one were are
  // handling here now, then all the params will be messed up.

  rootPlotWidget->setFocus();
}


/*/js/
 * ColorMapPlotWidget.jsSetMzIntegrationParams(mzIntegrationParams)
 *
 * Set the m/z integration parameters object passed as parameter to this plot
 * widget so that they are taken into account for the next integrations to a
 * mass spectrum.
 */
void
ColorMapPlotWidget::jsSetMzIntegrationParams()
{
  if(m_isMultiGraph)
    return;

  MainWindow *mainWindow = static_cast<MainWindow *>(mp_parentWnd->parent());
  QScriptContext *ctx =
    mainWindow->mp_scriptingWnd->m_scriptEngine.currentContext();

  int argCount = ctx->argumentCount();
  // qDebug() << __FILE__ << __LINE__ << __FUNCTION__ << "()"
  //<< "argCount:" << argCount;

  if(argCount != 1)
    {
      ctx->throwError(
        "Syntax error: function setMzIntegrationParams takes one "
        "MzIntegrationParams object as argument.");
      return;
    }

  QScriptValue arg = ctx->argument(0);
  QVariant variant = arg.data().toVariant();

  if(!variant.isValid())
    {
      ctx->throwError(
        "Syntax error: function setMzIntegrationParams takes one "
        "MzIntegrationParams object as argument.");
      return;
    }

  if(variant.userType() ==
     QMetaType::type("msXpSmineXpert::MzIntegrationParams"))
    {
      MzIntegrationParams params(
        qscriptvalue_cast<msXpSmineXpert::MzIntegrationParams>(arg));
      AbstractPlotWidget::setMzIntegrationParams(params);

      // Now update the values in the MzIntegrationParamsWnd.

      AbstractMultiPlotWnd *parentWnd =
        dynamic_cast<AbstractMultiPlotWnd *>(mp_parentWnd);

      if(parentWnd == Q_NULLPTR)
        qFatal("Fatal error at %s@%d. Program aborted.", __FILE__, __LINE__);

      parentWnd->updateMzIntegrationParams(m_history.mzIntegrationParams(),
                                           m_plottingColor);
    }
  else
    {
      ctx->throwError(
        "Syntax error: function setMzIntegrationParams takes one "
        "MzIntegrationParams object as argument.");
      return;
    }
}


/*/js/
 * ColorMapPlotWidget.jsMzIntegrationParams()
 *
 * Return the MzIntegrationParams object currently set for this color map
 * plot widget.
 *
 * These parameters are taken into account for the integrations to a mass
 * spectrum.
 */
QScriptValue
ColorMapPlotWidget::jsMzIntegrationParams() const
{
  MainWindow *mainWindow = static_cast<MainWindow *>(mp_parentWnd->parent());

  if(m_isMultiGraph)
    return mainWindow->mp_scriptingWnd->m_scriptEngine.toScriptValue(
      MzIntegrationParams());

  return mainWindow->mp_scriptingWnd->m_scriptEngine.toScriptValue(
    m_history.mzIntegrationParams());
}


} // namespace msXpSmineXpert
