/* BEGIN software license
 *
 * MsXpertSuite - mass spectrometry software suite
 * -----------------------------------------------
 * Copyright(C) 2009,...,2018 Filippo Rusconi
 *
 * http://www.msxpertsuite.org
 *
 * This file is part of the MsXpertSuite project.
 *
 * The MsXpertSuite project is the successor of the massXpert project. This
 * project now includes various independent modules:
 *
 * - massXpert, model polymer chemistries and simulate mass spectrometric data;
 * - mineXpert, a powerful TIC chromatogram/mass spectrum viewer/miner;
 *
 * This program 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.
 *
 * This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
 *
 * END software license
 */

#pragma once


/////////////////////// Local includes
#include "exportimportconfig.h"
#include "Sequence.hpp"
#include "Ponderable.hpp"
#include "Ionizable.hpp"
#include "CalcOptions.hpp"
#include "IonizeRule.hpp"
#include "Monomer.hpp"
#include "Coordinates.hpp"
#include "CrossLink.hpp"


namespace MsXpS
{

namespace libXpertMass
{


class Polymer;


//! The Oligomer class provides an oligomer.
/*! An oligomer is a stretch of monomers belonging to a polymer. It is
  characterized by: \                         \

  \li The polymer(a pointer to a Polymer) in which it spans a
  given region;

  \li An index(integer) at which the region starts in the polymer sequence;

  \li An index(integer) at which the region stops in the polymer
  sequence

  The start index cannot be less than 0 and greater than the size of
  the polymer minus one, and the end index follows the same rule.

  Derived from Ponderable, an oligomer is also characterized by a
  monoisotopic mass and an average mass.

  All computations about an oligomer(fragmentation, composition, for
  example, isoelectric point, ...) can only be performed by referring
  to the sequence of its "enclosing" polymer. Therefore, an oligomer
  should never exist after the destruction of its enclosing polymer.
  */
class DECLSPEC Oligomer : public Sequence,
                 public CoordinateList,
                 public Ionizable,
                 public PropListHolder
{
  using Ionizable::operator=;
 using PropListHolder::operator=;

  public:
  Oligomer(Polymer *polymer,
           const QString &name,
           const QString &description,
           bool modified,
           const Ponderable &ponderable,
           const IonizeRule &ionizeRule,
           const CalcOptions &calcOptions,
           bool isIonized,
           int startIndex,
           int endIndex);

  Oligomer(PolChemDefCstSPtr polChemDefCstSPtr,
           const QString &name,
           const QString &description,
           bool modified,
           const Ponderable &ponderable,
           const IonizeRule &ionizeRule,
           const CalcOptions &calcOptions,
           bool isIonized,
           int startIndex,
           int endIndex);

  Oligomer(Polymer *polymer,
           const QString &name,
           const QString &description,
           bool modified                  = false,
           const Ponderable &ponderable   = Ponderable(),
           int startIndex                 = -1,
           int endIndex                   = -1,
           const CalcOptions &calcOptions = CalcOptions());

  Oligomer(PolChemDefCstSPtr polChemDefCstSPtr,
           const QString &name,
           const QString &description,
           bool modified                  = false,
           const Ponderable &ponderable   = Ponderable(),
           const CalcOptions &calcOptions = CalcOptions(),
           int startIndex                 = -1,
           int endIndex                   = -1);

  Oligomer(const Ionizable &ionizable,
           const CalcOptions &calcOptions = CalcOptions(),
           int startIndex                 = -1,
           int endIndex                   = -1);

  Oligomer(Polymer *polymer,
           const QString &name,
           const QString &description,
           bool modified                  = false,
           double mono                    = 0,
           double avg                     = 0,
           int startIndex                 = -1,
           int endIndex                   = -1,
           const CalcOptions &calcOptions = CalcOptions());

  Oligomer(PolChemDefCstSPtr polChemDefCstSPtr,
           const QString &name,
           const QString &description,
           bool modified                  = false,
           const CalcOptions &calcOptions = CalcOptions(),
           double mono                    = 0,
           double avg                     = 0,
           int startIndex                 = -1,
           int endIndex                   = -1);

  Oligomer(const Oligomer &other);

  virtual ~Oligomer();

  const Polymer *polymer() const;

  void setModified(bool modified = true);

  // By default look into the polymer to check if the
  // oligomer is modified.
  virtual bool isModified(bool deep = true);

  void setStartEndIndices(int, int);

  void setStartIndex(int);
  int startIndex() const;

  void setEndIndex(int);
  int endIndex() const;

  void setDescription(const QString &);
  QString description() const;

  int appendCoordinates(CoordinateList *);

  void setIonizeRule(IonizeRule &);
  IonizeRule &ionizeRule();

  void setCalcOptions(const CalcOptions &);
  const CalcOptions &calcOptions() const;
  void updateCalcOptions();

  const Monomer &atLeftEnd() const;
  const Monomer &atRightEnd() const;
  const Monomer *monomerAt(int) const;

  QList<CrossLink *> *crossLinkList();
  bool addCrossLink(CrossLink *);

  // ELEMENTAL CALCULATION FUNCTION
  /////////////////////////////////

  virtual QString elementalComposition();
  virtual QString elementalComposition(const CalcOptions &calc_options,
                                       const IonizeRule &ionize_rule);

  /////////////////////////////////
  // ELEMENTAL CALCULATION FUNCTION

  virtual int makeMonomerText();
  QString *monomerText();

  virtual bool calculateMasses();
  virtual bool calculateMasses(const CalcOptions *calcOptions,
                               const IonizeRule *ionizeRule = 0);

  int size();

  bool encompasses(int) const;
  bool encompasses(const Monomer *) const;

  protected:
  //! Polymer in which this oligomer spans a region.
  // At present, cannot be const because of Formula not being able to properly
  // work without modifying itself. FIXME: this is to be resolved.
  Polymer *mp_polymer;

  QString m_description;
  bool m_isModified;


  // !The list of the the cross-links that are involved in the
  // !formation of the cross-linked oligomer(the CrossLink *
  // !instances are in mp_polymer->crossLinkList()). We cannot use
  // !the CrossLinkList object because when destructed it would
  // !destroy the cross-links in it, while we only want to store
  // !pointers.
  QList<CrossLink *> m_crossLinkList;

  CalcOptions m_calcOptions;
};

typedef std::shared_ptr<Oligomer> OligomerSPtr;
typedef std::shared_ptr<const Oligomer> OligomerCstSPtr;


} // namespace libXpertMass

} // namespace MsXpS
