
/////////////////////// StdLib includes


/////////////////////// Qt includes
#include <QDebug>
#include <QFileInfo>


/////////////////////// libpwiz includes
#include <pwiz/data/msdata/DefaultReaderList.hpp>


/////////////////////// Local includes
#include "xymsrunreader.h"
#include "pappsomspp/core/utils.h"
#include "pappsomspp/core/pappsoexception.h"
#include "pappsomspp/core/exception/exceptionnotfound.h"
#include "pappsomspp/core/exception/exceptionnotpossible.h"
#include "pappsomspp/core/exception/exceptionnotimplemented.h"


namespace pappso
{

XyMsRunReader::XyMsRunReader(MsRunIdCstSPtr &msrun_id_csp) : pappso::MsRunReader(msrun_id_csp)
{
  // Run the initialization function that checks that the file exists!

  initialize();
}


void
XyMsRunReader::initialize()
{
  // qDebug();

  if(!QFileInfo(mcsp_msRunId->getFileName()).exists())
    throw ExceptionNotFound(
      QObject::tr("Xy MS file %1 not found\n").arg(mcsp_msRunId->getFileName()));
  // qDebug();
}


XyMsRunReader::~XyMsRunReader()
{
}


bool
XyMsRunReader::accept(const QString &file_name) const
{

  // Here we just test all the lines of the file to check that they comply with
  // the xy format.

  std::size_t line_count = 0;

  QFile file(file_name);

  if(!file.open(QFile::ReadOnly | QFile::Text))
    {
      qDebug() << __FILE__ << __LINE__ << "Failed to open file" << file_name;

      return false;
    }

  QRegularExpressionMatch regExpMatch;

  QString line;
  bool file_reading_failed = false;

  while(!file.atEnd())
    {
      line = file.readLine();
      ++line_count;

      if(line.startsWith('#') || line.isEmpty() || Utils::endOfLineRegExp.match(line).hasMatch())
        continue;

      // qDebug() << __FILE__ << __LINE__ << "Current xy format line:" << line;

      if(Utils::xyMassDataFormatRegExp.match(line).hasMatch())
        continue;
      else
        {
          file_reading_failed = true;
          break;
        }
    }

  file.close();

  if(!file_reading_failed && line_count >= 1)
    return true;

  return false;
}


pappso::MassSpectrumSPtr
XyMsRunReader::massSpectrumSPtr(std::size_t spectrum_index)
{
  return qualifiedMassSpectrum(spectrum_index).getMassSpectrumSPtr();
}


pappso::MassSpectrumCstSPtr
XyMsRunReader::massSpectrumCstSPtr(std::size_t spectrum_index)
{
  return qualifiedMassSpectrum(spectrum_index).getMassSpectrumCstSPtr();
}


QualifiedMassSpectrum
XyMsRunReader::qualifiedMassSpectrumFromXyMSDataFile(MassSpectrumId mass_spectrum_id) const
{
  // qDebug();

  // This is a file that contains a single spectrum. Just iterate in the various
  // lines and convert them to DataPoint objects that are fed to the mass
  // spectrum.

  QualifiedMassSpectrum qualified_mass_spectrum(mass_spectrum_id);

  // Set manually data that are necessary for the correct use of this mass
  // spectrum in the MS data set tree node.
  qualified_mass_spectrum.setMsLevel(1);
  qualified_mass_spectrum.setRtInSeconds(0);

  MassSpectrum mass_spectrum;

  QFile file(mcsp_msRunId->getFileName());

  if(!file.exists())
    {
      // qDebug() << "File" << mcsp_msRunId->getFileName() << "does not exist.";

      return qualified_mass_spectrum;
    }

  if(!file.open(QFile::ReadOnly | QFile::Text))
    {
      // qDebug() << "Failed to open file" << mcsp_msRunId->getFileName();

      return qualified_mass_spectrum;
    }

  QRegularExpressionMatch regExpMatch;

  QString line;

  while(!file.atEnd())
    {
      line = file.readLine();

      if(line.startsWith('#') || line.isEmpty() || Utils::endOfLineRegExp.match(line).hasMatch())
        continue;

      if(Utils::xyMassDataFormatRegExp.match(line).hasMatch())
        {
          pappso_double x = -1;
          pappso_double y = -1;

          QRegularExpressionMatch regExpMatch = Utils::xyMassDataFormatRegExp.match(line);

          if(!regExpMatch.hasMatch())
            throw ExceptionNotPossible(
              QObject::tr("Failed to create data point with line %1.\n").arg(line));

          bool ok = false;

          x = regExpMatch.captured(1).toDouble(&ok);

          if(!ok)
            throw ExceptionNotPossible(
              QObject::tr("Failed to create data point with line %1.\n").arg(line));

          // Note that group 2 is the separator group.

          y = regExpMatch.captured(3).toDouble(&ok);

          if(!ok)
            throw ExceptionNotPossible(
              QObject::tr("Failed to create data point with line %1.\n").arg(line));

          // FIXME: what is this data point for if we use emplace_back
          // right below ?
          DataPoint data_point(x, y);

          mass_spectrum.emplace_back(x, y);
        }
    }

  file.close();

  MassSpectrumSPtr spectrum_sp = mass_spectrum.makeMassSpectrumSPtr();
  qualified_mass_spectrum.setMassSpectrumSPtr(spectrum_sp);

  // qDebug() << "the qualified mass spectrum has size:"
  //<< qualified_mass_spectrum.getMassSpectrumSPtr()->size();

  return qualified_mass_spectrum;
}


QualifiedMassSpectrum
XyMsRunReader::qualifiedMassSpectrum([[maybe_unused]] std::size_t spectrum_index,
                                     bool want_binary_data) const
{
  // qDebug();

  // In reality there is only one mass spectrum in the file, so we do not use
  // spectrum_index, but use 0 instead.

  MassSpectrumId massSpectrumId(mcsp_msRunId, 0);

  QualifiedMassSpectrum qualified_mass_spectrum =
    qualifiedMassSpectrumFromXyMSDataFile(massSpectrumId);

  // qDebug() << "qualified mass spectrum has size:"
  //<< qualified_mass_spectrum.getMassSpectrumSPtr()->size();

  // We also do not abide by the want_binary_data parameter because in this XY
  // mass spec data file loading process we actually want the data.
  if(!want_binary_data)
    {
      // qualified_mass_spectrum.setMassSpectrumSPtr(nullptr);
    }

  return qualified_mass_spectrum;
}


void
XyMsRunReader::readSpectrumCollection(SpectrumCollectionHandlerInterface &handler)
{
  // qDebug();

  // In reality there is only one mass spectrum in the file.

  MassSpectrumId massSpectrumId(mcsp_msRunId, 0 /* spectrum index*/);

  QualifiedMassSpectrum qualified_mass_spectrum =
    qualifiedMassSpectrumFromXyMSDataFile(massSpectrumId);

  // qDebug() << "qualified mass spectrum has size:"
  //<< qualified_mass_spectrum.getMassSpectrumSPtr()->size();

  // The handler will receive the index of the mass spectrum in the
  // current run via the mass spectrum id member datum.
  handler.setQualifiedMassSpectrum(qualified_mass_spectrum);

  // qDebug() << "Loading ended";
  handler.loadingEnded();
}


void
XyMsRunReader::readSpectrumCollectionByMsLevel(SpectrumCollectionHandlerInterface &handler,
                                               unsigned int ms_level)
{
  // qDebug();

  // In reality there is only one mass spectrum in the file.

  MassSpectrumId massSpectrumId(mcsp_msRunId, 0 /* spectrum index*/);

  QualifiedMassSpectrum qualified_mass_spectrum =
    qualifiedMassSpectrumFromXyMSDataFile(massSpectrumId);

  // qDebug() << "qualified mass spectrum has size:"
  //<< qualified_mass_spectrum.getMassSpectrumSPtr()->size();

  // The handler will receive the index of the mass spectrum in the
  // current run via the mass spectrum id member datum.

  if(qualified_mass_spectrum.getMsLevel() == ms_level)
    {
      handler.setQualifiedMassSpectrum(qualified_mass_spectrum);
    }

  // qDebug() << "Loading ended";
  handler.loadingEnded();
}

void
XyMsRunReader::readSpectrumCollection2([[maybe_unused]] const MsRunReadConfig &config,
                                       SpectrumCollectionHandlerInterface &handler)
{
  return readSpectrumCollection(handler);
}

std::size_t
XyMsRunReader::spectrumListSize() const
{
  return 1;
}

bool
XyMsRunReader::releaseDevice()
{
  return true;
}

bool
XyMsRunReader::acquireDevice()
{
  return true;
}

XicCoordSPtr
XyMsRunReader::newXicCoordSPtrFromSpectrumIndex(std::size_t spectrum_index [[maybe_unused]],
                                                pappso::PrecisionPtr precision
                                                [[maybe_unused]]) const
{
  throw ExceptionNotImplemented(
    QObject::tr("Not implemented %1 %2 %3").arg(__FILE__).arg(__FUNCTION__).arg(__LINE__));
}

XicCoordSPtr
XyMsRunReader::newXicCoordSPtrFromQualifiedMassSpectrum(
  const pappso::QualifiedMassSpectrum &mass_spectrum [[maybe_unused]],
  pappso::PrecisionPtr precision [[maybe_unused]]) const
{
  throw ExceptionNotImplemented(
    QObject::tr("Not implemented %1 %2 %3").arg(__FILE__).arg(__FUNCTION__).arg(__LINE__));
}


std::size_t
XyMsRunReader::spectrumStringIdentifier2SpectrumIndex(const QString &spectrum_identifier
                                                      [[maybe_unused]])
{
  throw pappso::ExceptionNotImplemented(
    QObject::tr("%1 %2 %3 not implemented").arg(__FILE__).arg(__FUNCTION__).arg(__LINE__));
}

} // namespace pappso
