/**
 * \file mzml2mzcborcli.cpp
 * \date 21/11/2025
 * \author Olivier Langella
 * \brief CLI tool to convert mzML to mzCBOR
 */


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


#include "mzml2mzcborcli.h"
#include "pappsomspp/config.h"
#include <QIODevice>
#include <QCommandLineParser>
#include "pappsomspp/core/processing/uimonitor/uimonitortext.h"
#include "pappsomspp/core/processing/uimonitor/uimonitorvoid.h"
#include "pappsomspp/core/processing/cbor/mzcbor/mzmlconvert.h"
#include "pappsomspp/core/pappsoexception.h"
#include <QDateTime>
#include <QDir>
#include <QFile>
#include <QFileInfo>
#include <QTimer>
#include <sys/stat.h>

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

Mzml2MzcborCli::~Mzml2MzcborCli()
{
}

// 10ms after the application starts this method will run
// all QT messaging is running at this point so threads, signals and slots
// will all work as expected.
void
Mzml2MzcborCli::run()
{
  qSetMessagePattern(QString("%{file}@%{line}, %{function}(): %{message}"));

  QTextStream errorStream(stderr, QIODevice::WriteOnly);
  QTextStream outputStream(stderr, QIODevice::WriteOnly);

  try
    {
      qDebug();
      QCommandLineParser parser;

      // throw pappso::PappsoException("test");
      // throw pappso::PappsoException("test");
      parser.setApplicationDescription(QString("mzml2mzcbor")
                                         .append(" ")
                                         .append(PAPPSOMSPP_VERSION)
                                         .append(" mzML to mzCBOR converter"));

      parser.addHelpOption();
      parser.addVersionOption();

      QCommandLineOption mzmlInputOption(
        QStringList() << "i" << "input",
        QCoreApplication::translate(
          "mzML file or directory",
          "PSI mzML file <mzML> or directory path containing <dir> "
          "containing mzML files or use '-' to read data from stdin."),
        QCoreApplication::translate("mzML", "mzML"));

      QCommandLineOption mzcborOutputOption(
        QStringList() << "o" << "output",
        QCoreApplication::translate(
          "mzCBOR file or directory",
          "path to the mzCBOR output file <mzcbor> or output directory."),
        QCoreApplication::translate("mzcbor", "mzcbor"));


      QCommandLineOption writeIndex(QStringList() << "index",
                                    tr("write mzcbor index file"));
      parser.addOption(writeIndex);

      parser.addOption(mzmlInputOption);
      parser.addOption(mzcborOutputOption);

      qDebug();

      // Process the actual command line arguments given by the user
      parser.process(*app);

      // QCoreApplication * app(this);
      // Add your main code here
      // qDebug();
      const QDateTime dt_begin = QDateTime::currentDateTime();
      const QStringList args   = parser.positionalArguments();


      qDebug();
      pappso::UiMonitorText monitor(outputStream);


      // PsmProcess cbor_psm_process(json_parameters);
      bool is_dir = false;
      QFile input_mzml_file;
      if(!parser.isSet(mzmlInputOption))
        {
          if(parser.positionalArguments().contains("-"))
            {
              input_mzml_file.open(stdin, QIODevice::ReadOnly);
            }
          else
            {
              throw pappso::PappsoException(
                QObject::tr("input file is not set. Please provide --input or "
                            "use - to read from <stdin>"));
            }
        }
      else
        {
          QFileInfo input_mzml_fileinfo(parser.value(mzmlInputOption));
          if(input_mzml_fileinfo.isFile())
            {
              qDebug();
              input_mzml_file.setFileName(
                input_mzml_fileinfo.absoluteFilePath());
              if(!input_mzml_file.open(QIODevice::ReadOnly))
                {
                  throw pappso::PappsoException(
                    QObject::tr("Unable to open mzML file '%1' :\n%2\n")
                      .arg(QFileInfo(input_mzml_file).absoluteFilePath())
                      .arg(input_mzml_file.errorString()));
                }
            }
          else if(input_mzml_fileinfo.isDir())
            {
              qDebug();
              is_dir = true;
              convertAllMzmlInDir(monitor,
                                  input_mzml_fileinfo,
                                  parser.value(mzcborOutputOption),
                                  parser.isSet(writeIndex));
            }
        }

      if(is_dir)
        {
        }
      else
        {
          QFile mzcbor_file;
          pappso::cbor::CborStreamWriter *p_cborWriter = nullptr;
          QString mzcbor_index_filename;
          if(parser.isSet(mzcborOutputOption))
            {
              QString mzcbor_filename = parser.value(mzcborOutputOption);
              if(!mzcbor_filename.isEmpty())
                {
                  mzcbor_file.setFileName(mzcbor_filename);
                  mzcbor_file.open(QIODevice::WriteOnly);
                  p_cborWriter =
                    new pappso::cbor::CborStreamWriter(&mzcbor_file);
                  mzcbor_index_filename = mzcbor_filename.append(".idx");
                }
            }
          else
            {
              mzcbor_file.open(stdout, QIODevice::WriteOnly);
              p_cborWriter = new pappso::cbor::CborStreamWriter(&mzcbor_file);
            }


          if(p_cborWriter != nullptr)
            {
              pappso::cbor::mzcbor::MzmlConvert mzml_convert(&monitor,
                                                             p_cborWriter);

              mzml_convert.read(&input_mzml_file);
              input_mzml_file.close();
              mzcbor_file.close();
              if(!mzml_convert.errorString().isEmpty())
                {
                  throw pappso::PappsoException(
                    QObject::tr("ERROR converting mzML to mzCBOR :\n%1")
                      .arg(mzml_convert.errorString()));
                }

              if(!mzcbor_index_filename.isEmpty() && parser.isSet(writeIndex))
                {

                  QFile mzcbor_index_file;
                  mzcbor_index_file.setFileName(mzcbor_index_filename);
                  mzcbor_index_file.open(QIODevice::WriteOnly);
                  pappso::cbor::CborStreamWriter *index_writer =
                    new pappso::cbor::CborStreamWriter(&mzcbor_index_file);
                  mzml_convert.writeMzcborIndex(index_writer);
                  mzcbor_index_file.close();
                  delete index_writer;
                }
              delete p_cborWriter;
            }
        }

      qDebug();
    }
  catch(pappso::PappsoException &error)
    {

      errorStream << QString("Oops! an error occurred in %1. Don't Panic :\n%2")
                       .arg(QCoreApplication::applicationName())
                       .arg(error.qwhat());

      errorStream << Qt::endl << Qt::endl;

      errorStream.flush();
      app->exit(1);
      exit(1);
    }

  catch(std::exception &error)
    {

      errorStream << QString("Oops! an error occurred in %1. Don't Panic :\n%2")
                       .arg(QCoreApplication::applicationName())
                       .arg(error.what());

      errorStream << Qt::endl << Qt::endl;

      errorStream.flush();
      app->exit(1);
      exit(1);
    }


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

// call this routine to quit the application
void
Mzml2MzcborCli::quit()
{
  // you can do some cleanup here
  // then do emit finished to signal CoreApplication to quit
  emit finished();
}

// shortly after quit is called the CoreApplication will signal this routine
// this is a good place to delete any objects that were created in the
// constructor and/or to stop any threads
void
Mzml2MzcborCli::aboutToQuitApp()
{
  // stop threads
  // sleep(1);   // wait for threads to stop.
  // delete any objects
}

void
Mzml2MzcborCli::convertAllMzmlInDir(pappso::UiMonitorInterface &monitor,
                                    const QFileInfo &input_dir,
                                    const QString &output_str,
                                    bool write_index)
{
  qDebug();
  QStringList mzml_file_list;
  QDir dir(input_dir.absoluteDir());
  dir.setFilter(QDir::Files);
  foreach(QFileInfo fileInfo, dir.entryInfoList(QDir::Files))
    {

      qDebug() << fileInfo.suffix().toLower();
      if(fileInfo.isFile() && (fileInfo.suffix().toLower() == "mzml"))
        {
          mzml_file_list << fileInfo.absoluteFilePath();
        }
    }
  QDir output_dir(dir);
  if(!output_str.isEmpty())
    {
      output_dir.setPath(QFileInfo(output_str).absoluteDir().absolutePath());
    }
  if(!output_dir.exists())
    {

      throw pappso::PappsoException(
        QObject::tr("ERROR the output directory does not exists :\n%1")
          .arg(output_dir.absolutePath()));
    }

  for(QString &input_file_name : mzml_file_list)
    {
      QString output_file_name = output_dir.absoluteFilePath(
        QFileInfo(input_file_name).baseName().append(".mzcbor"));
      qDebug() << output_file_name;
      qDebug() << input_file_name;

      monitor.setStatus(QObject::tr("Converting %1 to %2")
                          .arg(input_file_name)
                          .arg(output_file_name));
      QFile input_file(input_file_name);
      if(input_file.open(QIODevice::ReadOnly))
        {

          QFile mzcbor_file;
          mzcbor_file.setFileName(output_file_name);
          if(mzcbor_file.open(QIODevice::WriteOnly))
            {
              pappso::cbor::CborStreamWriter *p_cborWriter =
                new pappso::cbor::CborStreamWriter(&mzcbor_file);

              pappso::UiMonitorVoid void_monitor;

              pappso::cbor::mzcbor::MzmlConvert mzml_convert(&void_monitor,
                                                             p_cborWriter);

              mzml_convert.read(&input_file);
              mzcbor_file.flush();
              input_file.close();
              mzcbor_file.close();
              if(!mzml_convert.errorString().isEmpty())
                {
                  throw pappso::PappsoException(
                    QObject::tr(
                      "ERROR converting mzML file %1 to mzCBOR file %2 :\n%3")
                      .arg(input_file_name)
                      .arg(output_file_name)
                      .arg(mzml_convert.errorString()));
                }

              delete p_cborWriter;
              if(write_index)
                {
                  QString mzcbor_index_filename =
                    output_file_name.append(".idx");
                  QFile mzcbor_index_file;
                  mzcbor_index_file.setFileName(mzcbor_index_filename);
                  mzcbor_index_file.open(QIODevice::WriteOnly);
                  pappso::cbor::CborStreamWriter *index_writer =
                    new pappso::cbor::CborStreamWriter(&mzcbor_index_file);
                  mzml_convert.writeMzcborIndex(index_writer);
                  mzcbor_index_file.flush();
                  mzcbor_index_file.close();
                  delete index_writer;
                }
            }
        }
    }
}

int
main(int argc, char **argv)
{
  umask(0);
  // QTextStream consoleErr(stderr);
  // QTextStream consoleOut(stdout, QIODevice::WriteOnly);
  // ConsoleOut::setCout(new QTextStream(stdout, QIODevice::WriteOnly));
  // ConsoleOut::setCerr(new QTextStream(stderr, QIODevice::WriteOnly));
  qDebug();
  QCoreApplication app(argc, argv);
  qDebug();
  QCoreApplication::setApplicationName("mzml2mzcbor");
  QCoreApplication::setApplicationVersion(PAPPSOMSPP_VERSION);
  QLocale::setDefault(QLocale::system());

  // create the main class
  Mzml2MzcborCli myMain;
  // connect up the signals
  QObject::connect(&myMain, SIGNAL(finished()), &app, SLOT(quit()));
  QObject::connect(
    &app, SIGNAL(aboutToQuit()), &myMain, SLOT(aboutToQuitApp()));
  qDebug();


  // This code will start the messaging engine in QT and in
  // 10ms it will start the execution in the MainClass.run routine;
  QTimer::singleShot(10, &myMain, SLOT(run()));
  return app.exec();
}
