
/*******************************************************************************
 * Copyright (c) 2017 Olivier Langella <olivier.langella@u-psud.fr>.
 *
 * This file is part of i2MassChroQ.
 *
 *     i2MassChroQ 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.
 *
 *     i2MassChroQ 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 i2MassChroQ.  If not, see <http://www.gnu.org/licenses/>.
 *
 * Contributors:
 *     Olivier Langella <olivier.langella@u-psud.fr> - initial API and
 *implementation
 ******************************************************************************/

#include "peptidewindow.h"
#include "../../utils/utils.h"
#include "../project_view/projectwindow.h"
#include "../../config.h"
#include "ui_peptide_detail_view.h"
#include <pappsomspp/core/exception/exceptionnotfound.h>
#include <pappsomspp/core/massspectrum/qualifiedmassspectrum.h>
#include <pappsomspp/core/msfile/mzformatenumstr.h>
#include <pappsomspp/core/psm/features/psmfeatures.h>
#include <QFileDialog>
#include <QSettings>
#include <QDebug>
#include <QProcess>
#include <QSvgGenerator>
#include <QMessageBox>
#include <QSpacerItem>
#include <pappsomspp/core/utils.h>
#include <pappsomspp/core/psm/peptideisotopespectrummatch.h>
#include <pappsomspp/core/peptide/peptidefragmentionlistbase.h>


void
SpectrumSpLoaderThread::doLoadSpectrumSp(PeptideEvidence *p_peptide_evidence)
{
  qDebug() << "begin";
  pappso::QualifiedMassSpectrumCstSPtr spectrum;
  try
    {
      spectrum = p_peptide_evidence->getQualifiedMassSpectrum(true)
                   .makeQualifiedMassSpectrumCstSPtr();


      emit spectrumSpReady(spectrum, QString(""), QString(""));
    }

  catch(pappso::ExceptionNotFound &error)
    {
      qDebug() << error.qwhat();
      emit spectrumSpReady(spectrum, error.qwhat(), QString(""));
    }

  catch(pappso::PappsoException &error)
    {
      qDebug() << error.qwhat();
      emit spectrumSpReady(spectrum, QString(""), error.qwhat());
    }

  catch(std::exception &error)
    {
      qDebug() << error.what();
      emit spectrumSpReady(spectrum, QString(""), QString(error.what()));
    }
  qDebug();
}

PeptideWindow::PeptideWindow(ProjectWindow *parent)
  : QMainWindow(parent), ui(new Ui::PeptideDetailView)
{
  qDebug() << "begin";
  _p_project_window = parent;
  ui->setupUi(this);
  /*
   */
  SpectrumSpLoaderThread *worker = new SpectrumSpLoaderThread;
  worker->moveToThread(&_spectrum_loader_thread);
  _spectrum_loader_thread.start();


  if(_p_project_window->getProjectP() != nullptr)
    {
      setWindowTitle(
        QString("%1 - Peptide details")
          .arg(_p_project_window->getProjectP()->getProjectName()));
    }

  QSettings settings;
  QString precision_str =
    settings.value("peptideview/precision", "0.2 dalton").toString();

  _p_precision = pappso::PrecisionFactory::fromString(precision_str);

  ui->file_not_found->setVisible(false);

  mp_movie = new QMovie(":icons/resources/icons/icon_wait.gif");
  mp_movie->start();
  mp_movie->setScaledSize(QSize(20, 20));

  _mz_label = new QLabel("");
  _mz_label->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum);
  _mz_label->setMovie(mp_movie);
  ui->statusbar->addWidget(_mz_label);
  mp_status_label = new QLabel("");
  ui->statusbar->addWidget(mp_status_label);
  QWidget *p_spacer = new QWidget();
  p_spacer->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum);
  ui->statusbar->addWidget(p_spacer);
  _peak_label = new QLabel("");
  _peak_label->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum);
  ui->statusbar->addWidget(_peak_label);
  ui->statusbar->addWidget(p_spacer);
  _ion_label = new QLabel("");
  _ion_label->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum);
  ui->statusbar->addWidget(_ion_label);

  ui->spectrum_widget->setMaximumIsotopeNumber(3);
  ui->spectrum_widget->setMaximumIsotopeRank(1);

  ui->pushButton->setVisible(checkPeptideViewerWorking());


#if QT_VERSION >= 0x050000
  // Qt5 code
  connect(_p_project_window,
          &ProjectWindow::identificationGroupGrouped,
          this,
          &PeptideWindow::doIdentificationGroupGrouped);

  connect(this,
          &PeptideWindow::loadSpectrumSp,
          worker,
          &SpectrumSpLoaderThread::doLoadSpectrumSp);
  connect(worker,
          &SpectrumSpLoaderThread::spectrumSpReady,
          this,
          &PeptideWindow::doSpectrumSpReady);
  connect(_p_project_window,
          &ProjectWindow::projectNameChanged,
          this,
          &PeptideWindow::doProjectNameChanged);

  /*
  connect(ui->spectrum_widget,
          &pappso::SpectrumWidget::mzChanged,
          this,
          &PeptideWindow::setMz);
  connect(ui->spectrum_widget,
          qOverload<const pappso::Peak *>(&pappso::SpectrumWidget::peakChanged),
          this,
          &PeptideWindow::setPeak);
  connect(
    ui->spectrum_widget,
    qOverload<pappso::PeakIonIsotopeMatch>(&pappso::SpectrumWidget::ionChanged),
    this,
    &PeptideWindow::setIon);

          */
#else
  // Qt4 code
  connect(_p_project_window,
          SIGNAL(identificationGroupGrouped(IdentificationGroup *)),
          this,
          SLOT(doIdentificationGroupGrouped(IdentificationGroup *)));

  connect(this,
          SIGNAL(loadSpectrumSp(PeptideMatch *)),
          worker,
          SLOT(doLoadSpectrumSp(PeptideMatch *)));
  connect(worker,
          SIGNAL(spectrumSpReady(pappso::SpectrumSp, QString, QString)),
          this,
          SLOT(doSpectrumSpReady(pappso::SpectrumSp, QString, QString)));
  connect(
    _p_spectrum_overlay, SIGNAL(mzChanged(double)), this, SLOT(setMz(double)));
  // connect(_protein_table_model_p, SIGNAL(layoutChanged()), this,
  // SLOT(updateStatusBar()));
#endif

  qDebug() << "end";
}

PeptideWindow::~PeptideWindow()
{

  _spectrum_loader_thread.quit();
  _spectrum_loader_thread.wait();
  delete ui;
}


void
PeptideWindow::setIon(pappso::PeakIonIsotopeMatchCstSPtr ion)
{
  if(ion == nullptr)
    {
      _ion_label->setText("");
    }
  else
    {
      // String plusstr = "+";
      // plusstr         = plusstr.repeated(ion->getCharge());
      _ion_label->setText(
        QString("%1 (+%2) - %3 - th. isotope ratio %4%")
          .arg(ion->getPeptideFragmentIonSp().get()->getCompletePeptideIonName(
            ion->getCharge()))
          .arg(
            ion->getPeptideNaturalIsotopeAverageSp().get()->getIsotopeNumber())
          .arg(ion->getPeptideFragmentIonSp().get()->getSequence())
          .arg((int)(ion->getPeptideNaturalIsotopeAverageSp()
                       .get()
                       ->getIntensityRatio() *
                     100)));
    }
}

void
PeptideWindow::setPeak(pappso::DataPointCstSPtr p_peak_match)
{
  qDebug() << "begin";
  if(p_peak_match == nullptr)
    {
      _peak_label->setText(QString("no peak"));
      _ion_label->setText("");
    }
  else
    {
      _peak_label->setText(QString("peak mz=%1 intensity=%2")
                             .arg(QString::number(p_peak_match->x, 'g', 10))
                             .arg(p_peak_match->y));
    }
}

void
PeptideWindow::setMz(double mz)
{
  if(mz > 0)
    {
      _mz_label->setText(QString("mz=%1").arg(QString::number(mz, 'g', 10)));
    }
  else
    {
      _mz_label->setText("");
    }
}

void
PeptideWindow::doIdentificationGroupGrouped(
  IdentificationGroup *p_identification_group [[maybe_unused]])
{
  updateDisplay();
}

void
PeptideWindow::updateDisplay()
{
  try
    {
      qDebug();
      ui->sequence_label->setText(_p_peptide_evidence->toProForma());
      ui->mz_label->setText(
        QString("%1").arg(_p_peptide_evidence->getPeptideXtpSp().get()->getMz(
          _p_peptide_evidence->getCharge())));
      ui->z_label->setText(QString("%1").arg(_p_peptide_evidence->getCharge()));

      if(_p_peptide_evidence->getMsRunP()->getMsDataFormat() ==
         pappso::Enums::MsDataFormat::unknown)
        {

          ui->scan_label->setText(
            QString("%1").arg(_p_peptide_evidence->getScanNumber()));
        }
      else
        {
          ui->scan_label->setText(
            QString("%1 : %2")
              .arg(_p_peptide_evidence->getScanNumber())
              .arg(pappso::MsDataFormatEnumStr::toString(
                _p_peptide_evidence->getMsRunP()->getMsDataFormat())));
          ui->scan_label->setToolTip(
            QString("scan number %1 in %2 format")
              .arg(_p_peptide_evidence->getScanNumber())
              .arg(pappso::MsDataFormatEnumStr::toString(
                _p_peptide_evidence->getMsRunP()->getMsDataFormat())));
        }
      ui->sample_label->setText(
        _p_peptide_evidence->getMsRunP()->getSampleName());
      ui->sample_label->setToolTip(
        _p_peptide_evidence->getMsRunPtr()->getFileName());
      ui->modification_label->setText(
        _p_peptide_evidence->getPeptideXtpSp().get()->getModifString());
      ui->hyperscore_label->setText(
        _p_peptide_evidence->getParam(PeptideEvidenceParam::tandem_hyperscore)
          .toString());
      ui->evalue_label->setText(
        QString::number(_p_peptide_evidence->getEvalue(), 'g', 4));
      ui->mh_label->setText(QString::number(
        _p_peptide_evidence->getPeptideXtpSp().get()->getMz(1), 'f', 4));
      ui->mz_label->setText(
        QString::number(_p_peptide_evidence->getPeptideXtpSp().get()->getMz(
                          _p_peptide_evidence->getCharge()),
                        'f',
                        4));
      ui->expmz_label->setText(
        QString::number(_p_peptide_evidence->getExperimentalMz(), 'f', 4));
      ui->delta_label->setText(
        QString::number(_p_peptide_evidence->getDeltaMass(), 'g', 4));
      ui->retention_time_label->setText(
        QString::number(_p_peptide_evidence->getRetentionTime(), 'f', 2));
      ui->retention_time_min_label->setText(
        QString::number(_p_peptide_evidence->getRetentionTime() / 60, 'f', 2));

      qDebug();
      updateIonMobilityDisplay();

      updatePsmFeaturesDisplay();
    }
  catch(pappso::PappsoException &exception_pappso)
    {
      QMessageBox::warning(this,
                           tr("Unable to display peptide details :"),
                           exception_pappso.qwhat());
    }
  catch(std::exception &exception_std)
    {
      QMessageBox::warning(
        this, tr("Unable to display peptide details :"), exception_std.what());
    }
}

void
PeptideWindow::updatePsmFeaturesDisplay()
{
  qDebug();

  try
    {
      if(_p_peptide_evidence != nullptr)
        {

          qDebug() << mcsp_qualifiedMassSpectrum.get();
          if(mcsp_qualifiedMassSpectrum.get() != nullptr)
            {
              qDebug();
              pappso::PsmFeatures features(_p_precision, 150);

              pappso::MassSpectrumCstSPtr spectrum_sp;
              spectrum_sp =
                mcsp_qualifiedMassSpectrum.get()->getMassSpectrumCstSPtr();


              features.setPeptideSpectrumCharge(
                _p_peptide_evidence->getPeptideXtpSp(),
                spectrum_sp.get(),
                _p_peptide_evidence->getCharge(),
                2);


              ui->pairs_value->setText(
                QString("%1").arg(features.countMatchedIonComplementPairs()));
              ui->pairs_percent_value->setText(
                QString("%1%").arg(QString::number(
                  (features.getTotalIntensityOfMatchedIonComplementPairs() /
                   features.getTotalIntensity()) *
                    100,
                  'f',
                  2)));

              ui->intensity_cov_value->setText(QString("%1%").arg(
                QString::number((features.getTotalIntensityOfMatchedIons() /
                                 features.getTotalIntensity()) *
                                  100,
                                'f',
                                2)));

              ui->peak_sd_value->setText(
                QString("%1").arg(features.getMatchedMzDiffSd()));


              ui->matched_peak_value->setText(
                QString("%1").arg(features.getNumberOfMatchedIons()));

              ui->max_ion_y_value->setText(QString("%1").arg(
                features.getMaxConsecutiveIon(pappso::Enums::PeptideIon::y)));
              ui->max_ion_b_value->setText(QString("%1").arg(
                features.getMaxConsecutiveIon(pappso::Enums::PeptideIon::b)));
            }
          qDebug();
        }
    }
  catch(pappso::PappsoException &error)
    {
      qWarning() << error.qwhat();
    }
}

void
PeptideWindow::updateIonMobilityDisplay()
{
  qDebug();

  ui->ion_mobility_form_widget->hide();
  ui->ion_mobility_line->hide();
  if(mcsp_qualifiedMassSpectrum != nullptr)
    {
      qDebug();
      QVariant ion_mobility_begin =
        mcsp_qualifiedMassSpectrum.get()->getParameterValue(
          pappso::QualifiedMassSpectrumParameter::IonMobOneOverK0Begin);
      qDebug();
      if(!ion_mobility_begin.isNull())
        {
          qDebug();
          ui->ion_mobility_form_widget->show();
          ui->ion_mobility_line->show();

          QString im_begin_val =
            QString::number(ion_mobility_begin.toDouble(), 'f', 3);
          QString im_end_val = QString::number(
            mcsp_qualifiedMassSpectrum.get()
              ->getParameterValue(
                pappso::QualifiedMassSpectrumParameter::IonMobOneOverK0End)
              .toDouble(),
            'f',
            3);
          ui->ion_mobility_begin_value->setText(im_begin_val + " 1/K0");
          ui->ion_mobility_end_value->setText(im_end_val + " 1/K0");

          ui->collision_value->setText(QString("%1 eV").arg(
            mcsp_qualifiedMassSpectrum.get()
              ->getParameterValue(
                pappso::QualifiedMassSpectrumParameter::CollisionEnergy)
              .toInt()));
        }
    }
  qDebug();
}


void
PeptideWindow::chooseDefaultMzDataDir()
{
  QSettings settings;
  QString default_location = settings.value("path/mzdatadir", "").toString();

  QString filename =
    QFileDialog::getExistingDirectory(this,
                                      tr("Choose default mz data directory"),
                                      default_location,
                                      QFileDialog::ShowDirsOnly);

  if(filename.isEmpty())
    {
      return;
    }
  QString path = QFileInfo(filename).absoluteFilePath();
  settings.setValue("path/mzdatadir", path);
  ui->mz_data_dir_label->setText(path);


  qDebug() << "begin";
  ui->file_not_found->setVisible(false);
  ui->xic_button->setEnabled(true);
  ui->spectrum_widget->setVisible(true);
  emit loadSpectrumSp(_p_peptide_evidence);
  mp_status_label->setText("loading spectrum");
}

void
PeptideWindow::openInPeptideViewer()
{
  QSettings settings;
  QString program =
    settings.value("application/peptideviewer", "/usr/bin/pt-peptideviewer")
      .toString();
  QStringList arguments;
  arguments << _p_peptide_evidence->getPeptideXtpSp().get()->toString()
            << QString("%1").arg(_p_peptide_evidence->getCharge())
            << _p_peptide_evidence->getMsRunP()->getFileName()
            << QString("%1").arg(_p_peptide_evidence->getScanNumber());

  qDebug() << program << " " << arguments.join(" ");
  QProcess *myProcess = new QProcess(this);
  myProcess->start(program, arguments);
}


void
PeptideWindow::openInXicViewer()
{
  _p_project_window->openInXicViewer(_p_peptide_evidence);
}

void
PeptideWindow::doSpectrumSpReady(
  pappso::QualifiedMassSpectrumCstSPtr spectrum_sp,
  QString error,
  QString fatal_error)
{
  qDebug() << "begin error=" << error << " fatal_error=" << fatal_error;
  mcsp_qualifiedMassSpectrum = spectrum_sp;
  mp_status_label->clear();
  _mz_label->clear();
  ui->precursor_intensity_label->clear();

  if((error.isEmpty()) && (fatal_error.isEmpty()))
    {
      ui->file_not_found->setVisible(false);
      ui->spectrum_widget->setVisible(true);
      ui->xic_button->setEnabled(true);

      ui->spectrum_widget->setMsLevel(2);
      ui->spectrum_widget->setMassSpectrumCstSPtr(
        mcsp_qualifiedMassSpectrum.get()->getMassSpectrumCstSPtr());
      qDebug() << "plot";
      ui->spectrum_widget->plot();
      qDebug() << "highlightPrecursorPeaks";
      ui->spectrum_widget->highlightPrecursorPeaks();
      qDebug() << "rescale";
      ui->spectrum_widget->rescale();

      ui->precursor_intensity_label->setText(QString::number(
        mcsp_qualifiedMassSpectrum.get()->getPrecursorIntensity(), 'g', 10));

      _spectrum_is_ready = true;
    }
  if(!error.isEmpty())
    {
      // not found
      mp_status_label->setText(tr("spectrum not found"));
      QSettings settings;
      QString path = settings.value("path/mzdatadir", "").toString();

      ui->mz_data_dir_label->setText(path);

      ui->file_not_found->setVisible(true);
      ui->spectrum_widget->setVisible(false);
      ui->xic_button->setEnabled(false);
      _spectrum_is_ready = false;
    }
  if(!fatal_error.isEmpty())
    {
      // fatal_error

      QMessageBox::warning(
        this,
        tr("Oops! an error occurred in i2MassChroQ. Don't panic :"),
        fatal_error);
      mp_status_label->setText(tr("ERROR reading spectrum"));
      QSettings settings;
      QString path = settings.value("path/mzdatadir", "").toString();

      ui->mz_data_dir_label->setText(path);

      ui->file_not_found->setVisible(true);
      ui->spectrum_widget->setVisible(false);
      _spectrum_is_ready = false;
    }
  updateDisplay();
  qDebug() << "end";
}

void
PeptideWindow::setPeptideEvidence(PeptideEvidence *p_peptide_evidence)
{
  qDebug() << "begin " << p_peptide_evidence;
  _p_peptide_evidence = p_peptide_evidence;
  _spectrum_is_ready  = false;
  ui->precursor_intensity_label->setText("");
  mcsp_qualifiedMassSpectrum = nullptr;

  qDebug() << "1";

  ui->file_not_found->setVisible(false);
  ui->spectrum_widget->setVisible(true);
  mp_status_label->setText("loading spectrum");

  qDebug() << "2";
  pappso::PeptideSp peptide = _p_peptide_evidence->getPeptideXtpSp();
  qDebug() << "3";
  ui->spectrum_widget->setMs2Precision(_p_precision);
  ui->spectrum_widget->setPeptideCharge(_p_peptide_evidence->getCharge());
  ui->spectrum_widget->setPeptideSp(peptide);

  qDebug() << "4";
  // ui->spectrum_widget->plot();

  updateDisplay();
  qDebug() << " emit "
              "loadSpectrumSp(_p_peptide_match)";
  emit loadSpectrumSp(_p_peptide_evidence);
  qDebug() << "end";
}

void
PeptideWindow::doMsmsPrecisionChanged(pappso::PrecisionPtr precision)
{

  qDebug() << "begin " << precision->toString();
  QSettings settings;
  _p_precision = precision;
  ui->spectrum_widget->setMs2Precision(_p_precision);
  qDebug() << " plot ";
  ui->spectrum_widget->plot();

  settings.setValue("peptideview/precision", precision->toString());

  updatePsmFeaturesDisplay();

  qDebug() << "end ";
}

void
PeptideWindow::doSaveSvg()
{

  try
    {
      QSettings settings;
      QString default_location =
        settings.value("path/export_svg", "").toString();

      QString proposed_filename =
        QString("%1/%2_%3.svg")
          .arg(default_location)
          .arg(_p_peptide_evidence->getMsRunP()->getSampleName())
          .arg(_p_peptide_evidence->getScanNumber());

      if(_p_peptide_evidence->getValidationState() == ValidationState::grouped)
        {
          proposed_filename =
            QString("%1/%2_%3_%4.svg")
              .arg(default_location)
              .arg(
                _p_peptide_evidence->getGrpPeptideSp().get()->getGroupingId())
              .arg(_p_peptide_evidence->getMsRunP()->getSampleName())
              .arg(_p_peptide_evidence->getScanNumber());
        }

      QString filename = QFileDialog::getSaveFileName(
        this,
        tr("Save SVG file"),
        proposed_filename,
        tr("Scalable Vector Graphic (*.svg);;Json file (*.json)"));

      if(filename.isEmpty())
        {
          return;
        }

      settings.setValue("path/export_svg", QFileInfo(filename).absolutePath());


      if(QFileInfo(filename).suffix() == "json")
        {
          writeJsonPsmIsotope(filename);
        }
      else
        {
          ui->spectrum_widget->toSvgFile(
            filename,
            tr("%1 SVG spectrum generator").arg(SOFTWARE_NAME),
            tr("This is an annotated SVG spectrum"),
            QSize(1200, 500));
          // emit operateXpipFile(filename);
        }
    }
  catch(pappso::PappsoException &error)
    {
      QMessageBox::warning(
        this, tr("Error trying to save spectrum to SVG file :"), error.qwhat());
    }
}

void
PeptideWindow::doProjectNameChanged(QString name)
{
  setWindowTitle(tr("%1 - Peptide details").arg(name));
}

bool
PeptideWindow::checkPeptideViewerWorking()
{
  QSettings settings;
  QString program =
    settings.value("application/peptideviewer", "/usr/bin/pt-peptideviewer")
      .toString();
  QFileInfo program_info = QFileInfo(program);
  if(program_info.exists())
    {
      if(program_info.isExecutable())
        {
          return true;
        }
    }
  return false;
}

void
PeptideWindow::writeJsonPsmIsotope(const QString &filein)
{
  QFile jsonf(filein);


  if(jsonf.open(QIODevice::WriteOnly))
    {

      QJsonDocument doc;
      QJsonObject psm_json;


      psm_json = ui->spectrum_widget->getJsonPsmIsotope();


      doc.setObject(psm_json);
      jsonf.write(doc.toJson());
      jsonf.close();
    }
  else
    {
      throw pappso::PappsoException(
        QObject::tr("Unable to write JSON output file %1").arg(filein));
    }
}
