/**
 * \file pappsomspp/masschroq/quantificationmethod.cpp
 * \date 24/10/2024
 * \author Olivier Langella
 * \brief store parameters related to peak detection and quantification
 */

/*******************************************************************************
 * Copyright (c) 2024 Olivier Langella
 *<Olivier.Langella@universite-paris-saclay.fr>.
 *
 * This file is part of MassChroQ.
 *
 *     MassChroQ 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.
 *
 *     MassChroQ 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 MassChroQ.  If not, see <http://www.gnu.org/licenses/>.
 *
 ******************************************************************************/

#include "types.h"
#include "quantificationmethod.h"
#include "utils.h"
#include "pappsomspp/core/pappsoexception.h"
#include "pappsomspp/core/exception/exceptionnotfound.h"
#include "pappsomspp/core/processing/detection/tracedetectionzivy.h"

namespace pappso
{

masschroq::QuantificationMethod::QuantificationMethod(const QString &id) : m_id(id)
{
  // by default
  qDebug();
  mp_xicExtractionLowerPrecisionPtr  = PrecisionFactory::getPpmInstance(10);
  mp_xicExtractionUppersPrecisionPtr = PrecisionFactory::getPpmInstance(10);
  m_isotopeMinimumRatio              = 0.8;

  qDebug();
  setXicFilter(std::make_shared<FilterMorphoAntiSpike>(5));

  qDebug();
  mcsp_traceDetectionInterfaceCstSPtr = std::make_shared<TraceDetectionZivy>(1, 3, 2, 5000, 3000);
  qDebug();
}

masschroq::QuantificationMethod::QuantificationMethod(const masschroq::QuantificationMethod &other)
  : m_id(other.m_id)
{
  m_isotopeMinimumRatio                    = other.m_isotopeMinimumRatio;
  m_matchBetweenRun                        = other.m_matchBetweenRun;
  m_xicExtractMethod                       = other.m_xicExtractMethod;
  m_xicFilterSuite                         = other.m_xicFilterSuite;
  mcsp_traceDetectionInterfaceCstSPtr      = other.mcsp_traceDetectionInterfaceCstSPtr;
  mp_xicExtractionLowerPrecisionPtr        = other.mp_xicExtractionLowerPrecisionPtr;
  mp_xicExtractionUppersPrecisionPtr       = other.mp_xicExtractionUppersPrecisionPtr;
  m_xicExtractionRetentionTimeAroundTarget = other.m_xicExtractionRetentionTimeAroundTarget;
}


masschroq::QuantificationMethod::~QuantificationMethod()
{
}

const QString &
masschroq::QuantificationMethod::getId() const
{
  return m_id;
}

double
masschroq::QuantificationMethod::getXicExtractionRtRange() const
{
  return m_xicExtractionRetentionTimeAroundTarget;
}

void
masschroq::QuantificationMethod::setXicExtractionRtRange(double rt_range)
{
  m_xicExtractionRetentionTimeAroundTarget = rt_range;
}

bool
masschroq::QuantificationMethod::getMatchBetweenRun() const
{
  return m_matchBetweenRun;
}

void
masschroq::QuantificationMethod::setIsotopeMinimumRatio(double ratio)
{
  if((ratio < 1) && (ratio >= 0))
    {
      m_isotopeMinimumRatio = ratio;
    }
}

double
masschroq::QuantificationMethod::getIsotopeMinimumRatio() const
{
  return m_isotopeMinimumRatio;
}

void
masschroq::QuantificationMethod::setXicFilter(const FilterNameInterfaceSPtr &filter)
{
  m_xicFilterSuite = std::make_shared<FilterSuiteString>(filter.get()->toString());
}


void
masschroq::QuantificationMethod::addXicFilter(const FilterNameInterfaceSPtr &filter)
{
  if(m_xicFilterSuite.get() == nullptr)
    {
      m_xicFilterSuite = std::make_shared<FilterSuiteString>(filter.get()->toString());
    }
  else
    {
      m_xicFilterSuite.get()->addFilter(filter);
    }
}

const FilterSuiteStringSPtr &
masschroq::QuantificationMethod::getFilterSuiteStringSPtr() const
{
  return m_xicFilterSuite;
}


PrecisionPtr
masschroq::QuantificationMethod::getXicExtractionMeanPrecisionPtr() const
{

  if(mp_xicExtractionLowerPrecisionPtr->unit() == Enums::PrecisionUnit::ppm)
    {
      return PrecisionFactory::getPpmInstance((mp_xicExtractionLowerPrecisionPtr->getNominal() +
                                               mp_xicExtractionUppersPrecisionPtr->getNominal()) /
                                              2);
    }
  return PrecisionFactory::getDaltonInstance((mp_xicExtractionLowerPrecisionPtr->getNominal() +
                                              mp_xicExtractionUppersPrecisionPtr->getNominal()) /
                                             2);
}

Enums::XicExtractMethod
masschroq::QuantificationMethod::getXicExtractMethod() const
{
  return m_xicExtractMethod;
}

void
masschroq::QuantificationMethod::setTraceDetectionInterfaceCstSPtr(
  const TraceDetectionInterfaceCstSPtr &detection)
{
  mcsp_traceDetectionInterfaceCstSPtr = detection;
}

void
masschroq::QuantificationMethod::setXicExtractionLowerPrecisionPtr(PrecisionPtr precision)
{
  mp_xicExtractionLowerPrecisionPtr = precision;
}

void
masschroq::QuantificationMethod::setXicExtractionUpperPrecisionPtr(PrecisionPtr precision)
{
  mp_xicExtractionUppersPrecisionPtr = precision;
}

void
masschroq::QuantificationMethod::setXicExtractMethod(Enums::XicExtractMethod method)
{
  m_xicExtractMethod = method;
}

const FilterNameInterfaceSPtr
masschroq::QuantificationMethod::getXicFilter() const
{
  return m_xicFilterSuite;
}

const MzRange
masschroq::QuantificationMethod::getXicExtractionMzRange(double mz) const
{

  return MzRange(mz, mp_xicExtractionLowerPrecisionPtr, mp_xicExtractionUppersPrecisionPtr);
}

const TraceDetectionInterfaceCstSPtr &
masschroq::QuantificationMethod::getTraceDetectionInterfaceCstSPtr() const
{
  return mcsp_traceDetectionInterfaceCstSPtr;
}

ProjectParameters
masschroq::QuantificationMethod::getProjectParameters() const
{
  ProjectParameters parameters;

  ProjectParam project_param({ProjectParamCategory::quantification, "", QVariant()});
  // xic_type="max"
  project_param.name = "mcq_xic_extraction_type";
  project_param.value.setValue(masschroq::Utils::enumToString(m_xicExtractMethod));
  parameters.setProjectParam(project_param);

  project_param.name = "mcq_mbr";
  project_param.value.setValue(getMatchBetweenRun());
  parameters.setProjectParam(project_param);

  project_param.name = "mcq_isotope_minimum_ratio";
  project_param.value.setValue(getIsotopeMinimumRatio());
  parameters.setProjectParam(project_param);


  if(mp_xicExtractionLowerPrecisionPtr->unit() == Enums::PrecisionUnit::ppm)
    {

      //<!--For XIC extraction on Da use: mz_range-->
      //<ppm_range min="10" max="10"/>

      project_param.name = "mcq_xic_ppm_range_min";
      project_param.value.setValue(mp_xicExtractionLowerPrecisionPtr->getNominal());
      parameters.setProjectParam(project_param);

      project_param.name = "mcq_xic_ppm_range_max";
      project_param.value.setValue(mp_xicExtractionUppersPrecisionPtr->getNominal());
      parameters.setProjectParam(project_param);
    }
  if(mp_xicExtractionLowerPrecisionPtr->unit() == Enums::PrecisionUnit::dalton)
    {

      //<!--For XIC extraction on Da use: mz_range-->
      //<ppm_range min="10" max="10"/>

      project_param.name = "mcq_xic_mz_range_min";
      project_param.value.setValue(mp_xicExtractionLowerPrecisionPtr->getNominal());
      parameters.setProjectParam(project_param);

      project_param.name = "mcq_xic_mz_range_max";
      project_param.value.setValue(mp_xicExtractionUppersPrecisionPtr->getNominal());
      parameters.setProjectParam(project_param);
    }

  project_param.name = "mcq_xic_pre_filter";
  if(m_xicFilterSuite.get() != nullptr)
    project_param.value.setValue(m_xicFilterSuite.get()->toString());
  else
    project_param.value.setValue("");
  parameters.setProjectParam(project_param);


  const TraceDetectionZivy *detection_zivy =
    dynamic_cast<const TraceDetectionZivy *>(mcsp_traceDetectionInterfaceCstSPtr.get());

  if(detection_zivy != nullptr)
    {
      project_param.name = "mcq_detection_zivy";
      project_param.value.setValue(QString("%1 %2 %3 %4 %5")
                                     .arg(detection_zivy->getSmoothingHalfEdgeWindows())
                                     .arg(detection_zivy->getMinMaxHalfEdgeWindows())
                                     .arg(detection_zivy->getMaxMinHalfEdgeWindows())
                                     .arg(detection_zivy->getDetectionThresholdOnMinmax())
                                     .arg(detection_zivy->getDetectionThresholdOnMaxmin()));
      parameters.setProjectParam(project_param);
    }
  return parameters;
}

QJsonObject
masschroq::QuantificationMethod::getJsonObject() const
{
  QJsonObject method;

  qDebug();

  QString xic_type = Utils::enumToString(m_xicExtractMethod);

  if(mp_xicExtractionUppersPrecisionPtr == nullptr)
    {
      throw PappsoException(
        "mp_xicExtractionUppersPrecisionPtr == "
        "nullptr");
    }
  if(mp_xicExtractionLowerPrecisionPtr == nullptr)
    {
      throw PappsoException(
        "mp_xicExtractionLowerPrecisionPtr == "
        "nullptr");
    }
  qDebug();
  QJsonObject precision;
  precision.insert("unit", Utils::enumToString(mp_xicExtractionUppersPrecisionPtr->unit()));
  precision.insert("down", mp_xicExtractionLowerPrecisionPtr->getNominal());
  precision.insert("up", mp_xicExtractionUppersPrecisionPtr->getNominal());


  QJsonObject extraction;
  extraction.insert("integration", xic_type);
  extraction.insert("precision", precision);

  method.insert("extraction", extraction);
  method.insert("match_between_run", m_matchBetweenRun);
  method.insert("isotope_minimum_ratio", m_isotopeMinimumRatio);
  method.insert("rt_range", m_xicExtractionRetentionTimeAroundTarget);

  // xic_filters

  if(m_xicFilterSuite.get() == nullptr)
    {
      throw PappsoException(
        "m_xicFilterSuite.get() == "
        "nullptr");
    }
  method.insert("pre_filter", m_xicFilterSuite.get()->toString());


  const TraceDetectionZivy *detection_zivy =
    dynamic_cast<const TraceDetectionZivy *>(getTraceDetectionInterfaceCstSPtr().get());

  if(detection_zivy == nullptr)
    {
      throw PappsoException(
        "m_quantificationMethod.getTraceDetectionInterfaceCstSPtr().get() == "
        "nullptr");
    }

  QJsonObject detection;
  detection.insert("type", "zivy");
  detection.insert("meanfilter", (int)detection_zivy->getSmoothingHalfEdgeWindows());
  detection.insert("minmax", (int)detection_zivy->getMinMaxHalfEdgeWindows());
  detection.insert("maxmin", (int)detection_zivy->getMaxMinHalfEdgeWindows());
  detection.insert("threshold_on_max", detection_zivy->getDetectionThresholdOnMinmax());
  detection.insert("threshold_on_min", detection_zivy->getDetectionThresholdOnMaxmin());
  method.insert("detection", detection);


  return method;
}

void
masschroq::QuantificationMethod::setJsonObject(const QJsonObject &quantification_method)
{
  m_matchBetweenRun     = quantification_method.value("match_between_run").toBool();
  m_isotopeMinimumRatio = quantification_method.value("isotope_minimum_ratio").toDouble();
  QString filter_str    = quantification_method.value("pre_filter").toString();

  qDebug() << filter_str;
  if(!filter_str.isEmpty())
    {
      FilterNameInterfaceSPtr filter_sp = std::make_shared<FilterSuiteString>(filter_str);
      addXicFilter(filter_sp);
      qDebug() << filter_str;
    }
  QJsonObject extraction = quantification_method.value("extraction").toObject();

  QJsonValue rt_range = extraction.value("rt_range");
  if(!rt_range.isUndefined())
    {
      setXicExtractionRtRange(rt_range.toDouble());
    }

  QString integration = extraction.value("integration").toString();
  if(integration == "sum")
    {
      setXicExtractMethod(Enums::XicExtractMethod::sum);
    }
  else if(integration == "max")
    {
      setXicExtractMethod(Enums::XicExtractMethod::max);
    }
  else
    {
      throw ExceptionNotFound(QObject::tr("missing "
                                          "masschroq_methods>quantification_method>extraction>"
                                          "integration %1 value")
                                .arg(integration));
    }

  QJsonObject precision = extraction.value("precision").toObject();
  qDebug() << precision.value("down").toDouble();
  qDebug() << precision.value("up").toDouble();

  if(precision.value("unit").toString() == "dalton")
    {
      setXicExtractionLowerPrecisionPtr(
        PrecisionFactory::getDaltonInstance(precision.value("down").toDouble()));
      setXicExtractionUpperPrecisionPtr(
        PrecisionFactory::getDaltonInstance(precision.value("up").toDouble()));
    }
  else if(precision.value("unit").toString() == "ppm")
    {
      setXicExtractionLowerPrecisionPtr(
        PrecisionFactory::getPpmInstance(precision.value("down").toDouble()));
      setXicExtractionUpperPrecisionPtr(
        PrecisionFactory::getPpmInstance(precision.value("up").toDouble()));
    }
  else
    {
      throw ExceptionNotFound(
        QObject::tr("missing "
                    "masschroq_methods>quantification_method>extraction>precision>"
                    "unit"));
    }


  QJsonObject detection = quantification_method.value("detection").toObject();
  if(detection.value("type").toString() == "zivy")
    {
      std::shared_ptr<TraceDetectionZivy> sp_detection_zivy =
        std::make_shared<TraceDetectionZivy>(detection.value("meanfilter").toInt(),
                                             detection.value("minmax").toInt(),
                                             detection.value("maxmin").toInt(),
                                             detection.value("threshold_on_max").toInt(),
                                             detection.value("threshold_on_min").toInt());
      setTraceDetectionInterfaceCstSPtr(sp_detection_zivy);
    }
  else
    {

      throw ExceptionNotFound(
        QObject::tr("only masschroq_methods>quantification_method>detection>type == zivy "
                    "allowed"));
    }
}

PrecisionPtr
masschroq::QuantificationMethod::getXicExtractionLowerPrecisionPtr() const
{
  return mp_xicExtractionLowerPrecisionPtr;
}

PrecisionPtr
masschroq::QuantificationMethod::getXicExtractionUppersPrecisionPtr() const
{
  return mp_xicExtractionUppersPrecisionPtr;
}

void
masschroq::QuantificationMethod::setMatchBetweenRun(bool is_match)
{
  m_matchBetweenRun = is_match;
}

} // namespace pappso
