/**
 * \file pappsomspp/processing/filters/filterchargedeconvolution.h
 * \date 30/09/2020
 * \author Thomas Renne
 * \brief Sum peaks and transform mz to fit charge = 1
 */

/*******************************************************************************
 * Copyright (c) 2020 Olivier Langella <Olivier.Langella@u-psud.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/>.
 *
 ******************************************************************************/

#pragma once

#include "filternameinterface.h"

#include "../../trace/trace.h"

/**
 * @brief tries to keep as much as possible monoisotopes, removing any possible
 * C13 peaks and changes multicharge peaks to monocharge
 */

namespace pappso
{

class PMSPP_LIB_DECL FilterChargeDeconvolution : public FilterNameInterface
{
  private:
  struct DataPointInfo;
  typedef std::shared_ptr<DataPointInfo> DataPointInfoSp;
  struct DataPointInfo
  {
    // peak charge
    int z_charge = -1;
    // datapoint get from the raw data
    DataPoint data_point;
    // new data point with new intensity and mz
    DataPoint new_mono_charge_data_point;
    // range of mass possible for z1 peak (~1mz)
    std::pair<double, double> z1_range;
    // range of mass possible for z2 peak (~0.5mz)
    std::pair<double, double> z2_range;
    // datapointInfo where the mass is in the range
    std::weak_ptr<DataPointInfo> parent;
    // list of datapointinfo with 1mz diff multiple
    std::vector<std::weak_ptr<DataPointInfo>> z1_vect;
    // list of datapointinfo with 2mz diff multiple
    std::vector<std::weak_ptr<DataPointInfo>> z2_vect;
  };

  public:
  /**
   * Default constructor
   */
  FilterChargeDeconvolution(PrecisionPtr precision_ptr);


  /**
   * @param strBuildParams string to build the filter
   * "chargeDeconvolution|0.02dalton"
   */
  FilterChargeDeconvolution(const QString &strBuildParams);

  /**
   * Copy constructor
   *
   * @param other TODO
   */
  FilterChargeDeconvolution(const FilterChargeDeconvolution &other);

  /**
   * Destructor
   */
  virtual ~FilterChargeDeconvolution();

  /**
   * @brief get all the datapoints and remove different isotope and add their
   * intensity and change to charge = 1 when the charge is known
   * @return a list of datapoint
   */
  Trace &filter(Trace &data_points) const override;


  virtual QString name() const override;
  QString toString() const override;

  protected:
  void buildFilterFromString(const QString &strBuildParams) override;

  private:
  /**
   * @brief Add each datapoint to a vector of structure describe above
   * @return the vecotr of datapointInfo with their datapoint and their
   * exclusion mass range
   */
  void addDataPointToList(
    std::vector<FilterChargeDeconvolution::DataPointInfoSp> &points,
    DataPoint &data_point) const;
  /**
   * @brief For each datapointInfo add the datapoint to the lists by their
   * exclusion range
   * @return the vecotr of datapointInfo with their lists of isotopics peaks
   */
  void addDataPointRefByExclusion(
    std::vector<FilterChargeDeconvolution::DataPointInfoSp> &points,
    FilterChargeDeconvolution::DataPointInfoSp &new_dpi) const;
  /**
   * @brief Compare both list (z1 and z2) and add the right level of charge
   * @return the vecotr of datapointInfo with their charge
   */
  void computeBestChargeOfDataPoint(
    std::vector<FilterChargeDeconvolution::DataPointInfoSp> &data_points_info)
    const;
  /**
   * @brief For eache datapointInfo whith no parent copy info in new vector with
   * the intensity of the monoistipics peaks added
   * @return the vecotr of datapointInfo with their intensity merged
   */
  void computeIsotopeDeconvolution(
    std::vector<FilterChargeDeconvolution::DataPointInfoSp> &data_points_info)
    const;
  /**
   * @brief For eache datapointInfo with a charge = 2 transform the peak to a
   * charge = 1 by multiplying the mz by 2 and remove 1 H
   * @return the vecotr of datapointInfo their z corrected
   */
  void transformToMonoChargedForAllDataPoint(
    std::vector<FilterChargeDeconvolution::DataPointInfoSp> &data_points_info)
    const;

  private:
  double m_diffC12C13_z1;
  double m_diffC12C13_z2;
  PrecisionPtr m_precisionPtrZ1;
  PrecisionPtr m_precisionPtrZ2;
};
} // namespace pappso
