/* 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 "MassDataCborBaseHandler.hpp"


namespace MsXpS
{

namespace libXpertMass
{


// FIXME TODO DOCUMENTATION


MassDataCborBaseHandler::MassDataCborBaseHandler(QObject *parent_p)
  : QObject(parent_p)
{
}


MassDataCborBaseHandler::~MassDataCborBaseHandler()
{
}

void
MassDataCborBaseHandler::setInputFileName(const QString &file_name)
{
  m_inputFileName = file_name;
}


void
MassDataCborBaseHandler::setOutputFileName(const QString &file_name)
{
  m_outputFileName = file_name;
}


void
MassDataCborBaseHandler::setMassDataType(MassDataType mass_data_type)
{
  m_massDataType = mass_data_type;
}


MassDataType
MassDataCborBaseHandler::getMassDataType() const
{
  return m_massDataType;
}


bool
MassDataCborBaseHandler::readContext(
  [[maybe_unused]] QCborStreamReaderSPtr &reader_sp)
{
  qDebug() << "The base class handler function does nothing.";
  return false;
}


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<MassDataType>(reader_sp->toUnsignedInteger());
            }
          else
            qFatal(
              "The byte array does not have the expected CBOR "
              "structure for "
              "mass data.");
        }
    }

  return MassDataType::UNSET;
}


MassDataType
MassDataCborBaseHandler::readMassDataType(const QString &input_file_name)
{
  QFileInfo file_info(input_file_name);

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

  QFile file(input_file_name);

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

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

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

  return readMassDataType(reader_sp);
}


MassDataType
MassDataCborBaseHandler::readMassDataType(const QByteArray &byte_array)
{
  QCborStreamReaderSPtr reader_sp =
    std::make_shared<QCborStreamReader>(byte_array);

  return readMassDataType(reader_sp);
}


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


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


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


void
MassDataCborBaseHandler::writeByteArray([[maybe_unused]] QByteArray &byte_array)
{
  qDebug() << "The base class handler function does nothing.";
}


void
MassDataCborBaseHandler::setTitle(const QString &title)
{
  m_title = title;
}


QString
MassDataCborBaseHandler::getTitle() const
{
  return m_title;
}


} // namespace libXpertMass

} // namespace MsXpS

