/**
 * \file mcql/masschroqlightcborcli.cpp
 * \date 03/01/2025
 * \author Olivier Langella
 * \brief simple MassChroQ CBOR reader
 */

/*******************************************************************************
 * Copyright (c) 2025 Olivier Langella
 *<Olivier.Langella@universite-paris-saclay.fr>.
 *
 * This file is part of MassChroQ.
 *
 *     MassChroQ 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.
 *
 *     MassChroQ 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 MassChroQ.  If not, see <http://www.gnu.org/licenses/>.
 *
 ******************************************************************************/

#include "masschroqlightcborcli.h"
#include <QDateTime>
#include <QFile>
#include <QCommandLineParser>
#include <pappsomspp/core/pappsoexception.h>
#include <pappsomspp/core/processing/uimonitor/uimonitortextpercent.h>
#include "export/cborstreamreaderdebug.h"
#include "export/cborstreamreaderalignment.h"
#include "export/cborstreamreadercompartsv.h"
#include "export/cborstreamreadertsv.h"
#include "export/cborstreamreadershape.h"
#include "export/cborstreamreader2json.h"
#include "export/cborstreamreaderdebugquantificationdata.h"
#include <QJsonDocument>
#include <pappsomspp/core/pappsoexception.h>
#include <pappsomspp/core/masschroq/utils.h>
#include <qobject.h>

MassChroqLightCborCli::MassChroqLightCborCli(QObject *parent) : QObject(parent)
{
  // get the instance of the main application
  m_mcqLightCborApp = QCoreApplication::instance();
  // setup everything here
  // create any global objects
  // setup debug and warning mode
}

void
MassChroqLightCborCli::aboutToQuitApp()
{
}
void
MassChroqLightCborCli::quit()
{
  // you can do some cleanup here
  // then do emit finished to signal CoreApplication to quit
  emit finished();
}

void
MassChroqLightCborCli::run()
{

  QTextStream errorStream(stderr, QIODevice::WriteOnly);
  try
    {
      const QDateTime dt_begin = QDateTime::currentDateTime();
      const QString masschroq_dir_path(QCoreApplication::applicationDirPath());

      QCommandLineParser parser;
      parser.setApplicationDescription(
        "Perform Mass Chromatogram Quantification as indicated in the XML "
        "input "
        "FILE. For additional information, see the MassChroQ Homepage : "
        "http://pappso.inra.fr/bioinfo/masschroq/");
      parser.addHelpOption();
      parser.addVersionOption();

      parser.addPositionalArgument("[source]", "CBOR file to read from");

      QCommandLineOption tsvdOutput(QStringList() << "tsvd",
                                    tr("TSV output directory"),
                                    tr("directory path"));
      parser.addOption(tsvdOutput);


      QCommandLineOption alignmentdOutput(
        QStringList() << "alignmentd",
        tr("TSV output directory containing alignment directory"),
        tr("alignement directory path"));
      parser.addOption(alignmentdOutput);


      QCommandLineOption shapedOutput(QStringList() << "shaped",
                                      tr("TSV output directory"),
                                      tr("directory path"));
      parser.addOption(shapedOutput);

      QCommandLineOption noMissedOutput(
        QStringList() << "nomissed",
        tr("do not report missed peaks (to mimic MassChroQ legacy tsv "
           "reports)"));
      parser.addOption(noMissedOutput);


      QCommandLineOption odsOutput(QStringList() << "ods",
                                   tr("Open Document Spreadsheet output"),
                                   tr("ODS file"));
      parser.addOption(odsOutput);


      QCommandLineOption odsComparOutput(
        QStringList() << "compar",
        tr("Open Document Spreadsheet compar output"),
        tr("ODS file"));
      parser.addOption(odsComparOutput);


      QCommandLineOption jsonOutput(QStringList() << "json",
                                    tr("produce json mcql parameter file"),
                                    tr("JSON output file"));
      parser.addOption(jsonOutput);


      QCommandLineOption debugInput(QStringList() << "debug",
                                    tr("debug CBOR parsing"));
      parser.addOption(debugInput);


      QCommandLineOption peptideId(
        QStringList() << "peptide",
        tr("get all quantification data in JSON concerning one peptide id"),
        tr("peptide identifier"));
      parser.addOption(peptideId);

      parser.process(*m_mcqLightCborApp);

      pappso::UiMonitorTextPercent ui_monitor(errorStream);
      ui_monitor.setTitle(QObject::tr("MassChroQ export %1")
                            .arg(pappso::masschroq::Utils::getVersion()));

      QStringList files = parser.positionalArguments();
      if(files.isEmpty())
        files << "-";

      QString tsv_directory = parser.value(tsvdOutput);

      QString shape_directory = parser.value(shapedOutput);

      QString ods_filename        = parser.value(odsOutput);
      QString ods_compar_filename = parser.value(odsComparOutput);

      bool no_missed_in_output = false;
      if(parser.isSet(noMissedOutput))
        {
          no_missed_in_output = true;
        }


      if(parser.isSet(debugInput))
        {

          debugCbor(ui_monitor, files);
        }
      else if(parser.isSet(alignmentdOutput))
        {
          QString alignement_dir = parser.value(alignmentdOutput);
          if(!alignement_dir.isEmpty())
            {
              alignmentOutputDirectory(ui_monitor, files, alignement_dir);
            }
        }
      else if(parser.isSet(peptideId))
        {
          QString json_file;
          QStringList peptide_id = parser.value(peptideId).split(" ");
          if(parser.isSet(jsonOutput))
            {
              json_file = parser.value(jsonOutput);
            }
          extractPeptideIdQuantificationData(
            ui_monitor, files, peptide_id, json_file);
        }
      else if(parser.isSet(jsonOutput))
        {
          QString json_file = parser.value(jsonOutput);
          produceJson(ui_monitor, files, json_file);
        }
      else if(!tsv_directory.isEmpty())
        {
          tsvOutputDirectory(
            ui_monitor, files, tsv_directory, no_missed_in_output);
        }
      else if(!shape_directory.isEmpty())
        {
          shapeOutputDirectory(ui_monitor, files, shape_directory);
        }
      else if(!ods_filename.isEmpty())
        {
          odsOutputDirectory(
            ui_monitor, files, ods_filename, no_missed_in_output);
        }
      else if(!ods_compar_filename.isEmpty())
        {
          odsComparOutputFile(ui_monitor, files, ods_compar_filename);
        }
    }
  catch(pappso::PappsoException &error)
    {
      errorStream << "MassChroQ encountered an error:" << Qt::endl;
      errorStream << error.qwhat() << Qt::endl;

      exit(1);
      m_mcqLightCborApp->exit(1);
    }
  qDebug() << "before final quit()";

  // you must call quit when complete or the program will stay in the
  // messaging loop
  quit();
}


void
MassChroqLightCborCli::debugCbor(pappso::UiMonitorInterface &monitor,
                                 const QStringList &files)
{
  mcql::CborStreamReaderDebug::DumpOptions opts;
  opts |= mcql::CborStreamReaderDebug::ShowCompact;
  for(const QString &file : std::as_const(files))
    {
      QFile f(file);
      monitor.setStatus(QString("reading file %1").arg(file));
      if(file == "-" ? f.open(stdin, QIODevice::ReadOnly)
                     : f.open(QIODevice::ReadOnly))
        {

          if(f.error() != QFileDevice::FileError::NoError)
            {
              throw pappso::PappsoException(
                QObject::tr("Unable to read file %1").arg(file));
            }
          mcql::CborStreamReaderDebug dumper(&f, opts);
          // QCborError err = dumper.dump();
          // if (err)
          //   return EXIT_FAILURE;
        }
    }

  // if (err)
  //  return EXIT_FAILURE;
}

void
MassChroqLightCborCli::shapeOutputDirectory(pappso::UiMonitorInterface &monitor,
                                            const QStringList &files,
                                            const QString &directory)
{
  CborStreamReaderShape shape_out(directory);
  for(const QString &file : std::as_const(files))
    {
      QFile f(file);
      monitor.setStatus(QString("reading file %1").arg(file));
      if(file == "-" ? f.open(stdin, QIODevice::ReadOnly)
                     : f.open(QIODevice::ReadOnly))
        {

          if(f.error() != QFileDevice::FileError::NoError)
            {
              throw pappso::PappsoException(
                QObject::tr("Unable to read file %1").arg(file));
            }
          shape_out.readCbor(&f, monitor);
          // if (err)
          //   return EXIT_FAILURE;
        }
    }
}

void
MassChroqLightCborCli::alignmentOutputDirectory(
  pappso::UiMonitorInterface &monitor,
  const QStringList &files,
  const QString &directory)
{
  CborStreamReaderAlignment alignment_out(directory);
  for(const QString &file : std::as_const(files))
    {
      QFile f(file);
      monitor.setStatus(QString("reading file %1").arg(file));
      if(file == "-" ? f.open(stdin, QIODevice::ReadOnly)
                     : f.open(QIODevice::ReadOnly))
        {

          if(f.error() != QFileDevice::FileError::NoError)
            {
              throw pappso::PappsoException(
                QObject::tr("Unable to read file %1").arg(file));
            }
          alignment_out.readCbor(&f, monitor);
          // if (err)
          //   return EXIT_FAILURE;
        }
      break;
    }
}


void
MassChroqLightCborCli::tsvOutputDirectory(pappso::UiMonitorInterface &monitor,
                                          const QStringList &files,
                                          const QString &directory,
                                          bool no_missed_in_output)
{
  qDebug();
  // check permissions
  QFileInfo my_dir(directory);
  if(QFileInfo(my_dir.absolutePath()).isDir() &&
     QFileInfo(my_dir.absolutePath()).isWritable())
    {
      mcql::CborStreamReaderTsv tsv_out(directory);
      qDebug();
      tsv_out.setNoMissedPeak(no_missed_in_output);
      qDebug();
      for(const QString &file : std::as_const(files))
        {
          qDebug() << file;
          QFile f(file);
          monitor.setStatus(QString("reading file %1").arg(file));
          if(file == "-" ? f.open(stdin, QIODevice::ReadOnly)
                         : f.open(QIODevice::ReadOnly))
            {
              if(f.error() != QFileDevice::FileError::NoError)
                {
                  throw pappso::PappsoException(
                    QObject::tr("Unable to read file %1").arg(file));
                }
              qDebug();
              tsv_out.readCbor(&f, monitor);
              // if (err)
              //   return EXIT_FAILURE;
            }
          else
            {
              qDebug();
            }
        }
    }
  else
    {
      throw pappso::PappsoException(
        QObject::tr("destination directory \"%1\" is not writable")
          .arg(my_dir.absolutePath()));
    }
}

void
MassChroqLightCborCli::odsOutputDirectory(pappso::UiMonitorInterface &monitor,
                                          const QStringList &files,
                                          const QString &filename,
                                          bool no_missed_in_output)
{

  QFileInfo my_dir(filename);
  if(QFileInfo(my_dir.absolutePath()).isDir() &&
     QFileInfo(my_dir.absolutePath()).isWritable())
    {
      mcql::CborStreamReaderOds ods_out(filename);
      ods_out.setNoMissedPeak(no_missed_in_output);
      for(const QString &file : std::as_const(files))
        {
          QFile f(file);
          monitor.setStatus(QString("reading file %1").arg(file));
          if(file == "-" ? f.open(stdin, QIODevice::ReadOnly)
                         : f.open(QIODevice::ReadOnly))
            {
              if(files.size() > 1)
                printf("/ From \"%s\" /\n", qPrintable(file));

              if(f.error() != QFileDevice::FileError::NoError)
                {
                  throw pappso::PappsoException(
                    QObject::tr("Unable to read file %1").arg(file));
                }
              ods_out.readCbor(&f, monitor);
              // if (err)
              //   return EXIT_FAILURE;
            }
        }
      ods_out.close();
    }
  else
    {
      throw pappso::PappsoException(
        QObject::tr("destination directory \"%1\" is not writable")
          .arg(my_dir.absolutePath()));
    }
}

void
MassChroqLightCborCli::odsComparOutputFile(pappso::UiMonitorInterface &monitor,
                                           const QStringList &files,
                                           const QString &filename)
{

  CborStreamReaderComparOds ods_out(filename);
  for(const QString &file : std::as_const(files))
    {
      QFile f(file);
      monitor.setStatus(QString("reading file %1").arg(file));
      if(file == "-" ? f.open(stdin, QIODevice::ReadOnly)
                     : f.open(QIODevice::ReadOnly))
        {
          if(f.error() != QFileDevice::FileError::NoError)
            {
              throw pappso::PappsoException(
                QObject::tr("Unable to read file %1").arg(file));
            }
          ods_out.readCbor(&f, monitor);
          // if (err)
          //   return EXIT_FAILURE;
        }
    }
}

void
MassChroqLightCborCli::produceJson(pappso::UiMonitorInterface &monitor,
                                   const QStringList &files,
                                   const QString &filename)
{
  for(const QString &file : std::as_const(files))
    {
      qDebug() << "open cbor file " << file;
      QFile f(file);
      monitor.setStatus(QString("reading file %1").arg(file));
      if(file == "-" ? f.open(stdin, QIODevice::ReadOnly)
                     : f.open(QIODevice::ReadOnly))
        {
          if(files.size() > 1)
            printf("/ From \"%s\" /\n", qPrintable(file));

          mcql::CborStreamReader2Json cbor2json;
          cbor2json.readCbor(&f, monitor);
          f.close();

          qDebug() << "open json file " << filename;
          QFile jsonf(filename);
          if(filename == "" ? jsonf.open(stdout, QIODevice::WriteOnly)
                            : jsonf.open(QIODevice::WriteOnly))
            {
            }
          if(jsonf.error() != QFileDevice::FileError::NoError)
            {
              throw pappso::PappsoException(
                QObject::tr("Unable to write JSON output file %1")
                  .arg(filename));
            }
          QJsonDocument doc;
          doc.setObject(cbor2json.getJsonObject());
          jsonf.write(doc.toJson());
          jsonf.close();
        }
      break;
    }
}

void
MassChroqLightCborCli::extractPeptideIdQuantificationData(
  pappso::UiMonitorInterface &monitor,
  const QStringList &inputfiles,
  const QStringList &peptide_id_list,
  const QString &json_output_file)
{
  for(const QString &file : std::as_const(inputfiles))
    {
      qDebug() << "open cbor file " << file;
      QFile f(file);
      monitor.setStatus(QString("reading file %1").arg(file));
      if(file == "-" ? f.open(stdin, QIODevice::ReadOnly)
                     : f.open(QIODevice::ReadOnly))
        {

          mcql::CborStreamReaderDebugQuantificationData
            peptideQuantification2json;
          peptideQuantification2json.setFindPeptideIdList(peptide_id_list);
          peptideQuantification2json.readCbor(&f, monitor);
          f.close();

          QFile jsonf(json_output_file);
          if(json_output_file == "" ? jsonf.open(stdout, QIODevice::WriteOnly)
                                    : jsonf.open(QIODevice::WriteOnly))
            {
            }
          if(jsonf.error() != QFileDevice::FileError::NoError)
            {
              throw pappso::PappsoException(
                QObject::tr("Unable to write JSON output file %1")
                  .arg(json_output_file));
            }
          QJsonDocument doc;
          doc.setObject(peptideQuantification2json.getJsonObject());
          jsonf.write(doc.toJson());
          jsonf.close();
        }
      break;
    }
}
