/* BEGIN software license
 *
 * msXpertSuite - mass spectrometry software suite
 * -----------------------------------------------
 * Copyright(C) 2009,...,2019 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
 */

#pragma once

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


/////////////////////// Qt includes
#include <QDebug>
#include <QObject>
#include <QSharedPointer>

/////////////////////// pappsomspp includes


/////////////////////// libXpertMassGui includes


/////////////////////// Local includes
#include "MsXpS/export-import-config.h"
#include "MsXpS/libXpertMassCore/AbstractLowMassDeconvolver.hpp"
#include "MsXpS/libXpertMassCore/MonitoredTaskInterface.hpp"

namespace MsXpS
{
namespace libXpertMassCore
{

struct DECLSPEC DeconvolutedMassIdMatcherParameters
{
  QString dictionaryFilePath           = "";
  QString isotopicDataFilePath         = "";
  QString processingDetailsLogFilePath = "";
  Enums::MassType massType             = Enums::MassType::MONO;
  double massTolerancePpm              = 20;

  DeconvolutedMassIdMatcherParameters();

  DeconvolutedMassIdMatcherParameters(
    const QString &dictionary_file_path,
    const QString &isotopic_data_file_path,
    const QString &processing_details_log_file_path,
    Enums::MassType mass_type,
    double mass_tolerance_ppm);


  DeconvolutedMassIdMatcherParameters(
    const DeconvolutedMassIdMatcherParameters &other);

  DeconvolutedMassIdMatcherParameters &
  operator=(const DeconvolutedMassIdMatcherParameters &other);

  QString
  toString() const
  {
    QString text = "DeconvolutedMassIdMatcher parameters:\n";

    text += QString("Dictionary file path: %1\n").arg(dictionaryFilePath);
    text += QString("Isotopic data file path: %1\n").arg(isotopicDataFilePath);
    text += QString("Mass type: %1\n")
              .arg(massType == Enums::MassType::MONO ? "MONO" : "AVG");
    text +=
      QString("Mass tolerance (ppm): %1\n").arg(massTolerancePpm, 0, 'f', 0);

    return text;
  }
};

class DECLSPEC DeconvolutedMassIdMatcher: public MonitoredTaskInterface
{
  Q_OBJECT

  public:
  // These are the data that are needed to document the context in the thread
  // without messing around in the main GUI thread.
  struct Data
  {
    QSharedPointer<QList<libXpertMassCore::DeconvolutedFeature>>
      deconvolutedFeaturesQSPtr;
    void *initialSourcePlottable = nullptr;
    QString sampleName;
    QString labelColorString;
    QString labelFontString;
    QString labelListName;
    bool shouldWriteLabels;
    bool shouldAddLabelToList;
    ErrorList *errorList = nullptr;
  } m_data;

  struct DECLSPEC MassIdMatch
  {
    QSharedPointer<DeconvolutedFeature> deconvolutedFeatureQsp = nullptr;

    QString id;
    QString structure;
    QString formula;
    double mass      = 0;
    double massError = 0;

    MassIdMatch(QString the_id,
                QString the_structure,
                QString the_formula,
                QSharedPointer<DeconvolutedFeature> deconvoluted_feature_qsp,
                double the_mass = 0)
      : deconvolutedFeatureQsp(deconvoluted_feature_qsp),
        id(the_id),
        structure(the_structure),
        formula(the_formula),
        mass(the_mass)
    {
    }

    QString
    toString()
    {
      QString text  = "<======= MassIdMatch =======>\n";
      text         += QString(
                "id: %1\nstructure: %2\nformula: %3\n"
                        "mass: %4 - mass error: %5\n\n"
                        "Deconvoluted feature: \n%6\n")
                .arg(id)
                .arg(structure)
                .arg(formula)
                .arg(mass, 0, 'f', 6)
                .arg(massError)
                .arg(deconvolutedFeatureQsp != nullptr
                       ? deconvolutedFeatureQsp->toString()
                       : "nullptr");
      text += "<===========================>\n";

      return text;
    }
  };

  DeconvolutedMassIdMatcher(
    DeconvolutedMassIdMatcherParameters &parameters,
    QSharedPointer<QList<DeconvolutedFeature>> deconvoluted_features_qsp,
    QObject *parent = nullptr);

  virtual ~DeconvolutedMassIdMatcher();

  void setData(const Data &data);

  const QList<QSharedPointer<MassIdMatch>> &getMassIdMatches() const;

  int loadDictionaryFile(ErrorList *error_list_p);
  int performMatches();
  void run();

  void removeUnusedMassIdMatches();

  void cancelTask() override;

  // Public member data
  DeconvolutedMassIdMatcherParameters m_params;

  signals:

  void finishedSignal(void *initial_source_plottable,
                      const QString &sample_name,
                      const QString &label_color_string,
                      const QString &label_font_string,
                      const QString &label_list_name,
                      bool should_write_labels,
                      bool should_add_label_to_list);

  private:
  IsotopicDataCstSPtr mcsp_isotopicData = nullptr;

  // If non-nullptr, all the details about the computation are stored in the
  // file.

  QFile *mp_logFile             = nullptr;
  QTextStream *mp_logTextStream = nullptr;

  QSharedPointer<QList<DeconvolutedFeature>> mqsp_deconvolutedFeatures =
    nullptr;
  QList<QSharedPointer<MassIdMatch>> m_massIdMatches;

  inline void
  logProcessingDetails(const QString &details_string) const
  {
    if(mp_logTextStream != nullptr)
      {
        *mp_logTextStream << details_string;
        mp_logTextStream->flush();
      }
  }
};


} // namespace libXpertMassCore

} // namespace MsXpS
