/* BEGIN software license
 *
 * MsXpertSuite - mass spectrometry software suite
 * -----------------------------------------------
 * Copyright (C) 2009--2020 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
 */


/////////////////////// Std lib includes


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


/////////////////////// IsoSpec


/////////////////////// Local includes
#include "MsXpS/libXpertMassCore/MassDataCborBaseHandler.hpp"

namespace MsXpS
{

namespace libXpertMassCore
{


/*!
\class MsXpS::libXpertMassCore::MassDataCborBaseHandler
\inmodule libXpertMassCore
\ingroup XpertMassCoreUtilities
\inheaderfile MassDataCborBaseHandler.hpp

\brief The MassDataCborBaseHandler class provides features to handle
mass spectrometric data using the CBOR (Concise Binary Object Representation)
container streaming classes.

The data transported using CBOR is first qualified using the
\l{Enums::MassDataType} enum. The data are packed following that mass data type
specification.
*/


/*!
 \brief Constructs a MassDataCborBaseHandler instance setting parent to \a
 parent_p.
 */
MassDataCborBaseHandler::MassDataCborBaseHandler(QObject *parent_p)
  : QObject(parent_p)
{
}

/*!
 \brief Destructs the MassDataCborBaseHandler instance.
 */
MassDataCborBaseHandler::~MassDataCborBaseHandler()
{
}

/*!
\brief Sets the input file name to \a file_name.
*/
void
MassDataCborBaseHandler::setInputFileName(const QString &file_name)
{
  m_inputFileName = file_name;
}

/*!
 \brief Sets the output file name to \a file_name.
 */
void
MassDataCborBaseHandler::setOutputFileName(const QString &file_name)
{
  m_outputFileName = file_name;
}

/*!
\brief Sets the mass data type to \a mass_data_type.
*/
void
MassDataCborBaseHandler::setMassDataType(Enums::MassDataType mass_data_type)
{
  m_massDataType = mass_data_type;
}

/*!
\brief Returns the mass data type.
*/
Enums::MassDataType
MassDataCborBaseHandler::getMassDataType() const
{
  return m_massDataType;
}

/*!
\brief Decodes the data stream from \a reader_sp.

\note The base class does nothing and always returns false.
*/
bool
MassDataCborBaseHandler::decodeStream(
  [[maybe_unused]] QCborStreamReaderSPtr &reader_sp)
{
  qDebug() << "The base class handler function does nothing.";
  return false;
}

/*!
\brief Decodes enough of the data streamed by  \a reader_sp to determine and
return the mass data type of the streamed data.

\note Failure to decode the mass data type is fatal.
*/
Enums::MassDataType
MassDataCborBaseHandler::readMassDataType(QCborStreamReaderSPtr &reader_sp)
{
  QString current_key;

  // The file format starts with a quint64 that indicates the type of mass
  // data (MassDataType enum to quint64).

  while(!reader_sp->lastError() && reader_sp->hasNext())
    {
      // The first iteration in the CBOR data structure is in a map.

      QCborStreamReader::Type type = reader_sp->type();
      // qDebug() << "Type is:" << type;

      if(type != QCborStreamReader::Map)
        qFatal(
          "The byte array does not have the expected CBOR structure for "
          "mass data.");

      reader_sp->enterContainer();

      while(reader_sp->lastError() == QCborError::NoError &&
            reader_sp->hasNext())
        {
          QCborStreamReader::Type type = reader_sp->type();
          // qDebug() << "Type is:" << type;

          // The very first item of the map should be a pair
          //
          // "DATA_TYPE / quint64

          if(type == QCborStreamReader::String)
            {
              // Read a CBOR string, concatenating all
              // the chunks into a single string.
              QString str;
              auto chunk = reader_sp->readString();
              while(chunk.status == QCborStreamReader::Ok)
                {
                  str   += chunk.data;
                  chunk  = reader_sp->readString();
                }

              if(chunk.status == QCborStreamReader::Error)
                {
                  // handle error condition
                  qDebug() << "There was an error reading string chunk.";
                  str.clear();
                }

              // qDebug() << "The string that was read:" << str;

              // If the current key is empty, this string value is a new key
              // or tag. Otherwise, it's a value.

              if(current_key.isEmpty())
                {
                  // qDebug() << "Setting current_key:" << str;
                  current_key = str;
                }
              else
                {
                  // At this point we can reset current_key.
                  current_key = QString();
                }
            }
          else if(type == QCborStreamReader::UnsignedInteger)
            {
              if(current_key != "DATA_TYPE")
                qFatal(
                  "The byte array does not have the expected CBOR "
                  "structure for "
                  "mass data.");

              // quint64 data_type = reader_sp->toUnsignedInteger();
              // qDebug() << "The mass data type:" << data_type;

              return static_cast<Enums::MassDataType>(
                reader_sp->toUnsignedInteger());
            }
          else
            qFatal(
              "The byte array does not have the expected CBOR "
              "structure for mass data.");
        }
    }

  return Enums::MassDataType::NOT_SET;
}

/*!
 \overload

 \brief Decodes enough of the data contained in file \a input_file_name to
 determine and return the mass data type of the streamed data.

 \note Failure to decode the mass data type is fatal.

 \sa readMassDataType() overloads
 */
Enums::MassDataType
MassDataCborBaseHandler::readMassDataType(const QString &input_file_name)
{
  QFileInfo file_info(input_file_name);

  if(!file_info.exists())
    {
      qDebug() << "File not found.";
      return Enums::MassDataType::NOT_SET;
    }

  QFile file(input_file_name);

  bool res = file.open(QIODevice::ReadOnly);

  if(!res)
    {
      qDebug() << "Failed to open the file for read.";
      return Enums::MassDataType::NOT_SET;
    }

  QCborStreamReaderSPtr reader_sp = std::make_shared<QCborStreamReader>(&file);

  return readMassDataType(reader_sp);
}

/*!
\overload

 \brief Decodes enough of the data contained in \a byte_array to
 determine and return the mass data type of the streamed data.

 \note Failure to decode the mass data type is fatal.

 \sa readMassDataType() overloads
 */
Enums::MassDataType
MassDataCborBaseHandler::readMassDataType(const QByteArray &byte_array)
{
  QCborStreamReaderSPtr reader_sp =
    std::make_shared<QCborStreamReader>(byte_array);

  return readMassDataType(reader_sp);
}

/*!
\brief Reads the data in file \a input_file_name by decoding the data stream
read from it.

\note The base class does nothing and always returns false.
*/
bool
MassDataCborBaseHandler::readFile(
  [[maybe_unused]] const QString &input_file_name)
{
  qDebug() << "The base class handler function does nothing.";
  return false;
}

/*!
 \brief Reads the data in byte array \a byte_array by decoding the data stream
 read from it.

 \note The base class does nothing and always returns false.
 */
bool
MassDataCborBaseHandler::readByteArray(
  [[maybe_unused]] const QByteArray &byte_array)
{
  qDebug() << "The base class handler function does nothing.";
  return false;
}

/*!
\brief Writes member data to file \a output_file_name.

\note The base class does nothing and always returns false.
*/
bool
MassDataCborBaseHandler::writeFile(
  [[maybe_unused]] const QString &output_file_name)
{
  qDebug() << "The base class handler function does nothing.";
  return false;
}

/*!
\brief Writes member data to byte array \a byte_array.

\note The base class does nothing and always returns false.
*/
void
MassDataCborBaseHandler::writeByteArray([[maybe_unused]] QByteArray &byte_array)
{
  qDebug() << "The base class handler function does nothing.";
}

/*!
\brief Sets the \a title of the mass data entity carried by the CBOR container.
*/
void
MassDataCborBaseHandler::setTitle(const QString &title)
{
  m_title = title;
}

/*!
 \brief Returns the title of the mass data entity carried by the CBOR container.
 */
QString
MassDataCborBaseHandler::getTitle() const
{
  return m_title;
}


} // namespace libXpertMassCore

} // namespace MsXpS
