/* 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

#include <memory>


/////////////////////// Qt includes
#include <QString>
#include <QObject>
#include <QDateTime>
#include <QCryptographicHash>


/////////////////////// Local includes
#include "exportimportconfig.h"
#include "Sequence.hpp"
#include "IonizeRule.hpp"
#include "Ionizable.hpp"
#include "Modif.hpp"
#include "CalcOptions.hpp"


namespace MsXpS
{

namespace libXpertMass
{

  const int POL_SEQ_FILE_FORMAT_VERSION = 1;

  class PolChemDef;
  typedef std::shared_ptr<const PolChemDef> PolChemDefCstSPtr;

  class IonizeRule;
  class Ionizable;
  class CrossLink;
  class CrossLinkList;


  //! The Polymer class provides a polymer.
  /*! A polymer is a sequence(Sequence) to which data are
    aggregated to make it able to perform a number of tasks.

    The polymer has a name and a code, so that it is possible to
    refer to it in printed material...

    Because Polymer derives from Ionizable, which itself derives from
    PolChemDefEntity, it holds a pointer to the polymer chemistry
    definition of the polymer sequence. Without that datum, using the
    polymer in a complex context would be impossible, as the polymer
    chemistry definition will provide a huge amount of data required
    to operate the polymer efficiently. Because Ionizable inherits
    Ponderable, the polymer has a mono mass and an average mass.

    The polymer also has modifications(left-end and right-end) to
    characterize it better.
  */
  class DECLSPEC Polymer : public QObject, public Sequence, public Ionizable
  {
    Q_OBJECT


    public:
    Polymer(PolChemDefCstSPtr pol_chem_def_csp,
            const QString & = QString("NOT_SET"),
            const QString & = QString("NOT_SET"),
            const QString & = QString("NOT SET"));

    virtual ~Polymer();

    void setName(const QString &);
    QString name() const;

    void setCode(const QString &);
    QString code() const;

    void setAuthor(const QString &);
    QString author() const;

    void setFilePath(const QString &);
    QString filePath() const;

    void setDateTime(const QString &);
    QString dateTime() const;

    bool hasModifiedMonomer(int left_index = -1, int right_index = -1) const;

    bool setLeftEndModif(const QString & = QString());
    bool setLeftEndModif(const Modif &);
    const Modif &leftEndModif() const;
    bool isLeftEndModified() const;

    bool setRightEndModif(const QString & = QString());
    bool setRightEndModif(const Modif &);
    const Modif &rightEndModif() const;
    bool isRightEndModified() const;


    const CrossLinkList &crossLinkList() const;
    CrossLinkList *crossLinkListPtr();
    bool crossLinkedMonomerIndexList(int, int, QList<int> *, int *);
    bool crossLinkList(int, int, QList<CrossLink *> *, int *);


#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Woverloaded-virtual"
    virtual bool prepareMonomerRemoval(Monomer *);
#pragma clang diagnostic pop

    using Ionizable::operator=;
    virtual bool removeMonomerAt(int);

    QByteArray md5Sum(int hash_data_specifs) const;


    // MASS CALCULATION FUNCTIONS
    /////////////////////////////

#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Woverloaded-virtual"
    bool accountMasses(const CalcOptions &);
    static bool
    accountMasses(Polymer *, const CalcOptions &, double *, double *);
    bool calculateMasses(const CalcOptions &, bool = true);
    static bool calculateMasses(
      Polymer *, const CalcOptions &, double *, double *, bool = true);
#pragma clang diagnostic pop


    bool accountCappingMasses(int, int = 1);
    static bool
    accountCappingMasses(Polymer *, int, double *, double *, int = 1);

    bool accountEndModifMasses(int);
    static bool accountEndModifMasses(Polymer *, int, double *, double *);
    static bool accountEndModifMasses(Polymer *, int, Ponderable *);

    bool crossLink(CrossLink *);
    bool uncrossLink(CrossLink *);

    /////////////////////////////
    // MASS CALCULATION FUNCTIONS

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

    QString elementalComposition(const IonizeRule &,
                                 const CoordinateList &,
                                 const CalcOptions &);

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

    bool renderXmlCodesElement(const QDomElement &element);

    static QString xmlPolymerFileGetPolChemDefName(const QString &file_path);

    bool renderXmlPolymerFile(QString = QString(""));
    bool renderXmlPolymerModifElement(const QDomElement &, int);
    bool renderXmlCrossLinksElement(const QDomElement &, int);

    QString *formatXmlDtd();
    QString *formatXmlPolSeqElement(int, const QString & = QString("  "));
    QString *formatXmlCrossLinksElement(int, const QString & = QString("  "));

    bool writeXmlFile();

    bool validate();

    void debugPutStdErr();

    signals:
    void polymerDestroyedSignal(Polymer *);
    void crossLinkChangedSignal(Polymer *);
    void crossLinksPartiallyEncompassedSignal(int) const;

    private:
    //! Name
    QString m_name;

    //! Code.
    QString m_code;

    //! Name of the last user having last modified the polymer sequence.
    QString m_author;

    //! File name.
    QString m_filePath;

    //! Date and time of the last modification.
    QDateTime m_dateTime;

    //! Left end modification.
    Modif m_leftEndModif;

    //! Right end modification.
    Modif m_rightEndModif;

    //! The list of CrossLink instances.
    CrossLinkList *mpa_crossLinkList;
  };

  typedef std::shared_ptr<Polymer> PolymerSPtr;
  typedef std::shared_ptr<const Polymer> PolymerCstSPtr;


} // namespace libXpertMass

} // namespace MsXpS

Q_DECLARE_METATYPE(MsXpS::libXpertMass::Polymer);
extern int polymerMetaTypeId;

Q_DECLARE_METATYPE(MsXpS::libXpertMass::PolymerSPtr);
extern int polymerSPtrMetaTypeId;

Q_DECLARE_METATYPE(MsXpS::libXpertMass::PolymerCstSPtr);
extern int polymerCstSPtrMetaTypeId;
