/* 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 <QDebug>
#include <QFileInfo>


/////////////////////// Local includes
#include <minexpert/nongui/FileFormatConverter.hpp>
#include <minexpert/nongui/MassSpecDataFileFormatAnalyzer.hpp>
#include <minexpert/nongui/MassSpecDataFileLoaderPwiz.hpp>


namespace msXpSmineXpert
{

//! Construct a FileFormatConverter instance
/*!

  The parent QObject class is initialized using \p parent.

  \param Parent QObject pointer

*/
FileFormatConverter::FileFormatConverter(QObject *parent) : QObject{parent}
{
}


//! Destruct \c this FileFormatConverter instance
FileFormatConverter::~FileFormatConverter()
{
}


//! Set the input file name.
/*!

  \param fileName new input file name

*/
void
FileFormatConverter::setInFileName(const QString &fileName)
{
  m_inFileName = fileName;
}


//! Get the input file name.
/*!

  \return a string containing the input file name.
  */
QString
FileFormatConverter::inFileName() const
{
  return m_inFileName;
}


//! Set the output file name.
/*!

  \param fileName new output file name

*/
void
FileFormatConverter::setOutFileName(const QString &fileName)
{
  m_outFileName = fileName;
}


//! Get the output file name.
/*!

  \return a string containing the output file name.
  */
QString
FileFormatConverter::outFileName() const
{
  return m_outFileName;
}


//! Tries to craft a SQLite3 db output file name
/*!

  Depending on the availability of a fully characterized output file name, this
  function tries to create one or not. The crafting process is as follow:

  - if the input file name is empty, return without trying anything

  - if the output file name is empty, craft an output file name based on the
  input filename, like substituting the mzml extension to db or by adding .db
  to the input file name

  - if the output file name is not empty, then check if that is a directory
  name:

  - if it is a directory name, then the db file will be written into that
  directory. Use the input file name to craft the output file name (.mzml
  becomes .db or append .db, see above). The absolute path of the output
  filename is then created if necessary with the help of the input file name.

  - if it is a file name, then nothing special is to be done: if the output
  file name is provided with a full path, the location of the file is at
  that path, otherwise it is relative to the current working directory.

  Note that the output file name that is crafted here is stored in the \c
  m_outFileName member datum.

*/
void
FileFormatConverter::craftDbFileName()
{
  if(m_inFileName.isEmpty())
    return;

  if(m_outFileName.isEmpty())
    {
      // If the output is empty, craft a file name using the mzml file name as
      // template.

      m_outFileName = m_inFileName;
      m_outFileName.replace(QRegExp("\\.mzml$", Qt::CaseInsensitive), ".db");

      if(m_outFileName == m_inFileName)
        {
          // This means that the replace did not work out.
          // We do not want to overwrite the original mzml file
          // and thus we craft a brand new file by appending .db.
          m_outFileName += ".db";
        }
    }
  else
    {

      // m_outFileName was not empty, it thus might be either a directory or a
      // file.

      QFileInfo outFileInfo(m_outFileName);

      if(outFileInfo.isDir())
        {

          // The m_outFileName is actually a directory. We need to write the
          // database file into it.  Craft a db file name using the mzml file
          // name as a template.

          QString tempDbFileName = m_inFileName;
          tempDbFileName.replace(QRegExp("\\.mzml$", Qt::CaseInsensitive),
                                 ".db");

          if(tempDbFileName == m_inFileName)
            {
              // This means that the replace did not work out.
              // We do not want to overwrite the original mzml file
              // and thus we craft a brand new file by appending .db.
              tempDbFileName += ".db";
            }

          // If the mzml filename was an absolute path filename, we need to only
          // use its filename part, not the path. QFileInfo helps.

          QFileInfo fileInfo(tempDbFileName);

          m_outFileName =
            QString("%1/%2").arg(m_outFileName).arg(fileInfo.fileName());

          return;
        }
      else
        {
          // m_outFileName is the requested db file name. OK

          // qDebug() << __FILE__ << __LINE__
          //<< "Provided db file name:" << dbFileName;
        }
    }

  // Now that we know what is the database file name, check if it exists
  // already.

  QFile dbFile(m_outFileName);

  if(dbFile.exists())
    {
      qDebug() << __FILE__ << __LINE__
               << "File exists already: " << m_outFileName;

      qDebug() << __FILE__ << __LINE__
               << "Should that file be overwritten (y/n)?";

      QFile fileIn;
      fileIn.open(stdin, QIODevice::ReadOnly);
      QByteArray answer = fileIn.readLine();
      fileIn.close();

      if(answer[0] == 'n' || answer[0] == 'N')
        {
          qDebug() << __FILE__ << __LINE__ << "Operation aborted by the user.";
          exit(1);
        }
      else
        {
          if(!dbFile.remove())
            {
              qDebug() << __FILE__ << __LINE__
                       << "Failed to remove the database file. Exiting.\n";

              exit(1);
            }
        }
    }

  return;
}


//! Convert the mzML file into a SQLite3 db file
/*!

  Runs the actual conversion from the input file to the output file. The \c
  m_inFileName and \c m_outFileName members should contain usable values,
  otherwise false is returned.

  The format of the input file needs to be mzML otherwise false is returned.

  Because converting from mzML is like actually parsing mzML, the conversion
  is performed by the MassSpecDataFileLoaderPwiz class.

  \param errors pointer to a string in which to record all the error messages
  during conversion.

  \return true if conversion was successful; false otherwise.

*/
bool
FileFormatConverter::convert(QString *errors)
{
  if(m_inFileName.isEmpty())
    {
      errors->append("There was no file name in input.\n");
      return false;
    }

  QFileInfo fileInfo(m_inFileName);
  if(!fileInfo.isFile())
    {
      errors->append(QString("File %1 in input not found or is not a file.\n")
                       .arg(m_inFileName));
      return false;
    }

  // Now make the file name an absolute file path.
  m_inFileName = fileInfo.absoluteFilePath();

  MassSpecDataFileFormatAnalyzer analyzer(m_inFileName);

  if(analyzer.canLoadData() !=
     MassSpecDataFileFormat::MASS_SPEC_DATA_FILE_FORMAT_MZML)
    {
      errors->append(
        QString("File %1 in input could not be loaded (not an mzML file).\n")
          .arg(m_inFileName));

      return false;
    }

  // qDebug() << __FILE__ << __LINE__ << __FUNCTION__
  //<< "The loader says mzML file is loadable.";

  // At this point, we know we can load the data. Work on the output file.
  // The crafting function below set m_outFileName to the database file
  // name.

  craftDbFileName();

  if(m_outFileName.isEmpty())
    {
      qDebug() << __FILE__ << __LINE__
               << "Failed to craft a file name for the output.";

      return false;
    }

  qDebug()
    << __FILE__ << __LINE__
    << "Crafted new db filename on the basis of the mzml name and dir name:"
    << m_outFileName;

  QFile file(m_outFileName);

  if(file.exists())
    {
      errors->append(
        QString("The database file exists already: %1. Conversion skipped.\n")
          .arg(m_outFileName));

      return false;
    }

  MassSpecDataFileLoaderPwiz loader(m_inFileName);

  // The default feedback mechanism is based on QObject::emit(signal). But we
  // cannot rely on that mechanism here. So we have this peculiar provision:

  loader.m_feedbackToStdout = true;

  int result = loader.loadDataToDatabase(m_outFileName);

  if(result == -1)
    {
      qDebug() << __FILE__ << __LINE__ << "Conversion to" << m_outFileName
               << ": failure\n.";
    }

  qDebug() << __FILE__ << __LINE__ << "Conversion to" << m_outFileName
           << ": success\n.";

  return result;
}


} // namespace msXpSmineXpert
