/**
 * \file pappsomspp/msrun/xiccoord/ionmobilitygrid.cpp
 * \date 26/01/2023
 * \author Olivier Langella
 * \brief store observed ion mobility coordinates differences between MS runs
 */


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

#include "ionmobilitygrid.h"
#include <QVariant>
#include "xiccoordtims.h"
#include "pappsomspp/core/exception/exceptionnotpossible.h"

using namespace pappso;

IonMobilityGrid::IonMobilityGrid()
{
}

IonMobilityGrid::~IonMobilityGrid()
{
}

void
pappso::IonMobilityGrid::storeObservedIdentityBetween(const pappso::MsRunId &msrun_ida,
                                                      const pappso::XicCoord *xic_coorda,
                                                      const pappso::MsRunId &msrun_idb,
                                                      const pappso::XicCoord *xic_coordb)
{
  if(msrun_ida == msrun_idb)
    return;
  QString msrun_key(QString("%1-%2").arg(msrun_ida.getXmlId()).arg(msrun_idb.getXmlId()));


  if(msrun_ida.getXmlId() > msrun_idb.getXmlId())
    {
      msrun_key = QString("%1-%2").arg(msrun_idb.getXmlId()).arg(msrun_ida.getXmlId());
      std::swap(xic_coorda, xic_coordb);
    }

  auto it_start = m_mapDiferrencesStart.insert({msrun_key, std::vector<qint64>()});
  const QVariant &ref_variantb =
    xic_coordb->getParam(XicCoordParam::TimsTofIonMobilityScanNumberStart);
  if(ref_variantb.isNull())
    {
      throw pappso::ExceptionNotPossible(QObject::tr(
        "XicCoordParam::TimsTofIonMobilityScanNumberStart QVariant is null for xic_coordb"));
    }
  const QVariant &ref_varianta =
    xic_coorda->getParam(XicCoordParam::TimsTofIonMobilityScanNumberStart);

  if(ref_varianta.isNull())
    {
      throw pappso::ExceptionNotPossible(QObject::tr(
        "XicCoordParam::TimsTofIonMobilityScanNumberStart QVariant is null for xic_coorda"));
    }
  it_start.first->second.push_back(ref_variantb.toLongLong() - ref_varianta.toLongLong());


  auto it_stop = m_mapDiferrencesStop.insert({msrun_key, std::vector<qint64>()});
  it_stop.first->second.push_back(
    xic_coordb->getParam(XicCoordParam::TimsTofIonMobilityScanNumberStop).toLongLong() -
    xic_coorda->getParam(XicCoordParam::TimsTofIonMobilityScanNumberStop).toLongLong());
}

void
pappso::IonMobilityGrid::computeCorrections()
{
  for(auto pair_key_start_vector : m_mapDiferrencesStart)
    {

      if(pair_key_start_vector.second.size() > 5)
        {
          // median
          const auto middleItr =
            pair_key_start_vector.second.begin() + (pair_key_start_vector.second.size() / 2);
          std::nth_element(
            pair_key_start_vector.second.begin(), middleItr, pair_key_start_vector.second.end());
          m_mapCorrectionsStart[pair_key_start_vector.first] = *middleItr;
        }
      else
        {
          m_mapCorrectionsStart[pair_key_start_vector.first] = 0;
        }
    }
  m_mapDiferrencesStart.clear();
  for(auto pair_key_stop_vector : m_mapDiferrencesStop)
    {

      if(pair_key_stop_vector.second.size() > 5)
        {
          // median
          const auto middleItr =
            pair_key_stop_vector.second.begin() + (pair_key_stop_vector.second.size() / 2);
          std::nth_element(
            pair_key_stop_vector.second.begin(), middleItr, pair_key_stop_vector.second.end());
          m_mapCorrectionsStop[pair_key_stop_vector.first] = *middleItr;
        }
      else
        {
          m_mapCorrectionsStop[pair_key_stop_vector.first] = 0;
        }
    }
  m_mapDiferrencesStop.clear();
}

pappso::XicCoordSPtr
pappso::IonMobilityGrid::translateXicCoordFromTo(const pappso::XicCoord &source_xic_coord,
                                                 const pappso::MsRunId &source_msrunid,
                                                 const pappso::MsRunId &target_msrunid) const
{
  if(m_mapCorrectionsStop.size() == 0)
    {
      return source_xic_coord.initializeAndClone();
    }

  bool opposed = false;
  QString msrun_key(QString("%1-%2").arg(source_msrunid.getXmlId()).arg(target_msrunid.getXmlId()));


  if(source_msrunid.getXmlId() > target_msrunid.getXmlId())
    {
      msrun_key = QString("%1-%2").arg(target_msrunid.getXmlId()).arg(source_msrunid.getXmlId());
      opposed   = true;
    }
  auto itstart   = m_mapCorrectionsStart.find(msrun_key);
  long start_dev = 0;
  if(itstart != m_mapCorrectionsStart.end())
    {
      start_dev = itstart->second;
      if(opposed)
        {
          start_dev *= -1;
        }
    }

  auto itstop   = m_mapCorrectionsStop.find(msrun_key);
  long stop_dev = 0;
  if(itstop != m_mapCorrectionsStop.end())
    {
      stop_dev = itstop->second;
      if(opposed)
        {
          stop_dev *= -1;
        }
    }

  pappso::XicCoordSPtr result_xic_coord_sp = source_xic_coord.initializeAndClone();
  XicCoordTims *tims_coord                 = static_cast<XicCoordTims *>(result_xic_coord_sp.get());

  tims_coord->scanNumBeginRangeCorrection(start_dev, stop_dev);

  return result_xic_coord_sp;
}

const std::map<QString, std::vector<qint64>> &
pappso::IonMobilityGrid::getMapDiferrencesStart() const
{
  return m_mapDiferrencesStart;
}

const std::map<QString, long> &
pappso::IonMobilityGrid::getMapCorrectionsStart() const
{
  return m_mapCorrectionsStart;
}
