/**
 * \file pappsomspp/core/processing/cbor/mzcbor/cvparam.cpp
 * \date 23/11/2025
 * \author Olivier Langella
 * \brief PSI cvParam 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 "cvparam.h"
#include "pappsomspp/core/pappsoexception.h"
#include <cmath>
#include <QJsonObject>
#include <QDebug>


void
pappso::cbor::mzcbor::CvParam::fromCbor(CborStreamReader &reader)
{
  qDebug();
  if(!reader.isMap())
    {
      throw pappso::PappsoException(QObject::tr("this is not a cvParam : no map"));
    }
  reader.enterContainer();
  QString attribute_cvparam;
  while(reader.hasNext())
    {
      reader.decodeString(attribute_cvparam);
      qDebug() << attribute_cvparam;
      if(attribute_cvparam == "cvRef")
        {
          reader.decodeString(cvRef);
        }
      else if(attribute_cvparam == "accession")
        {
          reader.decodeString(accession);
        }
      else if(attribute_cvparam == "name")
        {
          reader.decodeString(name);
        }
      else if(attribute_cvparam == "value")
        {
          cborType = reader.type();
          if(reader.isDouble())
            {
              valueDouble = reader.toDouble();
              reader.next();
            }
          else if(reader.isUnsignedInteger())
            {
              valueInt = reader.toUnsignedInteger();
              reader.next();
            }
          else if(reader.isInteger())
            {
              valueInt = reader.toInteger();
              reader.next();
            }
          else if(reader.type() == QCborStreamReader::Type::String)
            {
              if(reader.decodeString(attribute_cvparam))
                {
                  valueStr = attribute_cvparam;
                }
              else
                {
                  throw pappso::PappsoException(
                    QObject::tr("cvParam value string failed for accession %1").arg(accession));
                }
            }
          else
            {
              throw pappso::PappsoException(
                QObject::tr("cvParam value type not known for accession %1").arg(accession));
            }
        }
      else if(attribute_cvparam == "unitAccession")
        {
          reader.decodeString(unitAccession);
        }

      else if(attribute_cvparam == "unitCvRef")
        {
          reader.decodeString(unitCvRef);
        }
      else if(attribute_cvparam == "unitName")
        {
          reader.decodeString(unitName);
        }
      else
        {
          reader.next();
        }
    }

  reader.leaveContainer();
  qDebug();
}

void
pappso::cbor::mzcbor::CvParam::fromMzml(QXmlStreamReader &reader)
{
  // qDebug();
  for(auto &the_attribute : reader.attributes())
    {
      if(the_attribute.name() == "cvRef")
        {
          cvRef = the_attribute.value().toString();
        }
      else if(the_attribute.name() == "accession")
        {
          accession = the_attribute.value().toString();
        }
      else if(the_attribute.name() == "name")
        {
          name = the_attribute.value().toString();
        }
      else if(the_attribute.name() == "value")
        {
          valueStr = the_attribute.value().toString();
        }
      else if(the_attribute.name() == "unitCvRef")
        {
          unitCvRef = the_attribute.value().toString();
        }
      else if(the_attribute.name() == "unitAccession")
        {
          unitAccession = the_attribute.value().toString();
        }
      else if(the_attribute.name() == "unitName")
        {
          unitName = the_attribute.value().toString();
        }
    }

  reader.readNextStartElement();
  // qDebug();
}

void
pappso::cbor::mzcbor::CvParam::toCbor(CborStreamWriter &writer)
{

  writer.startMap();
  writer.append("cvRef");
  writer.append(cvRef);

  writer.append("accession");
  writer.append(accession);
  writer.append("name");
  writer.append(name);

  writer.append("value");
  xmlValueToCbor(writer, valueStr);

  if(!unitAccession.isEmpty())
    {
      writer.append("unitCvRef");
      writer.append(unitCvRef);
      writer.append("unitAccession");
      writer.append(unitAccession);
      writer.append("unitName");
      writer.append(unitName);
    }
  writer.endMap();
}


QJsonObject
pappso::cbor::mzcbor::CvParam::toJsonObject() const
{
  QJsonObject cv_param;
  cv_param.insert("cvRef", cvRef);
  cv_param.insert("accession", accession);
  cv_param.insert("name", name);


  if(std::isnan(valueDouble))
    {
      if(valueInt == std::numeric_limits<qint64>::max())
        {
          cv_param.insert("value", valueStr);
        }
      else
        {
          cv_param.insert("value", valueInt);
        }
    }
  else
    {
      cv_param.insert("value", valueDouble);
    }

  if(!unitAccession.isEmpty())
    {
      cv_param.insert("unitCvRef", unitCvRef);
      cv_param.insert("unitAccession", unitAccession);
      cv_param.insert("unitName", unitName);
    }
  return cv_param;
}

void
pappso::cbor::mzcbor::CvParam::setValue(const QString &value_str)
{
  valueInt    = 0;
  valueDouble = 0;
  valueStr    = value_str;
}

void
pappso::cbor::mzcbor::CvParam::toMzml(QXmlStreamWriter &writer)
{
  //          <cvParam cvRef="MS" accession="MS:1000514" value="" name="m/z array"
  writer.writeStartElement("cvParam");

  writer.writeAttribute("cvRef", cvRef);
  writer.writeAttribute("accession", accession);


  if(cborType == QCborStreamReader::Type::Double)
    {
      valueStr = QString::number(valueDouble, 'g', 15);
    }
  else if(cborType == QCborStreamReader::Type::UnsignedInteger)
    {
      valueStr = QString("%1").arg(valueInt);
    }
  else if(cborType == QCborStreamReader::Type::NegativeInteger)
    {
      valueStr = QString("%1").arg(valueInt);
    }
  else if(cborType == QCborStreamReader::Type::String)
    {
    }
  writer.writeAttribute("value", valueStr);

  writer.writeAttribute("name", name);
  //          unitAccession="MS:1000040" unitName="m/z" unitCvRef="MS" />
  if(!unitAccession.isEmpty())
    {
      writer.writeAttribute("unitAccession", unitAccession);
      writer.writeAttribute("unitName", unitName);
      writer.writeAttribute("unitCvRef", unitCvRef);
    }

  writer.writeEndElement(); // cvParam
}

std::uint8_t
pappso::cbor::mzcbor::CvParam::getExpectedUint8() const
{
  bool ok(false);
  int i = valueStr.toInt(&ok);
  if(ok)
    {
      return (std::uint8_t)i;
    }
  else
    {
      throw pappso::PappsoException(
        QObject::tr("cvParam value string is not an integer %1 %2").arg(accession).arg(valueStr));
    }
}

qint64
pappso::cbor::mzcbor::CvParam::getExpectedQint64() const
{
  bool ok(false);
  qint64 i = valueStr.toLongLong(&ok);
  if(ok)
    {
      return i;
    }
  else
    {
      throw pappso::PappsoException(
        QObject::tr("cvParam value string is not an integer %1 %2").arg(accession).arg(valueStr));
    }
}

double
pappso::cbor::mzcbor::CvParam::getExpectedDouble() const
{
  bool ok(false);
  double i = valueStr.toDouble(&ok);
  if(ok)
    {
      return i;
    }
  else
    {
      throw pappso::PappsoException(
        QObject::tr("cvParam value string is not a double %1 %2").arg(accession).arg(valueStr));
    }
}


void
pappso::cbor::mzcbor::CvParam::xmlValueToCbor(CborStreamWriter &writer,
                                              const QStringView &value_str)
{
  bool ok(false);
  double d = value_str.toDouble(&ok);
  if(ok)
    {
      if(value_str.contains('.'))
        {
          cborType = QCborStreamReader::Type::Double;
          writer.append(d);
        }
      else
        {
          qint64 bigint = value_str.toLongLong(&ok);
          if(ok)
            {
              cborType = QCborStreamReader::Type::NegativeInteger;
              writer.append(bigint);
            }
        }
    }
  else
    {
      cborType = QCborStreamReader::Type::String;
      writer.append(value_str);
    }
}
