/**
 * \file pappsomspp/core/processing/cbor/mzcbor/precursor.h
 * \date 24/11/2025
 * \author Olivier Langella
 * \brief PSI precursor object for mzML/mzCBOR
 */

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


#include "precursor.h"
#include "pappsomspp/core/exception/exceptionnotfound.h"


void
pappso::cbor::mzcbor::IsolationWindow::fromCbor(CborStreamReader &reader)
{
  QString txt_value;
  reader.enterContainer();
  qDebug() << txt_value;
  while(reader.hasNext() && (!reader.isInvalid()))
    {
      if(reader.isString())
        {
          if(reader.decodeString(txt_value))
            {
              qDebug() << txt_value;
              if(txt_value == "cvParam")
                {
                  cvParamMap.fromCbor(reader);
                  // reader.next();
                }
              else
                {
                  reader.next();
                }
            }
          else
            {
              reader.next();
            }
        }
      else
        {
          reader.next();
        }
    }
  reader.leaveContainer();
}

double
pappso::cbor::mzcbor::IsolationWindow::getTargetMz() const
{
  auto it = cvParamMap.find("MS:1000827");

  if(it == cvParamMap.end())
    {
      throw pappso::ExceptionNotFound(
        QObject::tr("isolation window target m/z MS:1000827 not found in cvParam map"));
    }

  return it->second.valueDouble;
}

double
pappso::cbor::mzcbor::IsolationWindow::getLowerMz() const
{
  double mz = getTargetMz();

  auto it = cvParamMap.find("MS:1000828");

  if(it == cvParamMap.end())
    {
      throw pappso::ExceptionNotFound(
        QObject::tr("isolation window lower offset MS:1000828 not found in cvParam map"));
    }

  return mz - it->second.valueDouble;
}

QJsonObject
pappso::cbor::mzcbor::IsolationWindow::toJsonObject() const
{
  QJsonObject isolationWindow;
  isolationWindow.insert("cvParam", cvParamMap.toJsonArray());
  return isolationWindow;
}
QJsonObject
pappso::cbor::mzcbor::Precursor::toJsonObject() const
{
  QJsonObject precursor;
  if(!spectrumRef.isEmpty())
    precursor.insert("spectrumRef", spectrumRef);
  precursor.insert("isolationWindow", isolationWindow.toJsonObject());
  return precursor;
}

double
pappso::cbor::mzcbor::IsolationWindow::getUpperMz() const
{

  double mz = getTargetMz();

  auto it = cvParamMap.find("MS:1000829");

  if(it == cvParamMap.end())
    {
      throw pappso::ExceptionNotFound(
        QObject::tr("isolation window upper offset MS:1000829 not found in cvParam map"));
    }

  return mz + it->second.valueDouble;
}

void
pappso::cbor::mzcbor::SelectedIon::fromCbor(CborStreamReader &reader)
{
  QString txt_value;
  reader.enterContainer();
  qDebug() << txt_value;
  while(reader.hasNext() && (!reader.isInvalid()))
    {
      if(reader.isString())
        {
          if(reader.decodeString(txt_value))
            {
              qDebug() << txt_value;
              if(txt_value == "cvParam")
                {
                  cvParamMap.fromCbor(reader);
                  // reader.next();
                }
              else
                {
                  reader.next();
                }
            }
          else
            {
              reader.next();
            }
        }
      else
        {
          reader.next();
        }
    }
  reader.leaveContainer();
}


int
pappso::cbor::mzcbor::SelectedIon::getChargeState() const
{

  //      <cvParam cvRef="MS" accession="MS:1000041" value="3" name="charge state" />

  auto it = cvParamMap.find("MS:1000041");
  if(it == cvParamMap.end())
    {
      throw pappso::ExceptionNotFound(
        QObject::tr("Charge state not found in SelectedIon cvParam map"));
    }
  qDebug() << it->first << " " << it->second.valueInt;
  return it->second.valueInt;
}

double
pappso::cbor::mzcbor::SelectedIon::getIntensity(bool *isok) const
{

  //         <cvParam cvRef="MS" accession="MS:1000042" value="248032.25" name="peak intensity"
  //         unitAccession="MS:1000131" unitName="number of detector counts" unitCvRef="MS" />
  // MS:1000042
  auto it = cvParamMap.find("MS:1000042");
  if(it == cvParamMap.end())
    {
      // throw pappso::ExceptionNotFound(
      // QObject::tr("Intensity not found in SelectedIon cvParam map"));
      *isok = false;
      return 0;
    }
  qDebug() << it->first << " " << it->second.valueDouble;
  double intensity = it->second.valueDouble;
  *isok            = true;
  if(std::isnan(intensity))
    {
      return it->second.valueInt;
    }
  return intensity;
}

double
pappso::cbor::mzcbor::SelectedIon::getMz() const
{

  //     <cvParam cvRef="MS" accession="MS:1000744" value="506.2513" name="selected ion m/z"
  //     unitAccession="MS:1000040" unitName="m/z" unitCvRef="MS" />

  auto it = cvParamMap.find("MS:1000744");
  if(it == cvParamMap.end())
    {
      throw pappso::ExceptionNotFound(QObject::tr("m/z not found in SelectedIon cvParam map"));
    }
  qDebug() << it->first << " " << it->second.valueDouble;
  return it->second.valueDouble;
}


void
pappso::cbor::mzcbor::Precursor::fromCbor(CborStreamReader &reader)
{

  QString txt_value;
  reader.enterContainer();
  qDebug() << txt_value;
  while(reader.hasNext() && (!reader.isInvalid()))
    {
      if(reader.isString())
        {
          if(reader.decodeString(txt_value))
            {
              qDebug() << txt_value;
              if(txt_value == "cvParam")
                {
                  // precursorListCvParamMap = CvParam::getCvParamsMapFromCbor(reader);
                  reader.next();
                }
              else if(txt_value == "isolationWindow")
                {
                  isolationWindow.fromCbor(reader);
                }
              else if(txt_value == "selectedIonList")
                {

                  reader.enterContainer();

                  while(reader.hasNext() && (!reader.isInvalid()))
                    {
                      if(reader.isString())
                        {
                          if(reader.decodeString(txt_value))
                            {
                              qDebug() << txt_value;
                              if(txt_value == "cvParam")
                                {
                                  selectedIonListCvParamMap.fromCbor(reader);
                                }
                              else if(txt_value == "selectedIon")
                                {

                                  reader.enterContainer(); // array

                                  while(reader.hasNext() && (!reader.isInvalid()))
                                    {
                                      SelectedIon selected_ion;
                                      selected_ion.fromCbor(reader);
                                      selectedIonList.emplace_back(selected_ion);
                                    }
                                  reader.leaveContainer(); // array
                                }
                              else
                                {
                                  reader.next();
                                }
                            }
                          else
                            {
                              reader.next();
                            }
                        }
                      else
                        {
                          reader.next();
                        }
                    }
                  reader.leaveContainer();
                }
              else if(txt_value == "activation")
                {
                  qDebug();
                  reader.next();
                }

              else if(txt_value == "spectrumRef")
                {
                  reader.decodeString(txt_value);
                  qDebug() << txt_value;
                  spectrumRef = txt_value;
                }
              else
                {
                  reader.next();
                }
            }
          else
            {
              reader.next();
            }
        }
      else
        {
          reader.next();
        }
    }
  reader.leaveContainer();
}
