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


/////////////////////// Qt includes
#include <QObject>
#include <QDebug>
#include <QDialog>
#include <QWidget>
#include <QDir>
#include <QFileDialog>
#include <QMessageBox>
#include <QDate>
#include <QSettings>
#include <QPlainTextEdit>
#include <QApplication>
#include <QClipboard>


/////////////////////// Local includes
#include "DiscoveriesPreferencesDlg.hpp"
#include "../nongui/DiscoveriesPreferences.hpp"
#include "Application.hpp"
#include "ui_DiscoveriesPreferencesDlg.h"

namespace MsXpS
{
namespace MineXpert
{


QString labelSeparator = " : ";

DiscoveriesPreferencesDlg::DiscoveriesPreferencesDlg(
  QWidget *parent, const QString &applicationName)
  : QDialog{parent},
    m_applicationName{applicationName},
    m_ui(new ::Ui::DiscoveriesPreferencesDlg)
{
  m_ui->setupUi(this);

  initialize();
}

DiscoveriesPreferencesDlg::~DiscoveriesPreferencesDlg()
{
}

void
DiscoveriesPreferencesDlg::readSettings()
{
  // Now fill in the history combo box
  QSettings settings(static_cast<Application *>(QCoreApplication::instance())
                       ->getUserConfigSettingsFilePath(),
                     QSettings::IniFormat);

  settings.beginGroup("DiscoveriesPreferencesDlg");

  QStringList historyList = settings.value("history").toStringList();
  m_ui->historyComboBox->insertItems(0, historyList);

  m_ui->destSplitter->restoreState(
    settings.value("destSplitter").toByteArray());
  m_ui->formatSplitter->restoreState(
    settings.value("formatSplitter").toByteArray());

  // Also restore the geometry of the window.
  restoreGeometry(settings.value("geometry").toByteArray());

  // Now the various format strings for the tic, ms, dt tab widgets, if any.

  for(int iter = 0; iter < FormatType::LAST; ++iter)
    {
      DataFormatStringSpecif *specif =
        m_discoveriesPreferences.m_dataFormatStringSpecifHash.value(iter);

      if(specif == nullptr)
        qFatal() << "Programming error.";

      QString tabString   = m_ui->formatsTabWidget->tabText(iter);
      QString settingsKey = QString("%1_formatString").arg(tabString);

      QString settingsValue = settings.value(settingsKey).toString();

      QPlainTextEdit *textEdit =
        static_cast<QPlainTextEdit *>(m_ui->formatsTabWidget->widget(iter));

      textEdit->setPlainText(settingsValue);

      // qDebug() << "Reading setting key/value for iter"
      //<< iter << settingsKey << settingsValue;
    }

  settings.endGroup();
}

void
DiscoveriesPreferencesDlg::writeSettings()
{
  QStringList historyList;

  for(int iter = 0; iter < m_ui->historyComboBox->count(); ++iter)
    historyList.append(m_ui->historyComboBox->itemText(iter));

  QSettings settings(static_cast<Application *>(QCoreApplication::instance())
                       ->getUserConfigSettingsFilePath(),
                     QSettings::IniFormat);

  settings.beginGroup("DiscoveriesPreferencesDlg");

  settings.setValue("destSplitter", m_ui->destSplitter->saveState());
  settings.setValue("formatSplitter", m_ui->formatSplitter->saveState());

  settings.setValue("history", historyList);
  settings.setValue("geometry", saveGeometry());

  // And now store the different format strings, if there are any, so that
  // upon reopening, it won't necessarily be required to set them again and
  // again.

  for(int iter = 0; iter < FormatType::LAST; ++iter)
    {
      DataFormatStringSpecif *specif =
        m_discoveriesPreferences.m_dataFormatStringSpecifHash.value(iter);

      if(specif == nullptr)
        qFatal() << "Programming error.";

      QString tabString   = specif->m_label;
      QString settingsKey = QString("%1_formatString").arg(tabString);

      QPlainTextEdit *textEdit =
        static_cast<QPlainTextEdit *>(m_ui->formatsTabWidget->widget(iter));

      QString settingsValue = textEdit->toPlainText();

      settings.setValue(settingsKey, settingsValue);

      // qDebug() << "Writing setting key/value for iter"
      //<< iter << settingsKey << settingsValue;
    }

  settings.endGroup();
}

void
DiscoveriesPreferencesDlg::initialize()
{
  // This window might be used by more than only one application, set its
  // name properly.
  setWindowTitle(QString("%1 - Analysis preferences").arg(m_applicationName));

  // Create the format tabs, using the data in the
  // m_dataFormatStringSpecifHash member of the m_discoveriesPreferences:

  for(int iter = 0; iter < FormatType::LAST; ++iter)
    {
      DataFormatStringSpecif *specif =
        m_discoveriesPreferences.m_dataFormatStringSpecifHash.value(iter);

      if(specif == nullptr)
        qFatal() << "Programming error.";

      // First allocate an new plain text edit widget that we'll set to the
      // tab when we create it below. We want to store a pointer to the text
      // edit widget for later use.
      QPlainTextEdit *newEdit = new QPlainTextEdit(m_ui->formatsTabWidget);
      m_plainTextEditList.append(newEdit);

      m_ui->formatsTabWidget->insertTab(iter, newEdit, specif->m_label);
    }

  // Restore the geometry of the window and fill in the history combo box.
  // And also change the contents of the various tab widgets if the settings
  // had stored something.

  readSettings();

  // At this point we need to display the help text that shows the various
  // possibilities for the format string.

  QString helpText;

  helpText +=
    "The formatting of the text describing the exploration session "
    "discovery is described below. It uses specific format patterns, like '%f' "
    "that will be replaced with corresponding value, like the filename, for "
    "example.\n\n";

  helpText +=
    "The numeric values that can be used to describe a discovery might "
    "be of differnt types: "
    "  - floats with decimals (m/z value)"
    "  - integers without decimals (charge z)\n\n";

  helpText +=
    "When float numeric values require a specific number of decimals, "
    "that can be specified using the decimals specifier of the form '.3', for "
    "example "
    "if the float number needs to be printed out with three decimas.\n\n";

  helpText +=
    "Float values for which no decimal specifier is set are printed "
    "out with 2 decimals by default.\n\n";

  helpText +=
    "If a float value is requied without decimal, use the '.0' decimal "
    "specifier.\n\n";

  helpText += "These are the available format strings:\n\n";

  helpText += "%f: mass spectrometry data file name\n";
  helpText += "%s: sample name\n";

  helpText += "%X (or %X.3): value on the X axis (no unit)\n";
  helpText += "%Y (or %Y.1): value on the Y axis at cursor point(no unit)\n";
  helpText += "%C (or %C.1): value on the Y axis on the curve (no unit)\n";

  helpText +=
    "%x (or %x.6): delta value on the X axis (when appropriate, no unit)\n";
  helpText +=
    "%y (or %y.1): delta value on the Y axis (when appropriate, no unit)\n";
  helpText +=
    "%c (or %c.1): delta value on the Y axis on the curve (no unit)\n";

  helpText +=
    "%I (or %I.1): TIC intensity after TIC integration over a graph range\n";

  helpText += "%b (or %b.3): X axis range begin value (where applicable)\n";
  helpText += "%e (or %e.3): X axis range end value (where applicable)\n";

  helpText += "\nMass spec plot widgets:\n";
  helpText += "\t%z : charge\n";
  helpText +=
    "\t%M (or %M.5 for small mass ions; %M.2 for medium-mass ions): Mr\n";

  m_ui->helpPlainTextEdit->appendPlainText(helpText);

  // Make the connections

  connect(m_ui->analysisFilePushButton,
          &QPushButton::clicked,
          this,
          &DiscoveriesPreferencesDlg::openAnalysisFile);

  connect(m_ui->applyPushButton,
          &QPushButton::clicked,
          this,
          &DiscoveriesPreferencesDlg::apply);

  connect(m_ui->closePushButton,
          &QPushButton::clicked,
          this,
          &DiscoveriesPreferencesDlg::close);

  connect(m_ui->addToHistoryPushButton,
          &QPushButton::clicked,
          this,
          &DiscoveriesPreferencesDlg::addToHistoryPushButton);

  connect(m_ui->removeFromHistoryPushButton,
          &QPushButton::clicked,
          this,
          &DiscoveriesPreferencesDlg::removeFromHistoryPushButton);

  connect(m_ui->historyComboBox,
          static_cast<void (QComboBox::*)(int)>(&QComboBox::activated),
          this,
          static_cast<void (DiscoveriesPreferencesDlg::*)(int)>(
            &DiscoveriesPreferencesDlg::historyItemActivated));
}

void
DiscoveriesPreferencesDlg::openAnalysisFile()
{
  QString fileName =
    QFileDialog::getSaveFileName(this,
                                 tr("Select the data analysis file"),
                                 QDir::home().absolutePath(),
                                 tr("Any file (*)"),
                                 0,
                                 QFileDialog::DontConfirmOverwrite);

  m_ui->analysisFileLineEdit->setText(fileName);
  m_ui->analysisFileLineEdit->setToolTip(fileName);
}

void
DiscoveriesPreferencesDlg::addToHistoryPushButton()
{
  // First get the tab that is currently visible, so that we know to what
  // history we need to add the contents of the plain text edit widget.

  int idx      = m_ui->formatsTabWidget->currentIndex();
  QString text = m_plainTextEditList.at(idx)->toPlainText();

  DataFormatStringSpecif *specif =
    m_discoveriesPreferences.m_dataFormatStringSpecifHash.value(idx);
  if(specif == nullptr)
    qFatal() << "Programming error.";

  QString historyText =
    QString("%1%2%3").arg(specif->m_label).arg(labelSeparator).arg(text);

  m_ui->historyComboBox->addItem(historyText, Qt::DisplayRole);

  // qDebug() << "Appending history:" << historyText;
}

void
DiscoveriesPreferencesDlg::removeFromHistoryPushButton()
{
  // Get the current item index from the combo box.

  int idx = m_ui->historyComboBox->currentIndex();
  m_ui->historyComboBox->removeItem(idx);
}

void
DiscoveriesPreferencesDlg::apply()
{
  // First check if the analysis should be recorded to the console (by
  // default it is).

  // Start by setting the value to a ground state.
  m_discoveriesPreferences.m_recordTarget = RecordTarget::RECORD_NOTHING;

  // qDebug() << "Record target: " << m_discoveriesPreferences.m_recordTarget ;

  if(m_ui->toConsoleCheckBox->isChecked())
    m_discoveriesPreferences.m_recordTarget |= RecordTarget::RECORD_TO_CONSOLE;

  if(m_ui->toClipboardCheckBox->isChecked())
    {
      m_discoveriesPreferences.m_recordTarget |= RecordTarget::RECORD_TO_CLIPBOARD;

      QMessageBox::StandardButton ret;
      QString msgBoxTitle =
        QString("%1 - Record to clipboard").arg(m_applicationName);
      ret = QMessageBox::warning(this,
                                 msgBoxTitle,
                                 tr("Initialize the clipboard by emptying it?"),
                                 QMessageBox::Yes | QMessageBox::No);

      if(ret == QMessageBox::Yes)
        {
          QClipboard *clipboard = QApplication::clipboard();
          clipboard->setText("", QClipboard::Clipboard);
        }
    }

  if(m_ui->toFileGroupBox->isChecked())
    m_discoveriesPreferences.m_recordTarget |= RecordTarget::RECORD_TO_FILE;

  // qDebug() << "Record target: " << m_discoveriesPreferences.m_recordTarget ;

  // And now if the record should be directed to a file, make the
  // verifications that we can open the files in either write only or append
  // mode.

  if((m_discoveriesPreferences.m_recordTarget & RecordTarget::RECORD_TO_FILE) ==
     RecordTarget::RECORD_TO_FILE)
    {
      // qDebug() << "Record to file bit set.";

      m_discoveriesPreferences.m_fileName = m_ui->analysisFileLineEdit->text();
      QFile analysisFile(m_discoveriesPreferences.m_fileName);

      if(m_ui->overwriteRadioButton->isChecked())
        {
          // qDebug() << "overwrite checked";

          m_discoveriesPreferences.m_fileOpenMode = QIODevice::Truncate;

          // Interestingly, only truncate did not work. Since writeonly
          // implies
          // truncate, that's fine, and it works.

          analysisFile.open(QIODevice::WriteOnly);

          analysisFile.close();
        }
      else
        {
          // qDebug() << "append checked";

          m_discoveriesPreferences.m_fileOpenMode = QIODevice::Append;

          analysisFile.open(QIODevice::Append);
          analysisFile.close();
        }
    }

  // At this point we need to get all the format strings from the various
  // tab pages.

  for(int iter = 0; iter < FormatType::LAST; ++iter)
    {
      QPlainTextEdit *textEdit = m_plainTextEditList.at(iter);
      QString formatString     = textEdit->toPlainText();

      // We need to get the DataFormatStringSpecif instance for the current
      // iter. We assume that the member m_discoveriesPreferences instance has
      // been correctly setup.

      DataFormatStringSpecif *specif =
        m_discoveriesPreferences.m_dataFormatStringSpecifHash.value(iter);

      // Now modify the format string.
      specif->m_format = formatString;

      // qDebug() << "Dealt with iter " << iter << "with format string:" <<
      // specif->m_format;
    }


  // Store the history to the settings, alongside the geometry of the window.
  writeSettings();

  emit applySignal();
}

void
DiscoveriesPreferencesDlg::close()
{
  QDialog::close();
}

void
DiscoveriesPreferencesDlg::historyItemActivated(int index)
{
  // We want to put the selected item's text in the proper tab widget.

  QString historyText = m_ui->historyComboBox->itemText(index);

  // What is the label at the beginning of the text that states what is the
  // specialized format string (TIC chrom or Mass spec or Drift spec).

  for(int iter = 0; iter < FormatType::LAST; ++iter)
    {
      // Get the DataFormatStringSpecif instance for iter
      DataFormatStringSpecif *specif =
        m_discoveriesPreferences.m_dataFormatStringSpecifHash.value(iter);

      QString label = specif->m_label;

      if(historyText.startsWith(label))
        {
          m_ui->formatsTabWidget->setCurrentIndex(iter);

          // Get a pointer to the proper tab plain text editor
          QPlainTextEdit *currentTextEdit = m_plainTextEditList.at(iter);

          // But before setting the text that we got from the combo box, we
          // need
          // to remvove from it the label and the separator that we used in
          // the
          // first place to tag that format string in the history combo box !
          QRegularExpression regExp(
            QString("^%1%2").arg(label).arg(labelSeparator));
          historyText = historyText.remove(regExp);

          currentTextEdit->setPlainText(historyText);

          return;
        }
    }
}

DiscoveriesPreferences
DiscoveriesPreferencesDlg::discoveriesPreferences()
{
  return m_discoveriesPreferences;
}


} // namespace MineXpert

} // namespace MsXpS
