/**
 * \file /output/masschroqjson.h
 * \date 04/02/2025
 * \author Olivier Langella
 * \brief JSON file for MassChroQ
 */

/*******************************************************************************
 * Copyright (c) 2025 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 "masschroqjson.h"
#include "../core/identificationgroup.h"
#include <pappsomspp/core/pappsoexception.h>
#include <QJsonArray>


MassChroQjson::MassChroQjson(const ProjectSp &sp_project)
{

  mp_labelingMethod = sp_project.get()->getLabelingMethodSp().get();

  pappso::ProjectParameters params = sp_project.get()->getProjectParameters();
  m_jsonProjectParameters          = params.toJsonObject();

  m_jsonQuantificationMethod = sp_project.get()
                                 ->getMasschroqFileParametersSp()
                                 .get()
                                 ->m_quantificationMethod.getJsonObject();

  m_jsonAlignmentMethod = sp_project.get()
                            ->getMasschroqFileParametersSp()
                            .get()
                            ->m_alignmentMethod.getJsonObject();


  std::vector<MsRunSp> msrun_list =
    sp_project.get()->getMsRunStore().getMsRunList();
  for(MsRunSp &msrun : msrun_list)
    {
      QJsonObject jmsrun;
      jmsrun.insert("file", msrun.get()->getFileName());
      m_jsonMsRunList.insert(msrun.get()->getXmlId(), jmsrun);
    }


  std::size_t nb_group = sp_project.get()->getIdentificationGroupList().size();
  if(nb_group == 1)
    {

      const IdentificationGroup *identification_group_p =
        sp_project.get()->getIdentificationGroupList().at(0);
      for(ProteinMatch *p_protein_match :
          identification_group_p->getProteinMatchList())
        {
          if(!p_protein_match->isGrouped())
            continue;
          //<protein desc="conta|P02769|ALBU_BOVIN SERUM ALBUMIN PRECURSOR."
          //	id="P1.1" />
          pappso::GrpProteinSp grp_protein = p_protein_match->getGrpProteinSp();
          if(grp_protein.get()->getRank() == 1)
            {
              QStringList list;
              list << p_protein_match->getProteinXtpSp().get()->getAccession();
              list
                << p_protein_match->getProteinXtpSp().get()->getDescription();

              QJsonObject protein;
              protein.insert("description", list.join(" "));
              if(!p_protein_match->getProteinXtpSp()
                    .get()
                    ->getSequence()
                    .isEmpty())
                {
                  protein.insert(
                    "sequence",
                    p_protein_match->getProteinXtpSp().get()->getSequence());
                }
              m_jsonProteinList.insert(grp_protein.get()->getGroupingId(),
                                       protein);
            }
        }
      //</protein_list>

      writeOneGroupStore(identification_group_p->getGroupStore());
    }
  else
    {
      throw pappso::PappsoException(QObject::tr(
        "sp_project.get()->getIdentificationGroupList().size() != 1"));
    }


  writeActions(*sp_project.get()->getMasschroqFileParametersSp().get());
}

MassChroQjson::~MassChroQjson()
{
}

void
MassChroQjson::writeOneGroupStore(const GroupStore &group_store)
{


  const std::map<unsigned int, GroupingGroupSp> &group_map =
    group_store.getGroupMap();

  for(auto &group_pair : group_map)
    {
      writePeptideListInGroupingGroup(*group_pair.second.get());
    }

  for(auto msrun_pair : m_mapMsrun2peptideObservation)
    {
      QJsonObject jmsrun;
      QString msrun = msrun_pair.first->getXmlId();

      QString current_peptide;
      QJsonArray jallobservation;
      for(auto &observation : msrun_pair.second)
        {
          if(!current_peptide.isEmpty())
            {
              if(observation.peptide_id != current_peptide)
                {

                  jmsrun.insert(current_peptide, jallobservation);
                  jallobservation = QJsonArray();
                }
            }
          current_peptide = observation.peptide_id;
          QJsonObject jobservation;
          if(observation.is_spectrumIndex)
            {
              jobservation.insert("index", (qint64)observation.scan);
            }
          else
            {
              jobservation.insert("scan", (qint64)observation.scan);
            }
          if(observation.label_p != nullptr)
            {
              jobservation.insert("label", observation.label_p->getXmlId());
            }

          QJsonObject jprecursor;
          jprecursor.insert("charge", (int)observation.charge);
          /*
           *
                "scan_index": 2345,
                "label": "light",
                "precursor": {
                    "charge": 2,
                    "mz": 2456.45,
                    "intensity": 4580,
                    "rt": 345.67,
                  }*/
          jobservation.insert("precursor", jprecursor);
          jallobservation.append(jobservation);
        }

      jmsrun.insert(current_peptide, jallobservation);

      QJsonObject jmsrun_observation;
      jmsrun_observation.insert("peptide_obs", jmsrun);
      m_jsonMsRunPeptideList.insert(msrun, jmsrun_observation);
    }
}
void
MassChroQjson::writePeptideListInGroupingGroup(
  const GroupingGroup &grouping_group)
{

  const std::vector<std::pair<unsigned int, const PeptideEvidence *>>
    &sg_peptide_evidence_list =
      grouping_group.getPairSgNumberPeptideEvidenceList();
  std::vector<McqPeptide> mcq_peptide_list;

  for(auto &sg_peptide_pair : sg_peptide_evidence_list)
    {
      unsigned int sg_number                  = sg_peptide_pair.first;
      const PeptideEvidence *peptide_evidence = sg_peptide_pair.second;

      McqPeptide mcq_peptide;
      mcq_peptide.id =
        peptide_evidence->getGrpPeptideSp().get()->getGroupingId();
      mcq_peptide.mods
        << QString("[%1] %2")
             .arg(peptide_evidence->getPeptideXtpSp().get()->getFormula(1))
             .arg(
               peptide_evidence->getPeptideXtpSp().get()->getModifString(true));
      mcq_peptide.prot_ids << grouping_group.getProteinGroupingIdOfSubgroup(
        sg_number);
      mcq_peptide.seq =
        peptide_evidence->getPeptideXtpSp().get()->getSequence();
      if(peptide_evidence->getPeptideXtpSp().get()->getLabel() != nullptr)
        {
          mcq_peptide.map_label.insert(
            {peptide_evidence->getPeptideXtpSp().get()->getLabel(),
             peptide_evidence->getPeptideXtpSp().get()->toProForma()});
        }
      mcq_peptide.native_peptide =
        peptide_evidence->getPeptideXtpSp().get()->getNativePeptideP();
      McqObserved observed = {
        mcq_peptide.id,
        peptide_evidence->getMsRunP(),
        peptide_evidence->getScanNumber(),
        peptide_evidence->getCharge(),
        peptide_evidence->isSpectrumIndex(),
        peptide_evidence->getPeptideXtpSp().get()->getLabel()};
      mcq_peptide.observed_in.push_back(observed);
      mcq_peptide_list.push_back(mcq_peptide);
    }

  // sort list
  std::sort(mcq_peptide_list.begin(),
            mcq_peptide_list.end(),
            [](const McqPeptide &first, const McqPeptide &second) {
              return (first.id < second.id);
            });

  std::vector<McqPeptide> cumul_mcq_peptide_list;
  // cumul same peptide id together
  if(mcq_peptide_list.size() > 0)
    {
      McqPeptide cumul = mcq_peptide_list[0];
      for(McqPeptide &mcq_peptide : mcq_peptide_list)
        {
          if(cumul.id == mcq_peptide.id)
            {


              if(cumul.observed_in.end() ==
                 std::find(cumul.observed_in.begin(),
                           cumul.observed_in.end(),
                           mcq_peptide.observed_in[0]))
                {
                  cumul.observed_in.push_back(mcq_peptide.observed_in[0]);
                }
              if(!cumul.prot_ids.contains(mcq_peptide.prot_ids[0]))
                {
                  cumul.prot_ids << mcq_peptide.prot_ids[0];
                }
              if(!cumul.mods.contains(mcq_peptide.mods[0]))
                {
                  cumul.mods << mcq_peptide.mods[0];
                }
              cumul.map_label.insert(mcq_peptide.map_label.begin(),
                                     mcq_peptide.map_label.end());
            }
          else
            {
              cumul_mcq_peptide_list.push_back(cumul);
              cumul = mcq_peptide;
            }
        }
      cumul_mcq_peptide_list.push_back(cumul);
    }


  // sort list
  std::sort(cumul_mcq_peptide_list.begin(),
            cumul_mcq_peptide_list.end(),
            [](const McqPeptide &first, const McqPeptide &second) {
              if(first.id != second.id)
                return (first.id < second.id);
              return (first.native_peptide < second.native_peptide);
            });

  // cumul same native peptide together (different labels of the same peptide,
  // the same masschroq entry)
  std::vector<McqPeptide> cumul_witout_label_mcq_peptide_list;
  if(cumul_mcq_peptide_list.size() > 0)
    {
      auto it          = cumul_mcq_peptide_list.begin();
      McqPeptide cumul = *it;
      it++;
      while(it != cumul_mcq_peptide_list.end())
        {
          if(cumul.native_peptide == it->native_peptide)
            {

              for(auto observation : it->observed_in)
                {
                  if(cumul.observed_in.end() ==
                     std::find(cumul.observed_in.begin(),
                               cumul.observed_in.end(),
                               observation))
                    {
                      cumul.observed_in.push_back(observation);
                    }
                }
              for(auto protein_id : it->prot_ids)
                {
                  if(!cumul.prot_ids.contains(protein_id))
                    {
                      cumul.prot_ids << protein_id;
                    }
                }

              cumul.map_label.insert(it->map_label.begin(),
                                     it->map_label.end());
            }
          else
            {
              cumul_witout_label_mcq_peptide_list.push_back(cumul);
              cumul = *it;
            }
          it++;
        }
      cumul_witout_label_mcq_peptide_list.push_back(cumul);
    }

  // sort list
  std::sort(cumul_witout_label_mcq_peptide_list.begin(),
            cumul_witout_label_mcq_peptide_list.end(),
            [](const McqPeptide &first, const McqPeptide &second) {
              return (first.id < second.id);
            });


  for(McqPeptide mcq_peptide : cumul_witout_label_mcq_peptide_list)
    {

      for(auto &observation : mcq_peptide.observed_in)
        {
          auto it = m_mapMsrun2peptideObservation.insert(
            {observation.msrun_p, std::vector<McqObserved>()});
          it.first->second.push_back(observation);
        }

      QJsonObject json_peptide;
      json_peptide.insert("proforma", mcq_peptide.native_peptide->toProForma());
      json_peptide.insert("mods", mcq_peptide.mods.join("|"));

      QJsonArray protein_array;
      for(const QString &prot_id : mcq_peptide.prot_ids)
        {
          protein_array.append(prot_id);
        }

      json_peptide.insert("proteins", protein_array);
      /*
          "pepa1a1": {
              "proforma": "SLTNDWEDHLAVK",
              "proteins": ["prota1a1"],
              "label_list": { "light": {"proforma": "SLTNDWEDHLAVK"}
              }
              */
      if(mp_labelingMethod != nullptr)
        {
          QJsonObject jlabellist;

          for(const auto &label_p : mp_labelingMethod->getLabelList())
            {
              pappso::PeptideSp label_peptide_sp =
                label_p->getLLabeledPeptideSp(mcq_peptide.native_peptide);
              QJsonObject jlabel;
              jlabel.insert("proforma", label_peptide_sp.get()->toProForma());
              jlabellist.insert(label_p->getXmlId(), jlabel);
            }
          json_peptide.insert("label_list", jlabellist);
        }
      m_jsonPeptideList.insert(mcq_peptide.id, json_peptide);
    }
}

void
MassChroQjson::writeActions(const MasschroqFileParameters &mcq_params)
{
  /*
   *
"action": {
    "group_list": { "g1": ["msruna1","msruna2","msruna3"]
    },
    "align_group": {"g1": {
        "alignment_reference":  "msruna1"
    }}
}*/
  // action.insert("group_list", getJsonGroupList());

  QJsonObject jgroup_list;
  for(MsRunAlignmentGroupSp group_sp : mcq_params.alignment_groups)
    {
      QJsonArray jmsrun_list;
      for(auto &one_msrun_sp : group_sp.get()->getMsRunsInAlignmentGroup())
        {
          jmsrun_list.append(one_msrun_sp.get()->getXmlId());
        }

      jgroup_list.insert(group_sp->getMsRunAlignmentGroupName(), jmsrun_list);
    }
  m_jsonActions.insert("group_list", jgroup_list);


  QJsonObject jalign_group;
  for(MsRunAlignmentGroupSp group_sp : mcq_params.alignment_groups)
    {
      QJsonObject msrun_ref;
      msrun_ref.insert("alignment_reference",
                       group_sp.get()->getMsRunReference().get()->getXmlId());
      jalign_group.insert(group_sp->getMsRunAlignmentGroupName(), msrun_ref);
    }
  m_jsonActions.insert("align_group", jalign_group);
  // action.insert("align_group", getJsonQuantifyGroup());
  m_jsonActions.insert("quantify_all", "true");
}

void
MassChroQjson::populateJsonDocument(QJsonDocument &document) const
{
  QJsonObject root;
  QJsonObject identification_data;


  identification_data.insert("msrun_list", m_jsonMsRunList);
  identification_data.insert("peptide_list", m_jsonPeptideList);
  identification_data.insert("msrunpeptide_list", m_jsonMsRunPeptideList);
  identification_data.insert("protein_list", m_jsonProteinList);
  root.insert("project_parameters", m_jsonProjectParameters);
  root.insert("identification_data", identification_data);

  QJsonObject methods;
  methods.insert("alignment_method", m_jsonAlignmentMethod);

  methods.insert("quantification_method", m_jsonQuantificationMethod);

  root.insert("masschroq_methods", methods);


  root.insert("actions", m_jsonActions);
  document.setObject(root);
}
