/**
 * \file pappsomspp/processing/project/projectparameters.cpp
 * \date 16/03/2024
 * \author Olivier Langella
 * \brief simple structure to store project parameters (identification,
 * filtering, quantification)
 */

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

#include "projectparameters.h"
#include <QDateTime>
#include <QUrl>
#include "pappsomspp/core/exception/exceptionnotfound.h"
#include <QDebug>
#include <QJsonValue>


pappso::ProjectParameters::ProjectParameters()
{
}

pappso::ProjectParameters::ProjectParameters(const QJsonObject &json_parameters)
{

  qDebug() << json_parameters.keys();
  pappso::ProjectParam project_parameter;
  auto itparam = json_parameters.begin();
  while(itparam != json_parameters.end())
    {
      qDebug() << itparam.key();
      project_parameter.name = itparam.key();
      project_parameter.category =
        (pappso::ProjectParamCategory)itparam.value().toObject().value("category").toInt();
      project_parameter.value = itparam.value().toObject().value("value").toVariant();

      setProjectParam(project_parameter);
      itparam++;
    }
}


pappso::ProjectParameters::ProjectParameters(const ProjectParameters &other)
{
  m_mapGeneralParameters        = other.m_mapGeneralParameters;
  m_mapFilterParameters         = other.m_mapFilterParameters;
  m_mapIdentificationParameters = other.m_mapIdentificationParameters;
  m_mapQuantificationParameters = other.m_mapQuantificationParameters;
  m_mapInstrumentParameters     = other.m_mapInstrumentParameters;
}

pappso::ProjectParameters::~ProjectParameters()
{
}

pappso::ProjectParameters &
pappso::ProjectParameters::operator=(const ProjectParameters &other)
{

  m_mapGeneralParameters        = other.m_mapGeneralParameters;
  m_mapFilterParameters         = other.m_mapFilterParameters;
  m_mapIdentificationParameters = other.m_mapIdentificationParameters;
  m_mapQuantificationParameters = other.m_mapQuantificationParameters;
  m_mapInstrumentParameters     = other.m_mapInstrumentParameters;

  return *this;
}


void
pappso::ProjectParameters::writeCellVariant(CalcWriterInterface &writer, const QVariant &var) const
{
  int type = var.metaType().id();
  switch(type)
    {
      case QMetaType::Void:
        writer.writeEmptyCell();
        break;
      case QMetaType::Bool:
        writer.writeCell(var.toBool());
        break;
      case QMetaType::Double:
      case QMetaType::Float:
        writer.writeCell(var.toDouble());
        break;
      case QMetaType::Int:
        writer.writeCell(var.toInt());
        break;
      case QMetaType::QDate:
        writer.writeCell(var.toDate());
        break;
      case QMetaType::QDateTime:
        writer.writeCell(var.toDateTime());
        break;
      case QMetaType::QString:
        writer.writeCell(var.toString());
        break;
      case QMetaType::QUrl:
        writer.writeCell(var.toUrl(), var.toUrl().toString());
        break;
      default:
        writer.writeCell(var.toString());
    }
}

void
pappso::ProjectParameters::writeXmlParameter(QXmlStreamWriter &writer,
                                             const ProjectParam &param) const
{

  writer.writeStartElement("project_param");
  writer.writeAttribute("category", QString("%1").arg((std::uint8_t)param.category));
  writer.writeAttribute("name", param.name);
  writeXmlVariant(writer, param.value);
  writer.writeEndElement();
}


void
pappso::ProjectParameters::writeParameters(QXmlStreamWriter &xml_writer) const
{

  for(auto param_entry : m_mapGeneralParameters)
    {
      writeXmlParameter(xml_writer, param_entry.second);
    }

  for(auto param_entry : m_mapInstrumentParameters)
    {
      writeXmlParameter(xml_writer, param_entry.second);
    }
  for(auto param_entry : m_mapIdentificationParameters)
    {
      writeXmlParameter(xml_writer, param_entry.second);
    }

  for(auto param_entry : m_mapFilterParameters)
    {
      writeXmlParameter(xml_writer, param_entry.second);
    }
  for(auto param_entry : m_mapQuantificationParameters)
    {
      writeXmlParameter(xml_writer, param_entry.second);
    }
}

void
pappso::ProjectParameters::writeXmlVariant(QXmlStreamWriter &writer, const QVariant &var) const
{
  int type = var.metaType().id();
  QString value;
  switch(type)
    {
      case QMetaType::Void:
        value = "";
        break;
      case QMetaType::Bool:
        value = "false";
        if(var.toBool())
          value = "true";
        break;
      case QMetaType::Double:
      case QMetaType::Float:
        value = QString::number(var.toDouble(), 'g', 10);
        break;
      case QMetaType::Int:
        value = QString::number(var.toInt());
        break;
      case QMetaType::QDate:
        value = var.toDate().toString(Qt::ISODate);
        break;
      case QMetaType::QDateTime:
        value = var.toDateTime().toString(Qt::ISODate);
        break;
      case QMetaType::QString:
        value = var.toString();
        break;
      case QMetaType::QUrl:
        value = var.toUrl().toString();
        break;
      default:
        value = var.toString();
    }

  writer.writeAttribute("value", value);
}

void
pappso::ProjectParameters::writeParameters(CalcWriterInterface &writer) const
{
  writer.writeSheet("Project parameters");

  for(auto param_entry : m_mapGeneralParameters)
    {
      writer.writeCell(param_entry.second.name);
      writeCellVariant(writer, param_entry.second.value);
      writer.writeLine();
    }

  for(auto param_entry : m_mapInstrumentParameters)
    {
      writer.writeCell(param_entry.second.name);
      writeCellVariant(writer, param_entry.second.value);
      writer.writeLine();
    }
  for(auto param_entry : m_mapIdentificationParameters)
    {
      writer.writeCell(param_entry.second.name);
      writeCellVariant(writer, param_entry.second.value);
      writer.writeLine();
    }
  for(auto param_entry : m_mapFilterParameters)
    {
      writer.writeCell(param_entry.second.name);
      writeCellVariant(writer, param_entry.second.value);
      writer.writeLine();
    }
  for(auto param_entry : m_mapQuantificationParameters)
    {
      writer.writeCell(param_entry.second.name);
      writeCellVariant(writer, param_entry.second.value);
      writer.writeLine();
    }
}

void
pappso::ProjectParameters::setProjectParam(const ProjectParam &param)
{
  switch(param.category)
    {
      case ProjectParamCategory::general:
        m_mapGeneralParameters[param.name] = param;
        break;
      case ProjectParamCategory::instrument:
        m_mapInstrumentParameters[param.name] = param;
        break;
      case ProjectParamCategory::filter:
        m_mapFilterParameters[param.name] = param;
        break;
      case ProjectParamCategory::identification:
        m_mapIdentificationParameters[param.name] = param;
        break;
      case ProjectParamCategory::quantification:
        m_mapQuantificationParameters[param.name] = param;
        break;
      default:
        break;
    }
}

void
pappso::ProjectParameters::merge(const ProjectParameters &parameters)
{

  for(auto key_value : parameters.m_mapGeneralParameters)
    {
      m_mapGeneralParameters[key_value.first] = key_value.second;
    }
  for(auto key_value : parameters.m_mapInstrumentParameters)
    {
      m_mapInstrumentParameters[key_value.first] = key_value.second;
    }
  for(auto key_value : parameters.m_mapFilterParameters)
    {
      m_mapFilterParameters[key_value.first] = key_value.second;
    }

  for(auto key_value : parameters.m_mapIdentificationParameters)
    {
      m_mapIdentificationParameters[key_value.first] = key_value.second;
    }

  for(auto key_value : parameters.m_mapQuantificationParameters)
    {
      m_mapQuantificationParameters[key_value.first] = key_value.second;
    }
}

std::size_t
pappso::ProjectParameters::size() const
{
  return (m_mapGeneralParameters.size() + m_mapInstrumentParameters.size() +
          m_mapFilterParameters.size() + m_mapIdentificationParameters.size() +
          m_mapQuantificationParameters.size());
}

const QVariant
pappso::ProjectParameters::getValue(ProjectParamCategory category, const QString &name) const
{
  const std::map<QString, ProjectParam> *map_pointer = nullptr;
  switch(category)
    {
      case ProjectParamCategory::general:
        map_pointer = &m_mapGeneralParameters;
        break;
      case ProjectParamCategory::instrument:
        map_pointer = &m_mapInstrumentParameters;
        break;
      case ProjectParamCategory::filter:
        map_pointer = &m_mapFilterParameters;
        break;
      case ProjectParamCategory::identification:
        map_pointer = &m_mapIdentificationParameters;
        break;
      case ProjectParamCategory::quantification:
        map_pointer = &m_mapQuantificationParameters;
        break;
      default:
        map_pointer = &m_mapGeneralParameters;
        break;
    }

  auto it = map_pointer->find(name);
  if(it != map_pointer->end())
    {
      return (it->second.value);
    }
  else
    {
      qDebug() << QObject::tr("project parameter named %1 in category %2 not found")
                    .arg(name)
                    .arg((std::int8_t)category);
    }
  return (QVariant());
}

std::vector<pappso::ProjectParam>
pappso::ProjectParameters::getProjectParamListByCategory(ProjectParamCategory category) const
{
  std::vector<ProjectParam> param_list;
  const std::map<QString, ProjectParam> *map_pointer = nullptr;
  switch(category)
    {
      case ProjectParamCategory::general:
        map_pointer = &m_mapGeneralParameters;
        break;
      case ProjectParamCategory::instrument:
        map_pointer = &m_mapInstrumentParameters;
        break;
      case ProjectParamCategory::filter:
        map_pointer = &m_mapFilterParameters;
        break;
      case ProjectParamCategory::identification:
        map_pointer = &m_mapIdentificationParameters;
        break;
      case ProjectParamCategory::quantification:
        map_pointer = &m_mapQuantificationParameters;
        break;
      default:
        map_pointer = &m_mapGeneralParameters;
        break;
    }

  for(auto pair_item : *map_pointer)
    {
      param_list.push_back(pair_item.second);
    }

  return param_list;
}

QJsonObject
pappso::ProjectParameters::toJsonObject() const
{
  QJsonObject parameters;

  for(auto param_entry : m_mapGeneralParameters)
    {
      parameters.insert(param_entry.first, param_entry.second.toJsonObject());
    }
  for(auto param_entry : m_mapInstrumentParameters)
    {
      parameters.insert(param_entry.first, param_entry.second.toJsonObject());
    }
  for(auto param_entry : m_mapIdentificationParameters)
    {
      parameters.insert(param_entry.first, param_entry.second.toJsonObject());
    }
  for(auto param_entry : m_mapFilterParameters)
    {
      parameters.insert(param_entry.first, param_entry.second.toJsonObject());
    }
  for(auto param_entry : m_mapQuantificationParameters)
    {
      parameters.insert(param_entry.first, param_entry.second.toJsonObject());
    }
  return parameters;
}


QJsonObject
pappso::ProjectParam::toJsonObject() const
{

  QJsonObject param;
  param.insert("category", (int)category);

  int type = value.metaType().id();
  switch(type)
    {
      case QMetaType::Void:
        param.insert("value", "");
        break;
      case QMetaType::Bool:
        param.insert("value", value.toBool());
        break;
      case QMetaType::Double:
      case QMetaType::Float:
        param.insert("value", value.toDouble());
        break;
      case QMetaType::Int:
        param.insert("value", value.toInt());
        break;
      case QMetaType::QDate:

        param.insert("value", value.toDate().toString(Qt::ISODate));
        break;
      case QMetaType::QDateTime:

        param.insert("value", value.toDateTime().toString(Qt::ISODate));
        break;
      case QMetaType::QString:
        param.insert("value", value.toString());
        break;
      case QMetaType::QUrl:

        param.insert("value", value.toString());
        break;
      default:
        param.insert("value", value.toString());
    }
  return param;
}

void
pappso::ProjectParameters::writeParameters(QCborStreamWriter &cbor_writer) const
{
  cbor_writer.append("project_parameters");

  QCborValue::fromJsonValue(toJsonObject()).toCbor(cbor_writer);
}
