/**
 * \file output/odsfeatures.cpp
 * \date 20/08/2025
 * \author Olivier Langella
 * \brief ODS PSM export
 */


/*
 * SpecGlobTool, Spectra to peptide alignment tool
 * Copyright (C) 2025  Olivier Langella
 * <olivier.langella@universite-paris-saclay.fr>
 *
 * 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/>.
 *
 */

#include "odsfeatures.h"
#include <pappsomspp/core/pappsoexception.h>
#include <pappsomspp/core/peptide/peptideproformaparser.h>

OdsFeaturesBase::OdsFeaturesBase(CalcWriterInterface &output) : m_output(output)
{
  output.writeSheet("features");
  output.writeCell("SampleName");
  output.writeCell("SpectrumIndex");
  output.writeCell("Peptide");
  output.writeCell("PeptideSpecFit");
  output.writeCell("Charge");
  output.writeCell("PrecursorM");
  output.writeCell("PrecursorMZ");
  output.writeCell("CalMW");
  output.writeCell("DeltaMass");
  output.writeCell("AbsDeltaMass");
  output.writeCell("PeptideLength");
  output.writeCell("IdentificationEngine");
  // SpecOMS
  output.writeCell("SpecOmsScore");
  output.writeCell("SpecFitScore");
  output.writeCell("SpecOmsMatchType");
  output.writeCell("SpecOmsCandidateStatus");
  output.writeCell("TIC");
  output.writeCell("MaxIntALL");
  output.writeCell("MaxYionInt");
  output.writeCell("MaxBionInt");
  output.writeCell("SumYmatchInt");
  output.writeCell("SumBmatchInt");
  output.writeCell("FracYmatchInt");
  output.writeCell("FracBmatchInt");
  output.writeCell("SeqCoverYion");
  output.writeCell("SeqCoverBion");
  output.writeCell("ConsecutiveYion");
  output.writeCell("ConsecutiveBion");
  output.writeCell("MassErrMean");
  output.writeCell("MassErrSD");
  output.writeCell("NumofAnnoPeaks");
  output.writeCell("NumofComplementPeaks");
  output.writeCell("SumComplementPeaksInt");
  output.writeCell("FracComplementPeaksInt");
  output.writeCell("SeqCoverComplementPeaks");
  output.writeCell("IsotopeRatioVectorSize");
  output.writeCell("IsotopeRatioCoefficientOfDetermination");
  output.writeCell("Hyperscore");
  //


  //********************
  output.writeCell("TorD");
  output.writeLine();
}
OdsFeaturesBase::~OdsFeaturesBase()
{
}


double
OdsFeaturesBase::getPrecursorMass(double mz_prec, uint charge) const
{
  // compute precursor mass given the charge state
  mz_prec = mz_prec * (double)charge;
  mz_prec -= (pappso::MHPLUS * (double)charge);
  return mz_prec;
}

void
OdsFeaturesBase::writePsmLine(const QString &sample_name,
                              const QString &psm_proforma,
                              const QCborMap &cborScanId,
                              const QCborMap &cborScanPrecursor,
                              const QCborMap &cborScanPsmEval,
                              bool target_or_decoy)
{
  m_output.writeCell(sample_name);
  m_output.writeCell((std::size_t)cborScanId.value("index").toInteger());

  int charge    = cborScanPrecursor.value("z").toInteger();
  double exp_mz = cborScanPrecursor.value("mz").toDouble();
  double exp_mh = getPrecursorMass(exp_mz, charge);

  pappso::PeptideSp peptide_sp =
    pappso::PeptideProFormaParser::parseString(psm_proforma);
  // m_output.writeCell("Peptide");
  m_output.writeCell(psm_proforma);
  m_output.writeCell("PeptideSpecFit");
  // m_output.writeCell("Charge");
  m_output.writeCell(charge);
  // m_output.writeCell("PrecursorM");
  m_output.writeCell(exp_mh);
  // m_output.writeCell("PrecursorMZ");
  m_output.writeCell(exp_mz);
  // m_output.writeCell("CalMW");
  m_output.writeCell(peptide_sp.get()->getMz(charge));
  // m_output.writeCell("DeltaMass");
  double mass_delta = exp_mh - peptide_sp.get()->getMz(1);
  m_output.writeCell(mass_delta);
  // m_output.writeCell("AbsDeltaMass");
  m_output.writeCell(std::abs(mass_delta));
  m_output.writeCell((int)peptide_sp.get()->size());

  int identification_engine = 0;
  if(cborScanPsmEval.contains(QString("specoms")))
    {
      identification_engine = 1;
    }
  else if(cborScanPsmEval.contains(QString("sage")))
    {
      identification_engine = 2;
    }
  // m_output.writeCell("IdentificationEngine");
  m_output.writeCell(identification_engine);
  // SpecOMS
  QCborMap cbor_specoms = cborScanPsmEval.value("specoms").toMap();
  QCborMap cbor_specfit = cborScanPsmEval.value("specfit").toMap();
  if(cbor_specoms.isEmpty())
    {
      m_output.writeEmptyCell();
      m_output.writeEmptyCell();
      m_output.writeEmptyCell();
      m_output.writeEmptyCell();
    }
  else
    {
      // qWarning() << cbor_specoms.keys();

      // m_output.writeCell("SpecOmsScore");
      m_output.writeCell((int)cbor_specoms.value("count").toInteger());
      // m_output.writeCell("SpecFitScore");
      m_output.writeCell((int)cbor_specfit.value("count").toInteger());
      // m_output.writeCell("SpecOmsMatchType");
      m_output.writeCell((int)cbor_specoms.value("match_type").toInteger());
      // m_output.writeCell("SpecOmsCandidateStatus");
      m_output.writeCell((int)cbor_specoms.value("status").toInteger());
    }


  QCborMap cbor_specglob = cborScanPsmEval.value("specglob").toMap();
  if(cbor_specglob.isEmpty())
    {
    }
  else
    {
      // qWarning() << cbor_specglob.keys();
      //  QList(QCborValue("max_score"), QCborValue("count"),
      //  QCborValue("proforma"), QCborValue("bracket"))
    }

  // features

  QCborMap cbor_features = cborScanPsmEval.value("features").toMap();
  if(cbor_features.isEmpty())
    {
      throw pappso::PappsoException(QObject::tr("no features in this PSM"));
    }
  // m_output.writeCell("TIC");
  m_output.writeCell(cbor_features.value("total_intensity").toDouble());
  // m_output.writeCell("MaxIntALL");
  m_output.writeCell(cbor_features.value("max_intensity").toDouble());
  // m_output.writeCell("MaxYionInt");
  m_output.writeCell(cbor_features.value("MaxYionInt").toDouble());
  // m_output.writeCell("MaxBionInt");
  m_output.writeCell(cbor_features.value("MaxBionInt").toDouble());
  // m_output.writeCell("SumYmatchInt");
  m_output.writeCell(cbor_features.value("SumYmatchInt").toDouble());
  // m_output.writeCell("SumBmatchInt");
  m_output.writeCell(cbor_features.value("SumBmatchInt").toDouble());
  // m_output.writeCell("FracYmatchInt");
  m_output.writeCell(cbor_features.value("FracYmatchInt").toDouble());
  // m_output.writeCell("FracBmatchInt");
  m_output.writeCell(cbor_features.value("FracBmatchInt").toDouble());
  // m_output.writeCell("SeqCoverYion");
  m_output.writeCell(cbor_features.value("SeqCoverYion").toDouble());
  // m_output.writeCell("SeqCoverBion");
  m_output.writeCell(cbor_features.value("SeqCoverBion").toDouble());
  // m_output.writeCell("ConsecutiveYion");
  m_output.writeCell((int)cbor_features.value("ConsecutiveYion").toInteger());
  // m_output.writeCell("ConsecutiveBion");
  m_output.writeCell((int)cbor_features.value("ConsecutiveBion").toInteger());
  // m_output.writeCell("MassErrMean");
  m_output.writeCell(cbor_features.value("MassErrMean").toDouble());
  // m_output.writeCell("MassErrSD");
  m_output.writeCell(cbor_features.value("MassErrSD").toDouble());
  // m_output.writeCell("NumofAnnoPeaks");
  m_output.writeCell((int)cbor_features.value("NumofAnnoPeaks").toInteger());
  // m_output.writeCell("NumofComplementPeaks");
  m_output.writeCell(
    (int)cbor_features.value("NumofComplementPeaks").toInteger());
  // m_output.writeCell("SumComplementPeaksInt");
  m_output.writeCell(cbor_features.value("SumComplementPeaksInt").toDouble());
  // m_output.writeCell("FracComplementPeaksInt");
  m_output.writeCell(cbor_features.value("FracComplementPeaksInt").toDouble());
  // m_output.writeCell("SeqCoverComplementPeaks");
  m_output.writeCell(cbor_features.value("SeqCoverComplementPeaks").toDouble());
  // m_output.writeCell("IsotopeRatioVectorSize");
  m_output.writeCell((int)cbor_features.value("lrSize").toInteger());
  // m_output.writeCell("IsotopeRatioCoefficientOfDetermination");
  m_output.writeCell(cbor_features.value("lrCoeffDet").toDouble());
  // m_output.writeCell("Hyperscore");
  m_output.writeCell(cbor_features.value("hyperscore").toDouble());
  //


  //********************
  // output.writeCell("TorD");
  if(target_or_decoy)
    {
      m_output.writeCell("T");
    }
  else
    {
      m_output.writeCell("D");
    }
  m_output.writeLine();
}


OdsFeatures::OdsFeatures(CalcWriterInterface &output) : OdsFeaturesBase(output)
{
}
OdsFeatures::~OdsFeatures()
{
}

void
OdsFeatures::close()
{
  m_output.close();
}


void
OdsFeatures::psmReady(pappso::UiMonitorInterface &monitor [[maybe_unused]])
{

  bool tord = false;
  //********************
  // output.writeCell("TorD");
  if(currentProteinRefListContainsTarget())
    {
      tord = true;
    }
  writePsmLine(m_currentSampleName,
               m_currentPsmProforma,
               m_cborScanId,
               m_cborScanPrecursor,
               m_cborScanPsmEval,
               tord);
}

OdsFeaturesReadAndCopy::OdsFeaturesReadAndCopy(
  pappso::cbor::CborStreamWriter *cbor_output_p, CalcWriterInterface &output)
  : PsmFileScanProcessAndCopy(1000, cbor_output_p, "copy"),
    OdsFeaturesBase(output)
{
}

void
OdsFeaturesReadAndCopy::close()
{
  m_output.close();
}

OdsFeaturesReadAndCopy::~OdsFeaturesReadAndCopy()
{
}

pappso::cbor::psm::CborScanMapBase *
OdsFeaturesReadAndCopy::newCborScanMap()
{
  pappso::cbor::psm::CborScanMapBase *p_cbor_scan_map =
    new pappso::cbor::psm::CborScanMapBase(*this);
  return p_cbor_scan_map;
}


void
OdsFeaturesReadAndCopy::processBufferScan(pappso::UiMonitorInterface &monitor)
{
  // you decide how to process the list of scans

  for(const pappso::cbor::psm::CborScanMapBase *p_scan : m_cborScanList)
    {
      QCborMap cborScanId(p_scan->getCborScanId());
      QCborMap cborScanPrecursor(p_scan->getCborScanPrecursor());


      for(QCborValue cbor_psm : p_scan->getCborPsmList())
        {
          QCborMap cbor_psm_map = cbor_psm.toMap();

          if(!cbor_psm_map.keys().contains("proforma"))
            {
              throw pappso::PappsoException(
                QObject::tr("missing proforma in psm %1")
                  .arg(cbor_psm_map.keys().size()));
            }

          QString psm_proforma = cbor_psm_map.value("proforma").toString();


          QCborMap psm_eval = cbor_psm_map.value("eval").toMap();


          m_currentPsmProteinRefList.clear();
          /*
           *
                      "protein_list" : [
                        {
                          "accession": "GRMZM2G083841_P01",
                          "position": [15,236]
                        }
                        */
          for(QCborValue the_protein_ref :
              cbor_psm_map.value("protein_list").toArray())
            {
              m_currentPsmProteinRefList.push_back(
                pappso::cbor::psm::PsmFileReaderBase::PsmProteinRef(
                  {the_protein_ref.toMap().value("accession").toString(), {}}));
            }

          bool tord = false;
          //********************
          // output.writeCell("TorD");
          if(currentProteinRefListContainsTarget())
            {
              tord = true;
            }
          writePsmLine(m_currentSampleName,
                       psm_proforma,
                       cborScanId,
                       cborScanPrecursor,
                       psm_eval,
                       tord);
        }
    }
  processBufferScanDone(monitor);
}
