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

#include "utils.h"
#include <pappsomspp/exception/exceptionnotfound.h>
#include <pappsomspp/mzrange.h>
#include <cmath>
#include <QProcess>
#include <QDebug>
#include <QFileInfo>

const QUrl
Utils::getOlsUrl(QString psimod_accession)
{

  QString iri(QString("http://purl.obolibrary.org/obo/%1")
                .arg(psimod_accession.replace(":", "_")));
  QUrl url(
    QString("http://www.ebi.ac.uk/ols/ontologies/mod/terms?iri=%1").arg(iri));
  return url;
}

const QString
Utils::getXmlDouble(pappso::pappso_double number)
{
  return QString::number(number, 'g', 6);
}

const QString
Utils::getIdentificationEngineName(IdentificationEngine engine)
{
  QString engine_name;
  switch(engine)
    {
      case IdentificationEngine::XTandem:
        engine_name = "X!Tandem";
        break;
      case IdentificationEngine::mascot:
        engine_name = "Mascot";
        break;

      case IdentificationEngine::peptider:
        engine_name = "Peptider";
        break;
      case IdentificationEngine::OMSSA:
        engine_name = "OMSSA";
        break;
      case IdentificationEngine::SEQUEST:
        engine_name = "Sequest";
        break;
      case IdentificationEngine::Comet:
        engine_name = "Comet";
        break;
      case IdentificationEngine::Morpheus:
        engine_name = "Morpheus";
        break;
      case IdentificationEngine::MSGFplus:
        engine_name = "MS-GF+";
        break;
      case IdentificationEngine::unknown:
        engine_name = "unknown";
        break;
    }
  return engine_name;
}

const QString
Utils::getDatabaseName(ExternalDatabase database)
{
  QString database_name;
  switch(database)
    {
      case ExternalDatabase::AGI_LocusCode:
        database_name = "AGI_LocusCode";
        break;
      case ExternalDatabase::NCBI_gi:
        database_name = "NCBI_gi";
        break;

      case ExternalDatabase::SwissProt:
        database_name = "Swiss-Prot";
        break;
      case ExternalDatabase::TrEMBL:
        database_name = "TrEMBL";
        break;
      case ExternalDatabase::ref:
        database_name = "ref";
        break;
      case ExternalDatabase::OboPsiMod:
        database_name = "PSI::MOD";
        break;
    }
  return database_name;
}


std::vector<std::pair<pappso::pappso_double, size_t>>
Utils::getHistogram(std::vector<pappso::pappso_double> data_values,
                    unsigned int number_of_class)
{
  std::vector<std::pair<pappso::pappso_double, size_t>> histogram(
    number_of_class + 1);
  try
    {
      qDebug() << "Utils::getHistogram begin";

      std::sort(data_values.begin(), data_values.end());
      pappso::pappso_double min   = *data_values.begin();
      pappso::pappso_double max   = *(data_values.end() - 1);
      pappso::pappso_double total = std::abs(max - min);
      pappso::pappso_double offset =
        (total / (pappso::pappso_double)number_of_class);
      // qDebug() << "Utils::getHistogram number_of_class offset=" << offset;
      for(unsigned int i = 0; i < histogram.size(); i++)
        {
          histogram[i] = std::pair<pappso::pappso_double, size_t>{
            (min + (offset * i) + (offset / 2)), 0};
          // qDebug() << "Utils::getHistogram x=" << histogram[i].first;
        }
      // qDebug() << "Utils::getHistogram data_values";
      for(pappso::pappso_double value : data_values)
        {
          // qDebug() << "Utils::getHistogram value=" << value;
          unsigned int i = std::abs((value - min) / offset);
          // qDebug() << "Utils::getHistogram i=" << i << " size=" <<
          // histogram.size();
          histogram.at(i).second++;
        }
    }
  catch(std::exception &exception_std)
    {
      throw pappso::PappsoException(
        QObject::tr("Utils::getHistogram error %1").arg(exception_std.what()));
    }
  qDebug() << "Utils::getHistogram end";
  return histogram;
}


pappso::AaModificationP
Utils::guessAaModificationPbyMonoisotopicMassDelta(pappso::pappso_double mass)
{
  pappso::PrecisionPtr precision =
    pappso::PrecisionFactory::getDaltonInstance(0.01);

  pappso::AaModificationP oxidation =
    pappso::AaModification::getInstance("MOD:00719");
  if(pappso::MzRange(oxidation->getMass(), precision).contains(mass))
    {
      return oxidation;
    }
  pappso::AaModificationP iodoacetamide =
    pappso::AaModification::getInstance("MOD:00397");
  if(pappso::MzRange(iodoacetamide->getMass(), precision).contains(mass))
    {
      return iodoacetamide;
    }
  pappso::AaModificationP acetylated =
    pappso::AaModification::getInstance("MOD:00408");
  if(pappso::MzRange(acetylated->getMass(), precision).contains(mass))
    {
      return acetylated;
    }
  pappso::AaModificationP phosphorylated =
    pappso::AaModification::getInstance("MOD:00696");
  if(pappso::MzRange(phosphorylated->getMass(), precision).contains(mass))
    {
      return phosphorylated;
    }
  pappso::AaModificationP ammonia =
    pappso::AaModification::getInstance("MOD:01160");
  if(pappso::MzRange(ammonia->getMass(), precision).contains(mass))
    {
      return ammonia;
    }
  pappso::AaModificationP dehydrated =
    pappso::AaModification::getInstance("MOD:00704");
  if(pappso::MzRange(dehydrated->getMass(), precision).contains(mass))
    {
      return dehydrated;
    }
  pappso::AaModificationP dimethylated =
    pappso::AaModification::getInstance("MOD:00429");
  if(pappso::MzRange(dimethylated->getMass(), precision).contains(mass))
    {
      return dimethylated;
    }

  pappso::AaModificationP dimethylated_medium =
    pappso::AaModification::getInstance("MOD:00552");
  if(pappso::MzRange(dimethylated_medium->getMass(), precision).contains(mass))
    {
      return dimethylated_medium;
    }

  pappso::AaModificationP dimethylated_heavy =
    pappso::AaModification::getInstance("MOD:00638");
  if(pappso::MzRange(dimethylated_heavy->getMass(), precision).contains(mass))
    {
      return dimethylated_heavy;
    }
  pappso::AaModificationP DimethylpyrroleAdduct =
    pappso::AaModification::getInstance("MOD:00628");
  if(pappso::MzRange(DimethylpyrroleAdduct->getMass(), precision)
       .contains(mass))
    {
      return DimethylpyrroleAdduct;
    }

  // modification not found, creating customized mod :
  return pappso::AaModification::getInstanceCustomizedMod(mass);

  throw pappso::ExceptionNotFound(
    QObject::tr("Utils::guessAaModificationPbyMonoisotopicMassDelta => "
                "modification not found for mass %1")
      .arg(mass));
}


pappso::AaModificationP
Utils::translateAaModificationFromUnimod(const QString &unimod_accession)
{
  if(unimod_accession == "UNIMOD:1")
    {

      return pappso::AaModification::getInstance("MOD:00394");
    }
  if(unimod_accession == "UNIMOD:4")
    {

      return pappso::AaModification::getInstance("MOD:00397");
    }
  if(unimod_accession == "UNIMOD:27")
    {

      return pappso::AaModification::getInstance("MOD:00420");
    }
  // UNIMOD:28 => MOD:00040
  if(unimod_accession == "UNIMOD:28")
    {

      return pappso::AaModification::getInstance("MOD:00040");
    }

  if(unimod_accession == "UNIMOD:35")
    {

      return pappso::AaModification::getInstance("MOD:00425");
    }
  qDebug() << __FILE__ << " " << __FUNCTION__ << " " << __LINE__ << " "
           << unimod_accession << " not found";
  return nullptr;
}


const QString
Utils::checkXtandemVersion(const QString &tandem_bin_path)
{
  qDebug() << __FILE__ << " " << __FUNCTION__ << " " << __LINE__;
  // check tandem path
  QFileInfo tandem_exe(tandem_bin_path);
  if(!tandem_exe.exists())
    {
      // dir.path() returns the unique directory path
      throw pappso::PappsoException(
        QObject::tr(
          "X!Tandem software not found at %1.\nPlease check the X!Tandem "
          "installation on your computer and set tandem.exe path.")
          .arg(tandem_exe.absoluteFilePath()));
    }
  if(!tandem_exe.isReadable())
    {
      // dir.path() returns the unique directory path
      throw pappso::PappsoException(
        QObject::tr("Please check permissions on X!Tandem software found at %1 "
                    "(file not readable).")
          .arg(tandem_exe.absoluteFilePath()));
    }
  if(!tandem_exe.isExecutable())
    {
      // dir.path() returns the unique directory path
      throw pappso::PappsoException(
        QObject::tr("Please check permissions on X!Tandem software found at %1 "
                    "(file not executable).")
          .arg(tandem_exe.absoluteFilePath()));
    }


  QString version_return;
  QStringList arguments;

  arguments << "-v";

  QProcess *xt_process = new QProcess();
  // hk_process->setWorkingDirectory(QFileInfo(_hardklor_exe).absolutePath());

  xt_process->start(tandem_bin_path, arguments);

  if(!xt_process->waitForStarted())
    {
      throw pappso::PappsoException(
        QObject::tr("X!Tandem process failed to start"));
    }

  while(xt_process->waitForReadyRead(1000))
    {
    }
  /*
  if (!xt_process->waitForFinished(_max_xt_time_ms)) {
      throw pappso::PappsoException(QObject::tr("can't wait for X!Tandem process
  to finish : timeout at %1").arg(_max_xt_time_ms));
  }
  */
  QByteArray result = xt_process->readAll();


  qDebug() << result.constData();

  // X! TANDEM Jackhammer TPP (2013.06.15.1 - LabKey, Insilicos, ISB)

  QRegExp parse_version("(.*) TANDEM ([A-Z,a-z, ]+) \\(([^ ,^\\)]*)(.*)");
  qDebug() << parse_version;
  // Pattern patt = Pattern.compile("X! TANDEM [A-Z]+ \\((.*)\\)",
  //			Pattern.CASE_INSENSITIVE);

  if(parse_version.exactMatch(result.constData()))
    {
      version_return = QString("X!Tandem %1 %2")
                         .arg(parse_version.capturedTexts()[2])
                         .arg(parse_version.capturedTexts()[3]); //.join(" ");
    }
  else
    {
      throw pappso::PappsoException(
        QObject::tr("This executable %1 may not be a valid X!Tandem software. "
                    "Please check your X!Tandem installation.")
          .arg(tandem_bin_path));
    }

  QProcess::ExitStatus Status = xt_process->exitStatus();
  delete xt_process;
  if(Status != 0)
    {
      // != QProcess::NormalExit
      throw pappso::PappsoException(
        QObject::tr("error executing X!Tandem Status != 0 : %1 %2\n%3")
          .arg(tandem_bin_path)
          .arg(arguments.join(" ").arg(result.data())));
    }
  qDebug() << __FILE__ << " " << __FUNCTION__ << " " << __LINE__;
  return version_return;
}

const QString
Utils::checkMassChroQVersion(const QString &masschroq_bin_path)
{
  // check masschroq path
  QFileInfo masschroq_exe(masschroq_bin_path);
  if(!masschroq_exe.exists())
    {
      // dir.path() returns the unique directory path
      throw pappso::PappsoException(
        QObject::tr(
          "MassChroQ software not found at \"%1\".\nPlease check the MassChroQ "
          "installation on your computer and set masschroq.exe path.")
          .arg(masschroq_exe.absoluteFilePath()));
    }
  if(!masschroq_exe.isReadable())
    {
      // dir.path() returns the unique directory path
      throw pappso::PappsoException(
        QObject::tr(
          "Please check permissions on MassChroQ software found at %1 "
          "(file not readable).")
          .arg(masschroq_exe.absoluteFilePath()));
    }
  if(!masschroq_exe.isExecutable())
    {
      // dir.path() returns the unique directory path
      throw pappso::PappsoException(
        QObject::tr(
          "Please check permissions on MassChroQ software found at %1 "
          "(file not executable).")
          .arg(masschroq_exe.absoluteFilePath()));
    }

  QString version_return;
  QStringList arguments;

  arguments << "-v";

  QProcess *mcq_process = new QProcess();

  mcq_process->start(masschroq_bin_path, arguments);

  if(!mcq_process->waitForStarted())
    {
      throw pappso::PappsoException(
        QObject::tr("MassChroQ process failed to start"));
    }

  while(mcq_process->waitForReadyRead(1000))
    {
    }
  QByteArray result = mcq_process->readAll();


  qDebug() << result.constData();
  //  /home/trenne/developpement/git/masschroq/build/src/masschroq
  QRegExp parse_version("(MassChroQ [0-9]{1,2}\\.[0-9]{1,2}\\.[0-9]{1,3})");
  qDebug() << parse_version;

  if(parse_version.exactMatch(QString(result.constData()).simplified()))
    {
      version_return = result.constData();
    }
  else
    {
      throw pappso::PappsoException(
        QObject::tr("This executable %1 may not be a valid MassChroQ software. "
                    "Please check your MassChroQ installation.")
          .arg(masschroq_bin_path));
    }

  QProcess::ExitStatus Status = mcq_process->exitStatus();
  delete mcq_process;
  if(Status != 0)
    {
      // != QProcess::NormalExit
      mcq_process->kill();
      delete mcq_process;
      throw pappso::PappsoException(
        QObject::tr("error executing MassChroQ Status != 0 : %1 %2\n%3")
          .arg(masschroq_bin_path)
          .arg(arguments.join(" ").arg(result.data())));
    }
  qDebug();
  return version_return;
}


double
Utils::computeFdr(std::size_t count_decoy, std::size_t count_target)
{
  return ((double)count_decoy / (double)count_target);
}
