//#include <proteowizard/pwiz/data/msdata/DefaultReaderList.hpp>

#include <QDebug>
#include <QFile>
#include <QFileInfo>


#include "msfileaccessor.h"
#include "pwizmsfilereader.h"
#include "timsmsfilereader.h"
#include "xymsfilereader.h"


#include "../exception/exceptionnotfound.h"
#include "../exception/exceptionnotpossible.h"
#include "../utils.h"
#include "../msrun/msrunid.h"
#include "../msrun/private/pwizmsrunreader.h"
#include "../msrun/private/timsmsrunreader.h"
#include "../msrun/private/timsmsrunreaderms2.h"
#include "../msrun/xymsrunreader.h"


namespace pappso
{


MsFileAccessor::MsFileAccessor(const QString &file_name,
                               const QString &xml_prefix)
  : m_fileName(file_name), m_xmlPrefix(xml_prefix)
{
  QFile file(file_name);
  if(!file.exists())
    throw(ExceptionNotFound(QObject::tr("File %1 not found.")
                              .arg(QFileInfo(file_name).absoluteFilePath())));
}


MsFileAccessor::MsFileAccessor(const MsFileAccessor &other)
  : m_fileName(other.m_fileName),
    m_xmlPrefix(other.m_xmlPrefix),
    m_fileFormat(other.m_fileFormat),
    m_fileReaderType(other.m_fileReaderType)
{
}

MsFileAccessor::~MsFileAccessor()
{
}


const QString &
MsFileAccessor::getFileName() const
{
  return m_fileName;
}


MzFormat
MsFileAccessor::getFileFormat() const
{
  return m_fileFormat;
}


std::vector<MsRunIdCstSPtr>
MsFileAccessor::getMsRunIds()
{
  // qDebug();

  // Try the PwizMsFileReader

  PwizMsFileReader pwiz_ms_file_reader(m_fileName);

  std::vector<MsRunIdCstSPtr> ms_run_ids =
    pwiz_ms_file_reader.getMsRunIds(m_xmlPrefix);
  if(ms_run_ids.size())
    {
      // qDebug() << "Might well be handled using the Pwiz code.";
      m_fileReaderType = FileReaderType::pwiz;

      m_fileFormat = pwiz_ms_file_reader.getFileFormat();

      return ms_run_ids;
    }

  // qDebug() << "The Pwiz reader did not work.";

  // Try the TimsData reader

  QString tims_dir = m_fileName;
  if(!QFileInfo(tims_dir).isDir())
    {
      tims_dir = QFileInfo(m_fileName).absolutePath();
    }

  TimsMsFileReader tims_file_reader(tims_dir);

  ms_run_ids = tims_file_reader.getMsRunIds(m_xmlPrefix);

  if(ms_run_ids.size())
    {
      // qDebug() << "Might well be handled using the Bruker code";
      m_fileReaderType = FileReaderType::tims;
      m_fileFormat     = tims_file_reader.getFileFormat();
      m_fileName       = tims_dir;


      auto pref = m_preferedFileReaderTypeMap.find(m_fileFormat);
      if(pref != m_preferedFileReaderTypeMap.end())
        {
          m_fileReaderType = pref->second;
        }

      return ms_run_ids;
    }

  // qDebug() << "The Tims reader did not work.";

  // At this point try the XyMsFileReader

  XyMsFileReader xy_ms_file_reader(m_fileName);

  ms_run_ids = xy_ms_file_reader.getMsRunIds(m_xmlPrefix);

  if(ms_run_ids.size())
    {
      // qDebug() << "Might well be handled using the XY code";
      m_fileReaderType = FileReaderType::xy;

      m_fileFormat = xy_ms_file_reader.getFileFormat();

      return ms_run_ids;
    }

  // qDebug() << "The XY reader did not work.";

  return ms_run_ids;
}


TimsMsRunReaderMs2SPtr
MsFileAccessor::buildTimsMsRunReaderMs2SPtr()
{

  // try TimsData reader
  QString tims_dir = m_fileName;
  if(!QFileInfo(tims_dir).isDir())
    {
      tims_dir = QFileInfo(m_fileName).absolutePath();
    }
  TimsMsFileReader tims_file_reader(tims_dir);

  std::vector<MsRunIdCstSPtr> ms_run_ids =
    tims_file_reader.getMsRunIds(m_xmlPrefix);

  if(ms_run_ids.size())
    {
      // qDebug() << "Might well be handled using the Bruker code";
      m_fileReaderType = FileReaderType::tims_ms2;
      m_fileFormat     = tims_file_reader.getFileFormat();
      m_fileName       = tims_dir;

      return std::make_shared<TimsMsRunReaderMs2>(ms_run_ids.front());
    }
  else
    {
      throw(ExceptionNotPossible(
        QObject::tr("Unable to read mz data directory %1 with TimsTOF reader.")
          .arg(tims_dir)));
    }
}

MsRunReaderSPtr
MsFileAccessor::msRunReaderSp(MsRunIdCstSPtr ms_run_id)
{
  if(m_fileName != ms_run_id->getFileName())
    throw(ExceptionNotPossible(
      QObject::tr("The MsRunId instance must have the name file name as the "
                  "MsFileAccessor.")));

  if(m_fileReaderType == FileReaderType::pwiz)
    {
      qDebug() << "Returning a PwizMsRunReader.";

      return std::make_shared<PwizMsRunReader>(ms_run_id);
    }
  else if(m_fileReaderType == FileReaderType::xy)
    {
      qDebug() << "Returning a XyMsRunReader.";

      return std::make_shared<XyMsRunReader>(ms_run_id);
    }

  else if(m_fileReaderType == FileReaderType::tims)
    {
      qDebug() << "Returning a TimsMsRunReader.";

      return std::make_shared<TimsMsRunReader>(ms_run_id);
    }
  else if(m_fileReaderType == FileReaderType::tims_ms2)
    {
      qDebug() << "Returning a TimsMsRunReaderMs2.";

      return std::make_shared<TimsMsRunReaderMs2>(ms_run_id);
    }

  if(m_fileFormat == MzFormat::unknown)
    {
      if(ms_run_id.get()->getMzFormat() == MzFormat::xy)
        {
          return std::make_shared<XyMsRunReader>(ms_run_id);
        }
      else
        {
          return std::make_shared<PwizMsRunReader>(ms_run_id);
        }
    }

  return nullptr;
}

MsRunReaderSPtr
MsFileAccessor::buildMsRunReaderSPtr(MsRunIdCstSPtr ms_run_id)
{
  return buildMsRunReaderSPtr(ms_run_id, pappso::FileReaderType::tims);
}

MsRunReaderSPtr
MsFileAccessor::buildMsRunReaderSPtr(
  MsRunIdCstSPtr ms_run_id, pappso::FileReaderType prefered_file_reader_type)
{

  QFile file(ms_run_id.get()->getFileName());
  if(!file.exists())
    throw(ExceptionNotFound(
      QObject::tr("unable to build a reader : file %1 not found.")
        .arg(QFileInfo(ms_run_id.get()->getFileName()).absoluteFilePath())));

  MzFormat file_format = ms_run_id.get()->getMzFormat();

  if(file_format == MzFormat::xy)
    {
      qDebug() << "Returning a XyMsRunReader.";

      return std::make_shared<XyMsRunReader>(ms_run_id);
    }
  else if(file_format == MzFormat::unknown)
    {
      throw(PappsoException(
        QObject::tr("unable to build a reader for %1 : unknown file format")
          .arg(QFileInfo(ms_run_id.get()->getFileName()).absoluteFilePath())));
    }
  else if(file_format == MzFormat::brukerTims)
    {
      if(prefered_file_reader_type == pappso::FileReaderType::tims)
        {
          return std::make_shared<TimsMsRunReader>(ms_run_id);
        }
      else if(prefered_file_reader_type == pappso::FileReaderType::tims_ms2)
        {
          qDebug();
          return std::make_shared<TimsMsRunReaderMs2>(ms_run_id);
        }

      qDebug() << "by default, build a TimsMsRunReader.";
      return std::make_shared<TimsMsRunReader>(ms_run_id);
    }
  else
    {
      qDebug() << "Returning a PwizMsRunReader .";

      return std::make_shared<PwizMsRunReader>(ms_run_id);
    }
}

MsRunReaderSPtr
MsFileAccessor::getMsRunReaderSPtrByRunId(const QString &run_id,
                                          const QString &xml_id)
{
  qDebug() << "GGGGGGGGG1";
  std::vector<MsRunIdCstSPtr> run_list = getMsRunIds();
  MsRunReaderSPtr reader_sp;
  qDebug() << "GGGGGGGGG";
  for(MsRunIdCstSPtr &original_run_id : run_list)
    {
      if(original_run_id.get()->getRunId() == run_id)
        {
          MsRunId new_run_id(*original_run_id.get());
          new_run_id.setXmlId(xml_id);

          return msRunReaderSp(std::make_shared<MsRunId>(new_run_id));
        }
    }

  if((run_id.isEmpty()) && (run_list.size() == 1))
    {
      MsRunId new_run_id(*run_list[0].get());
      new_run_id.setXmlId(xml_id);

      return msRunReaderSp(std::make_shared<MsRunId>(new_run_id));
    }


  if(reader_sp == nullptr)
    {
      throw(
        ExceptionNotFound(QObject::tr("run id %1 not found in file %2")
                            .arg(run_id)
                            .arg(QFileInfo(m_fileName).absoluteFilePath())));
    }
  return reader_sp;
}

void
MsFileAccessor::setPreferedFileReaderType(MzFormat format,
                                          FileReaderType reader_type)
{
  auto ret = m_preferedFileReaderTypeMap.insert(
    std::pair<MzFormat, FileReaderType>(format, reader_type));
  if(!ret.second)
    {
      // replace
      ret.first->second = reader_type;
    }
}

FileReaderType
MsFileAccessor::getFileReaderType() const
{
  return m_fileReaderType;
}
} // namespace pappso
