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


/////////////////////// 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 <minexpert/gui/AnalysisPreferencesDlg.hpp>
#include <minexpert/nongui/AnalysisPreferences.hpp>


namespace msXpSmineXpert
{

QString labelSeparator = " : ";

AnalysisPreferencesDlg::AnalysisPreferencesDlg(QWidget *parent,
                                               const QString &applicationName)
  : QDialog{parent}, m_applicationName{applicationName}
{
  m_ui.setupUi(this);

  initialize();
}


AnalysisPreferencesDlg::~AnalysisPreferencesDlg()
{
}


void
AnalysisPreferencesDlg::readSettings()
{
  // Now fill in the history combo box
  QSettings settings;

  settings.beginGroup("AnalysisPreferencesDlg");

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

  // 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_analysisPreferences.m_dataFormatStringSpecifHash.value(iter);

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

      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() << __FILE__ << __LINE__ << "Reading setting key/value for
      // iter"
      //<< iter << settingsKey << settingsValue;
    }

  settings.endGroup();
}


void
AnalysisPreferencesDlg::writeSettings()
{
  QStringList historyList;

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

  QSettings settings;

  settings.beginGroup("AnalysisPreferencesDlg");

  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_analysisPreferences.m_dataFormatStringSpecifHash.value(iter);

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

      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() << __FILE__ << __LINE__ << "Writing setting key/value for
      // iter"
      //<< iter << settingsKey << settingsValue;
    }

  settings.endGroup();
}


void
AnalysisPreferencesDlg::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_analysisPreferences:

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

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

      // 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 += "%f : mass spectrometry data file name\n";
  helpText += "%X : value on the X axis (no unit)\n";
  helpText += "%Y : value on the Y axis (no unit)\n";
  helpText += "%x : delta value on the X axis (when appropriate, no unit)\n";
  helpText += "%y : delta value on the Y axis (when appropriate, no unit)\n";
  helpText += "%I : TIC intensity after TIC integration over a graph range\n";
  helpText += "%s : X axis range start for computation (where applicable)\n";
  helpText += "%e : X axis range end for computation (where applicable)\n";

  helpText += "\nMass spec plot widgets:\n";
  helpText += "\t%z : charge\n";
  helpText += "\t%M : Mr\n";

  m_ui.helpPlainTextEdit->appendPlainText(helpText);

  // Make the connections

  connect(m_ui.analysisFilePushButton,
          &QPushButton::clicked,
          this,
          &AnalysisPreferencesDlg::openAnalysisFile);

  connect(m_ui.okPushButton,
          &QPushButton::clicked,
          this,
          &AnalysisPreferencesDlg::okPushButton);

  connect(m_ui.cancelPushButton,
          &QPushButton::clicked,
          this,
          &AnalysisPreferencesDlg::cancelPushButton);

  connect(m_ui.addToHistoryPushButton,
          &QPushButton::clicked,
          this,
          &AnalysisPreferencesDlg::addToHistoryPushButton);

  connect(m_ui.removeFromHistoryPushButton,
          &QPushButton::clicked,
          this,
          &AnalysisPreferencesDlg::removeFromHistoryPushButton);

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


void
AnalysisPreferencesDlg::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
AnalysisPreferencesDlg::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_analysisPreferences.m_dataFormatStringSpecifHash.value(idx);
  if(specif == nullptr)
    qFatal("Fatal error at %s@%d. Program aborted.", __FILE__, __LINE__);

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

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

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


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

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


void
AnalysisPreferencesDlg::okPushButton()
{
  // 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_analysisPreferences.m_recordTarget = RecordTarget::RECORD_NOTHING;

  // qDebug() << __FILE__ << __LINE__
  // << "Record target: " << m_analysisPreferences.m_recordTarget ;

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

  if(m_ui.toClipboardCheckBox->isChecked())
    {
      m_analysisPreferences.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_analysisPreferences.m_recordTarget |= RecordTarget::RECORD_TO_FILE;

  // qDebug() << __FILE__ << __LINE__
  // << "Record target: " << m_analysisPreferences.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_analysisPreferences.m_recordTarget & RecordTarget::RECORD_TO_FILE) ==
     RecordTarget::RECORD_TO_FILE)
    {
      // qDebug() << __FILE__ << __LINE__
      // << "Record to file bit set.";

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

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

          m_analysisPreferences.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() << __FILE__ << __LINE__
          // << "append checked";

          m_analysisPreferences.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_analysisPreferences instance has
      // been correctly setup.

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

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

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


  // Before accepting, we need to store the history to the settings,
  // alongside the geometry of the window.
  writeSettings();

  setResult(QDialog::Accepted);
  done(QDialog::Accepted);
}


void
AnalysisPreferencesDlg::cancelPushButton()
{
  setResult(QDialog::Rejected);
  done(QDialog::Rejected);
}


void
AnalysisPreferencesDlg::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_analysisPreferences.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;
        }
    }
}


AnalysisPreferences
AnalysisPreferencesDlg::analysisPreferences()
{
  return m_analysisPreferences;
}

} // namespace msXpSmineXpert
