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


/////////////////////// StdLib includes


/////////////////////// Qt includes
#include <QMessageBox>
#include <QInputDialog>


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


/////////////////////// pappsomspp includes
#include <pappsomspp/core/processing/combiners/tracepluscombiner.h>


/////////////////////// libXpertMass includes
#include <MsXpS/libXpertMassGui/ColorSelector.hpp>

/////////////////////// libXpertMassGui includes


/////////////////////// Local includes
#include "BaseColorMapPlotCompositeWidget.hpp"
#include "BaseColorMapPlotWnd.hpp"
#include "MsFragmentationSpecDlg.hpp"
#include "MzIntegrationParamsDlg.hpp"
#include "ProgramWindow.hpp"
#include "ui_BasePlotCompositeWidget.h"

namespace MsXpS
{
namespace MineXpert
{


BaseColorMapPlotCompositeWidget::BaseColorMapPlotCompositeWidget(
  QWidget *parent, const QString &x_axis_label, const QString &y_axis_label)
  : BasePlotCompositeWidget(parent, x_axis_label, y_axis_label)
{
  // qDebug();

  createMainMenu();

  // We hide  all the buttons that configure the destination of new
  // integrations. We do not support the stuffing more than one color map into a
  // color map plot, contrary to what we do with graphs.
  m_ui->tracePlotSpecificWidgets->hide();
}

BaseColorMapPlotCompositeWidget::~BaseColorMapPlotCompositeWidget()
{
  // qDebug();
}

void
BaseColorMapPlotCompositeWidget::createMainMenu()
{
  // qDebug() << "Completing the main menu:" << mp_mainMenu;

  // Complete the base class menu with the color-map-specific menu
  // item.
  // Debug: the icon is there
  // QIcon the_icon = m_ui->mainMenuPushButton->icon();
  // QSize size = m_ui->mainMenuPushButton->iconSize(); // typically what you want
  // QPixmap pixmap = the_icon.pixmap(size);
  // pixmap.save("/tmp/basecolormapicon.png");
  // qDebug() << "The size of the icon is: " << size;
  // m_ui->mainMenuPushButton->setMinimumSize(m_ui->mainMenuPushButton->iconSize());

  // Update the menu tooltip.
  m_ui->mainMenuPushButton->setToolTip(
    "Color map plot widget menu (Ctrl+P, M)");

  m_ui->mainMenuPushButton->setMinimumSize(m_ui->mainMenuPushButton->iconSize());

  QAction *save_matrix_to_text_file_p =
    new QAction("Save matrix as 3 columns to text file", this);
  save_matrix_to_text_file_p->setStatusTip(
    tr("Save matrix as 3 columns to text file"));
  save_matrix_to_text_file_p->setShortcut(QKeySequence("Ctrl+M, C"));

  connect(save_matrix_to_text_file_p, &QAction::triggered, [this]() {
    saveMatrixAs3ColumnsToTextFile();
  });

  mp_mainMenu->addAction(save_matrix_to_text_file_p);

  save_matrix_to_text_file_p =
    new QAction("Save matrix as a matrix to text file", this);
  save_matrix_to_text_file_p->setStatusTip(
    tr("Save matrix as a matrix to text file"));
  save_matrix_to_text_file_p->setShortcut(QKeySequence("Ctrl+M, M"));

  connect(save_matrix_to_text_file_p, &QAction::triggered, [this]() {
    saveMatrixAsMatrixToTextFile(false);
  });

  mp_mainMenu->addAction(save_matrix_to_text_file_p);

  save_matrix_to_text_file_p =
    new QAction("Save matrix as a detailed matrix to text file", this);
  save_matrix_to_text_file_p->setStatusTip(
    tr("Save matrix as a detailed matrix to text file"));
  save_matrix_to_text_file_p->setShortcut(QKeySequence("Ctrl+M, D"));

  connect(save_matrix_to_text_file_p, &QAction::triggered, [this]() {
    saveMatrixAsMatrixToTextFile(true);
  });

  mp_mainMenu->addAction(save_matrix_to_text_file_p);

  // An now append new ColorMap-specific menu

  mp_mainMenu->addSeparator();

  // The axes transposition.
  QAction *transpose_axes_p = new QAction("Transpose axes", this);
  transpose_axes_p->setStatusTip(tr("Transpose axes"));
  connect(transpose_axes_p, &QAction::triggered, [this]() {
    static_cast<pappso::BaseColorMapPlotWidget *>(mp_plotWidget)
      ->transposeAxes();
  });

  mp_mainMenu->addSeparator();

  mp_mainMenu->addAction(transpose_axes_p);

  QMenu *z_axis_scales_menu_p = mp_mainMenu->addMenu("Z axis scales");

  // The z axis scale (log10 or linear).
  QAction *z_axis_scale_to_log10_p =
    new QAction("Set Z axis scale to log10", this);
  z_axis_scale_to_log10_p->setStatusTip(tr("Set Z axis scale to log10"));
  connect(z_axis_scale_to_log10_p, &QAction::triggered, [this]() {
    static_cast<pappso::BaseColorMapPlotWidget *>(mp_plotWidget)
      ->zAxisScaleToLog10();
  });

  z_axis_scales_menu_p->addAction(z_axis_scale_to_log10_p);

  // The low pass filter over the z axis.
  QAction *double_spin_box_action_p =
    new QAction("Low pass-filter Z axis data", this);
  double_spin_box_action_p->setStatusTip(tr("Low pass-filter Z axis data"));
  double_spin_box_action_p->setToolTip(
    "Enter the percentage (%) of the highest intensity (i) in the map");

  connect(double_spin_box_action_p, &QAction::triggered, [this]() {
    bool ok = false;

    double percentage = QInputDialog::getDouble(
      this,
      "Low pass filter the color map's intensities",
      "Enter the threshold as the percentage (%) \n"
      "of the highest intensity (i) in the map.\n"
      "\n"
      "The threshold (th) is computed as th = i * % / 100.\n"
      "\n"
      "The intensity values below th are kept unchanged.\n"
      "\n"
      "The intensity values above th are replaced with the th value.",
      90,
      0,
      100,
      1,
      &ok);

    if(ok)
      static_cast<pappso::BaseColorMapPlotWidget *>(mp_plotWidget)
        ->zAxisFilterLowPassPercentage(percentage);
  });

  z_axis_scales_menu_p->addAction(double_spin_box_action_p);


  // The high pass filter over the z axis.
  double_spin_box_action_p = new QAction("High pass-filter Z axis data", this);
  double_spin_box_action_p->setStatusTip(tr("High pass-filter Z axis data"));
  double_spin_box_action_p->setToolTip(
    "Enter the percentage (%) of the highest intensity (i) in the map");

  connect(double_spin_box_action_p, &QAction::triggered, [this]() {
    bool ok = false;

    double percentage = QInputDialog::getDouble(
      this,
      "High pass filter the color map's intensities",
      "Enter the threshold as the percentage (%) \n"
      "of the highest intensity (i) in the map.\n"
      "\n"
      "The threshold (th) is computed as th = i * % / 100.\n"
      "\n"
      "The intensity values above th are kept unchanged.\n"
      "\n"
      "The intensity values below th are replaced with the th value.",
      10,
      0,
      100,
      1,
      &ok);

    if(ok)
      static_cast<pappso::BaseColorMapPlotWidget *>(mp_plotWidget)
        ->zAxisFilterHighPassPercentage(percentage);
  });

  z_axis_scales_menu_p->addAction(double_spin_box_action_p);

  // The z axis scale (log10 or linear, that is, original).
  QAction *z_axis_data_to_original_p =
    new QAction("Reset Z axis data to original", this);
  z_axis_data_to_original_p->setStatusTip(tr("Reset Z axis data to original"));
  connect(z_axis_data_to_original_p, &QAction::triggered, [this]() {
    static_cast<pappso::BaseColorMapPlotWidget *>(mp_plotWidget)
      ->zAxisDataResetToOriginal();
  });

  z_axis_scales_menu_p->addAction(z_axis_data_to_original_p);
}

QCPColorMap *
BaseColorMapPlotCompositeWidget::addColorMap(
  std::shared_ptr<std::map<double, pappso::MapTrace>> &double_map_trace_map_sp,
  const pappso::ColorMapPlotConfig &color_map_plot_config,
  QCPAbstractPlottable *parent_plottable_p,
  MsRunDataSetCstSPtr ms_run_data_set_csp,
  const QString &sample_name,
  const ProcessingFlow &processing_flow,
  const QColor &color)
{
  // qDebug() << "Adding color map with config:" <<
  // color_map_plot_config.toString();

  // If color is not valid, we need to create one.

  QColor local_color(color);

  // Get color from the available colors, or if none is available, create one
  // randomly without requesting the user to select one from QColorDialog.
  if(!local_color.isValid())
    local_color = libXpertMassGui::ColorSelector::getColor(true);

  // Contrary to what we do with graphs, we cannot stuff more than one color map
  // into a color map plot, so just add a new plot.

  QCPColorMap *color_map_p = nullptr;

  color_map_p = static_cast<pappso::BaseColorMapPlotWidget *>(mp_plotWidget)
                  ->addColorMap(double_map_trace_map_sp,
                                color_map_plot_config,
                                local_color);

  // Each time a new trace is added anywhere, it needs to be documented
  // in the main program window's graph nodes tree! This is what we call
  // the ms run data plot graph filiation documentation.

  mp_parentWnd->getProgramWindow()->documentMsRunDataPlottableFiliation(
    ms_run_data_set_csp, color_map_p, parent_plottable_p, this);

  // We need to allocate a new ProcessingFlow instance with the graph
  // as its parent.
  ProcessingFlow *processing_flow_p = processing_flow.clone(color_map_p);

  // Map the graph_p to the processing flow that we got to document how
  // the trace was obtained in the first place.
  m_plottableProcessingFlowMap[color_map_p] = processing_flow_p;

  // Note how we do  change the sample name text and its color.
  // He we do set a new color map plot.
  m_ui->sampleNameLabel->setText(sample_name);
  setSampleNameColor(color);

  mp_plotWidget->replot();

  // The setFocus() call below will trigger the mp_plotWidget to send a signal
  // that will be caught by the parent window that will iterate in all the plot
  // widget and set their background to focused/unfocused color according to
  // their focus status.
  // mp_plotWidget->setFocus();

  // color_map_p might be nullptr.
  return color_map_p;
}

pappso::Enums::DataKind
BaseColorMapPlotCompositeWidget::xAxisKind() const
{
  return static_cast<pappso::BaseColorMapPlotWidget *>(mp_plotWidget)
    ->xAxisDataKind();
}

pappso::Enums::DataKind
BaseColorMapPlotCompositeWidget::yAxisKind() const
{
  return static_cast<pappso::BaseColorMapPlotWidget *>(mp_plotWidget)
    ->yAxisDataKind();
}

bool
BaseColorMapPlotCompositeWidget::saveMatrixAs3ColumnsToTextFile()
{
  QString fileName =
    QFileDialog::getSaveFileName(this,
                                 tr("Select a file to save the matrix into"),
                                 QDir::home().absolutePath());

  if(fileName.isEmpty())
    {
      qDebug() << "The file name to write the data into is empty!";
      return false;
    }

  QFile file(fileName);

  if(!file.open(QIODevice::WriteOnly | QIODevice::Text))
    {
      QMessageBox::information(
        this,
        "Please select a writable destination file for the data export",
        "The file name provided points to a file that is not writable.",
        QMessageBox::StandardButton::Ok,
        QMessageBox::StandardButton::NoButton);

      return false;
    }

  QString matrix_string;

  static_cast<pappso::BaseColorMapPlotWidget *>(mp_plotWidget)
    ->dataTo3ColString(matrix_string);

  QTextStream outStream(&file);

  outStream << matrix_string;
  outStream.flush();

  file.close();

  return true;
}

bool
BaseColorMapPlotCompositeWidget::saveMatrixAsMatrixToTextFile(bool detailed)
{
  QString fileName =
    QFileDialog::getSaveFileName(this,
                                 tr("Select a file to save the matrix into"),
                                 QDir::home().absolutePath());

  if(fileName.isEmpty())
    {
      qDebug() << "The file name to write the data into is empty!";
      return false;
    }

  QFile file(fileName);

  if(!file.open(QIODevice::WriteOnly | QIODevice::Text))
    {
      QMessageBox::information(
        this,
        "Please select a writable destination file for the data export",
        "The file name provided points to a file that is not writable.",
        QMessageBox::StandardButton::Ok,
        QMessageBox::StandardButton::NoButton);

      return false;
    }

  QString matrix_string;

  static_cast<pappso::BaseColorMapPlotWidget *>(mp_plotWidget)
    ->dataToMatrixString(matrix_string, detailed);

  QTextStream outStream(&file);

  outStream << matrix_string;
  outStream.flush();

  file.close();

  return true;
}


} // namespace MineXpert

} // namespace MsXpS
