/* 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
 */


/////////////////////// StdLib includes
#include <vector>


/////////////////////// Qt includes
#include <QVector>


/////////////////////// Local includes
#include "basecolormapplotwidget.h"
#include "../../trace/maptrace.h"
#include "../../pappsoexception.h"


namespace pappso
{


BaseColorMapPlotWidget::BaseColorMapPlotWidget(QWidget *parent,
                                               const QString &x_axis_label,
                                               const QString &y_axis_label)
  : BasePlotWidget(parent, x_axis_label, y_axis_label)
{
}


//! Destruct \c this BaseColorMapPlotWidget instance.
/*!

  The destruction involves clearing the history, deleting all the axis range
  history items for x and y axes.

*/
BaseColorMapPlotWidget::~BaseColorMapPlotWidget()
{
}


void
BaseColorMapPlotWidget::setColorMapPlotConfig(
  const ColorMapPlotConfig &color_map_config)
{
  m_colorMapPlotConfig = color_map_config;
}


const ColorMapPlotConfig &
BaseColorMapPlotWidget::getColorMapPlotConfig()
{
  return m_colorMapPlotConfig;
}


QCPColorMap *
BaseColorMapPlotWidget::addColorMap(
  std::shared_ptr<std::map<double, MapTrace>> double_map_trace_map_sp,
  ColorMapPlotConfig color_map_plot_config,
  const QColor &color)
{
  // qDebug() << "Adding color map with config:" <<
  // color_map_plot_config.toString();

  if(!color.isValid())
    throw PappsoException(
      QString("The color to be used for the plot graph is invalid."));

  QCPColorMap *color_map_p = new QCPColorMap(xAxis, yAxis);

  // Do not forget to copy the config!

  m_colorMapPlotConfig = color_map_plot_config;

#if 0 
			// This is the code on the QCustomPlot documentation and it works fine.
			QCPColorMap *color_map_p = new QCPColorMap(xAxis, yAxis);

			color_map_p->data()->setSize(50, 50);
			color_map_p->data()->setRange(QCPRange(0, 2), QCPRange(0, 2));
			for(int x = 0; x < 50; ++x)
				for(int y = 0; y < 50; ++y)
					color_map_p->data()->setCell(x, y, qCos(x / 10.0) + qSin(y / 10.0));
			color_map_p->setGradient(QCPColorGradient::gpPolar);
			color_map_p->rescaleDataRange(true);
			rescaleAxes();
			replot();
#endif

  // Connect the signal of selection change so that we can re-emit it for the
  // widget that is using *this widget.

  connect(color_map_p,
          static_cast<void (QCPAbstractPlottable::*)(bool)>(
            &QCPAbstractPlottable::selectionChanged),
          [this, color_map_p]() {
            emit plottableSelectionChangedSignal(color_map_p,
                                                 color_map_p->selected());
          });

  // qDebug() << "Configuring the color map with this config:"
  //<< color_map_plot_config.toString();

  color_map_p->data()->setSize(color_map_plot_config.keyCellCount,
                               color_map_plot_config.mzCellCount);

  color_map_p->data()->setRange(QCPRange(color_map_plot_config.minKeyValue,
                                         color_map_plot_config.maxKeyValue),
                                QCPRange(color_map_plot_config.minMzValue,
                                         color_map_plot_config.maxMzValue));
  color_map_p->data()->fill(0.0);

  // We have now to fill the color map.

  for(auto &&pair : *double_map_trace_map_sp)
    {

      // The first value is the key and the second value is the MapTrace into
      // which we need to iterated and for each point (double mz, double
      // intensity) create a map cell.

      double dt_or_rt_key = pair.first;
      MapTrace map_trace  = pair.second;

      for(auto &&data_point_pair : map_trace)
        {
          double mz        = data_point_pair.first;
          double intensity = data_point_pair.second;

          // We are filling dynamically the color map. If a cell had already
          // something in, then we need to take that into account. This is
          // because we let QCustomPlot handle the fuzzy transition between
          // color map plot cells.

          double prev_intensity = color_map_p->data()->data(dt_or_rt_key, mz);

          // qDebug() << "Setting tri-point:" << dt_or_rt_key << "," << mz <<
          // ","
          //<< prev_intensity + intensity;

          color_map_p->data()->setData(
            dt_or_rt_key, mz, prev_intensity + intensity);
        }
    }

  // At this point we have finished filling-up the color map.

  // The gpThermal is certainly one of the best.

  color_map_p->setGradient(QCPColorGradient::gpThermal);

  color_map_p->rescaleDataRange(true);

  color_map_p->rescaleAxes();
  resetAxesRangeHistory();

  // The pen of the color map itself is of no use. Instead the user will see the
  // color of the axes' labels.

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

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

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

  // And now set the color map's pen to the same color, even if we do not use
  // it, we need it for coloring the plots that might be integrated from this
  // color map.

  color_map_p->setPen(pen);

  // Copy the color map's data into a backup copy.

  mpa_backupColorMapData = new QCPColorMapData(*(color_map_p->data()));

  replot();

  return color_map_p;
}


void
BaseColorMapPlotWidget::transposeAxes()
{
  // qDebug() << __FILE__ << __LINE__ << __FUNCTION__ << "()" ;

  QCPColorMap *color_map_p = static_cast<QCPColorMap *>(plottable(0));

  QCPColorMapData *origData = color_map_p->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.

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

  // Now we need to invert the labels and data kinds.

  DataKind temp_data_kind            = m_colorMapPlotConfig.xAxisDataKind;
  m_colorMapPlotConfig.xAxisDataKind = m_colorMapPlotConfig.yAxisDataKind;
  m_colorMapPlotConfig.yAxisDataKind = temp_data_kind;

  QString temp_axis_label = xAxis->label();
  xAxis->setLabel(yAxis->label());
  yAxis->setLabel(temp_axis_label);

  // Will take ownership of the newData.
  color_map_p->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);

  color_map_p->rescaleAxes();

  replot();
}


void
BaseColorMapPlotWidget::zAxisScaleToLog10()
{
  // qDebug() << __FILE__ << __LINE__ << __FUNCTION__ << "()" ;

  if(m_colorMapPlotConfig.zAxisScale == AxisScale::log10)
    {
      qDebug() << "Asking to change z axis scale to log10 while it is already "
                  "like so.";

      return;
    }

  QCPColorMap *color_map_p = static_cast<QCPColorMap *>(plottable(0));

  QCPColorMapData *origData = color_map_p->data();

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

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

  // Log-ified heat map.
  QCPColorMapData *newData =
    new QCPColorMapData(keySize, valueSize, keyRange, valueRange);

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

          double new_cell_data = 0;

          if(!cell_data)
            // The log10 would be -inf, but then we'd have a huge data range and
            // the color map would look totally blue... that is like 0 intensity
            // all over.
            new_cell_data = -1;
          else
            new_cell_data = std::log10(cell_data);

          // qDebug() << "cell_data:" << cell_data
          //<< "new_cell_data:" << new_cell_data;

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

  color_map_p->data()->clear();
  // Now we need to document the change.

  m_colorMapPlotConfig.zAxisScale = AxisScale::log10;

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

  color_map_p->data()->recalculateDataBounds();
  color_map_p->rescaleDataRange(true);

  // We should not do this, as the user might have zoomed to a region of
  // interest.
  // color_map_p->rescaleAxes();

  replot();
}


void
BaseColorMapPlotWidget::zAxisScaleResetToOriginal()
{
  QCPColorMap *color_map_p = static_cast<QCPColorMap *>(plottable(0));
  color_map_p->data()->clear();

  if(mpa_backupColorMapData == nullptr)
    throw(PappsoException(
      "Not possible that the mpa_backupColorMapData pointer is null."));

  // We do no want that the color_map_p takes ownership of the data, because
  // these must remain there always, so pass true, to say that we want to copy
  // the data not transfer the pointer.
  color_map_p->setData(mpa_backupColorMapData, true);

  color_map_p->data()->recalculateDataBounds();
  color_map_p->rescaleDataRange(true);

  // We should not do this, as the user might have zoomed to a region of
  // interest.
  // color_map_p->rescaleAxes();

  m_colorMapPlotConfig.zAxisScale = AxisScale::orig;

  replot();
}


DataKind
BaseColorMapPlotWidget::xAxisDataKind() const
{
  return m_colorMapPlotConfig.xAxisDataKind;
}


DataKind
BaseColorMapPlotWidget::yAxisDataKind() const
{
  return m_colorMapPlotConfig.yAxisDataKind;
}


AxisScale
BaseColorMapPlotWidget::axisScale(Axis axis) const
{
  if(axis == Axis::x)
    return m_colorMapPlotConfig.xAxisScale;
  else if(axis == Axis::y)
    return m_colorMapPlotConfig.yAxisScale;
  else if(axis == Axis::z)
    return m_colorMapPlotConfig.zAxisScale;
  else
    throw PappsoException(
      QString("basecolormapplotwidget.cpp: The axis cannot be different than "
              "x, y or z."));

  return AxisScale::unset;
}


AxisScale
BaseColorMapPlotWidget::xAxisScale() const
{
  return m_colorMapPlotConfig.xAxisScale;
}


AxisScale
BaseColorMapPlotWidget::yAxisScale() const
{
  return m_colorMapPlotConfig.yAxisScale;
}


AxisScale
BaseColorMapPlotWidget::zAxisScale() const
{
  return m_colorMapPlotConfig.zAxisScale;
}


void
BaseColorMapPlotWidget::setPlottingColor(QCPAbstractPlottable *plottable_p,
                                         const QColor &new_color)
{
  Q_UNUSED(plottable_p);

  // The pen of the color map itself is of no use. Instead the user will see the
  // color of the axes' labels.

  QPen pen = xAxis->basePen();
  pen.setColor(new_color);

  xAxis->setBasePen(pen);
  xAxis->setLabelColor(new_color);
  xAxis->setTickLabelColor(new_color);

  yAxis->setBasePen(pen);
  yAxis->setLabelColor(new_color);
  yAxis->setTickLabelColor(new_color);

  // And now set the color map's pen to the same color, even if we do not use
  // it, we need it for coloring the plots that might be integrated from this
  // color map.

  QCPColorMap *color_map_p = static_cast<QCPColorMap *>(plottable(0));

  color_map_p->setPen(pen);

  replot();
}


QColor
BaseColorMapPlotWidget::getPlottingColor(int index) const
{
  Q_UNUSED(index);

  QPen pen = xAxis->basePen();
  return pen.color();
}


} // namespace pappso
