/////////////////////// StdLib includes
#include <iostream>
#include <iomanip>


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


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


/////////////////////// Local includes
#include "pwizmsfilereader.h"
#include "pappsomspp/core/exception/exceptionnotfound.h"
#include "pappsomspp/core/utils.h"
#include "pappsomspp/core/types.h"
#include "pappsomspp/core/msrun/msrunid.h"

namespace pappso
{


PwizMsFileReader::PwizMsFileReader(const QString &file_name)
  : MsFileReader{file_name}
{
}

PwizMsFileReader::~PwizMsFileReader()
{
}

std::size_t
PwizMsFileReader::initialize()
{
  pwiz::msdata::DefaultReaderList defaultReaderList;
  std::string readerName;
  try
    {
      // qDebug() << m_fileName;
      readerName = defaultReaderList.identify(m_fileName.toStdString());
    }
  catch(std::runtime_error &error)
    {
      qDebug() << error.what() << " " << typeid(error).name();

      throw PappsoException(
        QObject::tr(
          "libpwiz ERROR reading MS data file %1 "
          "(std::runtime_error):\n%2\nsource file:%3 - source line:%4")
          .arg(m_fileName)
          .arg(error.what())
          .arg(__FILE__)
          .arg(__LINE__));
    }
  catch(std::exception &error)
    {
      qDebug() << error.what() << " " << typeid(error).name();

      throw PappsoException(
        QObject::tr(
          "libpwiz ERROR reading MS data file %1 "
          "(std::runtime_error):\n%2\nsource file:%3 - source line:%4")
          .arg(m_fileName)
          .arg(error.what())
          .arg(__FILE__)
          .arg(__LINE__));
    }

  if(readerName.empty())
    {
      // qDebug() << "Failed to identify the file.";

      return 0;
    }

  // Now convert the string to Enums::MsDataFormat.
  if(readerName == "mzML")
    m_fileFormat = Enums::MsDataFormat::mzML;
  else if(readerName == "mzXML")
    m_fileFormat = Enums::MsDataFormat::mzXML;
  else if(readerName == "Mascot Generic")
    m_fileFormat = Enums::MsDataFormat::MGF;
  else if(readerName == "MZ5")
    m_fileFormat = Enums::MsDataFormat::mz5;
  else if(readerName == "MSn")
    m_fileFormat = Enums::MsDataFormat::msn;
  else if(readerName == "ABSciex WIFF")
    m_fileFormat = Enums::MsDataFormat::abSciexWiff;
  else if(readerName == "ABSciex T2D")
    m_fileFormat = Enums::MsDataFormat::abSciexT2D;
  else if(readerName == "Agilent MassHunter")
    m_fileFormat = Enums::MsDataFormat::agilentMassHunter;
  else if(readerName == "Thermo RAW")
    m_fileFormat = Enums::MsDataFormat::thermoRaw;
  else if(readerName == "Water RAW")
    m_fileFormat = Enums::MsDataFormat::watersRaw;
  else if(readerName == "Bruker FID")
    m_fileFormat = Enums::MsDataFormat::brukerFid;
  else if(readerName == "Bruker YEP")
    m_fileFormat = Enums::MsDataFormat::brukerYep;
  else if(readerName == "Bruker BAF")
    m_fileFormat = Enums::MsDataFormat::brukerBaf;
  else
    {
      m_fileFormat = Enums::MsDataFormat::unknown;
      return 0;
    }

  // std::cout << __FILE__ << " @ " << __LINE__ << " " << __FUNCTION__ << " () "
  //         << std::setprecision(15) << "m_fileFormat: " << (int)m_fileFormat
  //        << std::endl;

  // At this point we know pwiz could be able to read the file. Actually fill-in
  // the MSDataPtr vector!
  try
    {
      defaultReaderList.read(Utils::toUtf8StandardString(m_fileName),
                             m_msDataPtrVector);
    }
  catch(std::runtime_error &error)
    {
      qDebug() << error.what() << " " << typeid(error).name();

      throw PappsoException(
        QObject::tr(
          "libpwiz ERROR reading MS data file %1 "
          "(std::runtime_error):\n%2\nsource file:%3 - source line:%4")
          .arg(m_fileName)
          .arg(error.what())
          .arg(__FILE__)
          .arg(__LINE__));
    }
  catch(std::exception &error)
    {
      qDebug() << error.what() << " " << typeid(error).name();

      throw PappsoException(
        QObject::tr(
          "libpwiz ERROR reading MS data file %1 "
          "(std::runtime_error):\n%2\nsource file:%3 - source line:%4")
          .arg(m_fileName)
          .arg(error.what())
          .arg(__FILE__)
          .arg(__LINE__));
    }

  // qDebug() << "The number of runs is:" << m_msDataPtrVector.size()
  //<< "The reader type is:" << QString::fromStdString(readerName)
  //<< "The number of spectra in first run is:"
  //<< m_msDataPtrVector.at(0)->run.spectrumListPtr->size();

  return m_msDataPtrVector.size();
}

Enums::MsDataFormat
PwizMsFileReader::getFileFormat()
{
  // std::cout << __FILE__ << " @ " << __LINE__ << " " << __FUNCTION__ << " () "
  //        << std::setprecision(15) << "m_fileFormat: " << (int)m_fileFormat
  //      << std::endl;

  return m_fileFormat;
}

std::vector<MsRunIdCstSPtr>
PwizMsFileReader::getMsRunIds(const QString &run_prefix)
{
  std::vector<MsRunIdCstSPtr> ms_run_ids;

  if(!initialize())
    return ms_run_ids;

  std::size_t iter = 0;

  for(const pwiz::msdata::MSDataPtr &ms_data_ptr : m_msDataPtrVector)
    {
      MsRunId ms_run_id(m_fileName,
                        QString::fromStdString(ms_data_ptr->run.id));

      // Set the MS data format as determined in initialize().
      ms_run_id.setMsDataFormat(m_fileFormat);

      // We need to set the unambiguous xmlId string.
      ms_run_id.setXmlId(QString("%1%2")
                           .arg(run_prefix)
                           .arg(Utils::getLexicalOrderedString(iter)));

      // Now set the sample name to the run id : we can not do that, this is not
      // the use sample name
      // ms_run_id.setSampleName(QString::fromStdString(ms_data_ptr->run.id));

      // And if it is possible, the real sample name because this one is for the
      // end user to recognize his sample:
      if(ms_data_ptr->run.samplePtr != nullptr)
        {
          ms_run_id.setSampleName(
            QString::fromStdString(ms_data_ptr->run.samplePtr->name));
        }

      // qDebug() << __FILE__ << "@" << __LINE__ << __FUNCTION__ << "()"
      //<< "Current ms_run_id:" << ms_run_id.toString();

      // Finally make a shared pointer out of it and append it to the vector.
      ms_run_ids.push_back(std::make_shared<MsRunId>(ms_run_id));

      ++iter;
    }

  return ms_run_ids;
}


} // namespace pappso
