/*
 Copyright (C) 2003 Ronald C Beavis, all rights reserved
 X! tandem
 This software is a component of the X! proteomics software
 development project

Use of this software governed by the Artistic license, as reproduced here:

The Artistic License for all X! software, binaries and documentation

Preamble
The intent of this document is to state the conditions under which a
Package may be copied, such that the Copyright Holder maintains some
semblance of artistic control over the development of the package,
while giving the users of the package the right to use and distribute
the Package in a more-or-less customary fashion, plus the right to
make reasonable modifications.

Definitions
"Package" refers to the collection of files distributed by the Copyright
  Holder, and derivatives of that collection of files created through
  textual modification.

"Standard Version" refers to such a Package if it has not been modified,
  or has been modified in accordance with the wishes of the Copyright
  Holder as specified below.

"Copyright Holder" is whoever is named in the copyright or copyrights
  for the package.

"You" is you, if you're thinking about copying or distributing this Package.

"Reasonable copying fee" is whatever you can justify on the basis of
  media cost, duplication charges, time of people involved, and so on.
  (You will not be required to justify it to the Copyright Holder, but
  only to the computing community at large as a market that must bear
  the fee.)

"Freely Available" means that no fee is charged for the item itself,
  though there may be fees involved in handling the item. It also means
  that recipients of the item may redistribute it under the same
  conditions they received it.

1. You may make and give away verbatim copies of the source form of the
Standard Version of this Package without restriction, provided that
you duplicate all of the original copyright notices and associated
disclaimers.

2. You may apply bug fixes, portability fixes and other modifications
derived from the Public Domain or from the Copyright Holder. A
Package modified in such a way shall still be considered the Standard
Version.

3. You may otherwise modify your copy of this Package in any way, provided
that you insert a prominent notice in each changed file stating how and
when you changed that file, and provided that you do at least ONE of the
following:

a.	place your modifications in the Public Domain or otherwise make them
  Freely Available, such as by posting said modifications to Usenet
  or an equivalent medium, or placing the modifications on a major
  archive site such as uunet.uu.net, or by allowing the Copyright Holder
  to include your modifications in the Standard Version of the Package.
b.	use the modified Package only within your corporation or organization.
c.	rename any non-standard executables so the names do not conflict
  with standard executables, which must also be provided, and provide
  a separate manual page for each non-standard executable that clearly
  documents how it differs from the Standard Version.
d.	make other distribution arrangements with the Copyright Holder.

4. You may distribute the programs of this Package in object code or
executable form, provided that you do at least ONE of the following:

a.	distribute a Standard Version of the executables and library files,
  together with instructions (in the manual page or equivalent) on
  where to get the Standard Version.
b.	accompany the distribution with the machine-readable source of the
  Package with your modifications.
c.	give non-standard executables non-standard names, and clearly
  document the differences in manual pages (or equivalent), together
  with instructions on where to get the Standard Version.
d.	make other distribution arrangements with the Copyright Holder.

5. You may charge a reasonable copying fee for any distribution of
this Package. You may charge any fee you choose for support of
this Package. You may not charge a fee for this Package itself.
However, you may distribute this Package in aggregate with other
(possibly commercial) programs as part of a larger (possibly
commercial) software distribution provided that you do not a
dvertise this Package as a product of your own. You may embed this
Package's interpreter within an executable of yours (by linking);
this shall be construed as a mere form of aggregation, provided that
the complete Standard Version of the interpreter is so embedded.

6. The scripts and library files supplied as input to or produced as
output from the programs of this Package do not automatically fall
under the copyright of this Package, but belong to whomever generated
them, and may be sold commercially, and may be aggregated with this
Package. If such scripts or library files are aggregated with this
Package via the so-called "undump" or "unexec" methods of producing
a binary executable image, then distribution of such an image shall
neither be construed as a distribution of this Package nor shall it
fall under the restrictions of Paragraphs 3 and 4, provided that you
do not represent such an executable image as a Standard Version of
this Package.

7. C subroutines (or comparably compiled subroutines in other languages)
supplied by you and linked into this Package in order to emulate
subroutines and variables of the language defined by this Package
shall not be considered part of this Package, but are the equivalent
of input as in Paragraph 6, provided these subroutines do not change
the language in any way that would cause it to fail the regression
tests for the language.

8. Aggregation of this Package with a commercial distribution is always
permitted provided that the use of this Package is embedded; that is,
when no overt attempt is made to make this Package's interfaces visible
to the end user of the commercial distribution. Such use shall not be
construed as a distribution of this Package.

9. The name of the Copyright Holder may not be used to endorse or promote
products derived from this software without specific prior written permission.

10. THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED
WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.

The End
*/

#pragma once

// File version: 2003-08-01
// File version: 2004-03-01
// File version: 2004-11-01

typedef std::map<std::size_t, std::string> SEQMAP;

#include <sys/timeb.h>
#include <ctime>
#include "mcleave.h"
#include "mspectrumcondition.h"
#include "mreport.h"
#include "mzid_report.h"
#include "msequenceserver.h"
#include "mplugin.h"
#include "msemistate.h"
#include "mrefine.h"
#include <set>
#include <pappsomspp/core/msfile/msfileaccessor.h>
#include <pappsomspp/core/pappsoexception.h>

typedef map<uint64_t, vector<msequence>> MSEQMAP;

/*
 * the process object coordinates the function of tandem. it contains the
 * information loaded from the input XML file in the m_xmlValues object and
 * performance information in the m_xmlPerformance object. The mass spectra to
 * be analyzed are in the m_vSpectra vector container. A set of input parameters
 * are used to initialize constants that are used in processing the mass
 * spectra.
 */
class merrors
{
  public:
  merrors(void)
  {
    m_bPpm     = true;
    m_bIsotope = false;
    m_dPlus    = 20.0;
    m_dMinus   = 20.0;
  }
  virtual ~merrors(void)
  {
  }
  bool m_bPpm;
  bool m_bIsotope;
  double m_dPlus;
  double m_dMinus;
  bool
  check(double _s, double _m)
  {
    double dDelta = _s - _m;
    double dPlus  = m_dPlus;
    double dMinus = m_dMinus;
    if(m_bPpm)
      {
        dPlus *= _m * 1.0e-6;
        dMinus *= _m * 1.0e-6;
      }
    if(dDelta < 0.0)
      {
        if(dDelta >= dMinus)
          {
            return true;
          }
      }
    else
      {
        if(dDelta <= dPlus)
          {
            return true;
          }
      }
    if(!m_bIsotope)
      {
        return false;
      }
    if(_s > 1000.0)
      {
        dDelta -= 1.008664916;
        if(dDelta < 0.0)
          {
            if(dDelta >= dMinus)
              {
                return true;
              }
          }
        else
          {
            if(dDelta <= dPlus)
              {
                return true;
              }
          }
      }
    if(_s > 1500.0)
      {
        dDelta -= 1.008664916;
        if(dDelta < 0.0)
          {
            if(dDelta >= dMinus)
              {
                return true;
              }
          }
        else
          {
            if(dDelta <= dPlus)
              {
                return true;
              }
          }
      }
    return false;
  }
};

class mprocesslog
{
  public:
  mprocesslog()
  {
  }
  virtual ~mprocesslog()
  {
  }
  bool
  open(string &_s)
  {
    m_ofLog.open(_s.c_str());
    if(m_ofLog.fail())
      {
        throw pappso::PappsoException(
          QString("ERROR opening log file %1, check path or file permission")
            .arg(_s.c_str()));
        return false;
      }
    return true;
  }
  bool
  log(string &_m)
  {
    if(!m_ofLog.is_open())
      {
        return false;
      }
    time_t tValue;
    time(&tValue);
    struct tm *tmValue = localtime(&tValue);
    char pLine[256];
    strftime(pLine, 255, "%Y-%m-%d %H:%M:%S", tmValue);
    m_ofLog << pLine << "\t" << _m.c_str() << "\n";
    m_ofLog.flush();
    return true;
  }
  bool
  log(const char *_m)
  {
    if(!m_ofLog.is_open())
      {
        return false;
      }
    string strValue = _m;
    return log(strValue);
  }
  bool
  close()
  {
    if(!m_ofLog.is_open())
      {
        return false;
      }
    m_ofLog.close();
    return true;
  }

  private:
  ofstream m_ofLog;
};

class mprocess
{
  public:
  mprocess(void);
  virtual ~mprocess(void);
  bool serialize(void); // serializes the m_vMI data elements in m_vSpectra onto
                        // the disk to save space during calculation
  bool restore(void);   // restores the m_vMI data elements from disk
  bool removeMI(void);
  vector<string> m_vstrPaths;
  mprocesslog m_prcLog;
  bool add_spectra(
    vector<mspectrum> &_v); // adds the spectra contained in _v to m_vSpectra
  bool clear(); // clears a selection of vectors in the mprocess object
  unsigned long
  get_thread(); // retrieves the number of the current thread (0 - 15)
  unsigned long
  get_threads(); // retrieves the total number of threads running (1 - 16)
  size_t get_protein_count();  // gets the total number of proteins read
  size_t get_peptide_count();  // gets the total number of peptides used
  size_t get_total_residues(); // gets the total number of residues read
  size_t get_valid();          // gets the number of valid peptide models
  double
  get_error_estimate()
  {
    return m_dEsum;
  }
  size_t get_unique();    // gets the number of valid peptide models
  long get_reversed();    // gets the number of reversed peptide models
  double get_threshold(); // gets the number of valid peptide models
  bool load(const char *_f, mprocess *_p = NULL); // loads input parameters
  bool load_saps(mprocess *_p); // loads sap information, if it exists
  bool load_annotation(
    mprocess *_p); // loads sequence annotation information, if it exists
  virtual bool
  merge_spectra(); // adds externally generated mspectrum vector to m_vSpectra
  virtual bool merge_spectra(
    vector<mspectrum>
      &_s); // adds externally generated mspectrum vector to m_vSpectra
  bool merge_map(
    SEQMAP &_s); // adds externally generated mspectrum vector to m_vSpectra
  bool merge_statistics(
    const mprocess *_p); // adds externally generated spectra to this object
  bool process(void); // performs identifications based on the input parameters
  bool refine(void);  // controls the protein model refinement process
  bool report(void);  // produces the XML output report, using an mreport class
  bool pyro_check(const char _c);
  bool pyro_reset(void);
  bool score_each_sequence(); // generates a score for all sequences in the
                              // m_svrSequences object
  bool set_threads(const unsigned long _t); // sets the object's thread number
  bool set_thread(const unsigned long _t);  // sets the object's thread number
  bool dynamic_parent_ion_selection(
    const double _d,
    const double _maxe); // selects a parent ion mass window for high resolution
                         // spectra dalton space
  bool dynamic_parent_ion_selection_ppm(
    const double _d,
    const double _maxe); // selects a parent ion mass window for high resolution
                         // spectra in ppm space
  int
  set_round(const int _r)
  {
    m_iCurrentRound = _r;
    return m_iCurrentRound;
  }
  virtual bool load_sequences();
  XmlParameter m_xmlPerformance; // stores process performance parameters
  XmlParameter m_xmlValues;      // store process input parameters
  vector<mspectrum> m_vSpectra;  // store spectra to be analyzed
  SEQMAP m_mapSequences; // a map containing all of the protein sequences
                         // discovered, indexed by their m_tUid value
  vector<msequence>
    m_vseqBest; // a vector of msequences used in the model refinement process
  vector<string>
    m_vstrModifications;   // a vector containing the strings defining fixed
                           // modifications for a protein
  size_t m_tRefineModels;  // total number of models generated by refinement
  size_t m_tRefineInput;   // total number of sequences included in a refinement
                           // session
  size_t m_tRefinePartial; // the number of models discovered to have partial
                           // cleavage
  size_t m_tRefineUnanticipated; // the number of models discovered to have
                                 // unanticpated cleavage
  size_t m_tRefineNterminal; // the number of models discovered to have modified
                             // N-terminii
  size_t m_tRefineCterminal; // the number of models discovered to have modified
                             // C-terminii
  size_t
    m_tRefinePam; // the number of models discovered to have point mutations
  double m_dRefineTime; // the time required to perform a refinement
  size_t
    m_tActive; // total number of models remaining after each refinement step
  bool
    m_bRefineCterm; // true if processing 'refine, potential C-terminus
                    // modifications'. Set in mrefine::refine and checked in
                    // score(), so the start position can be set to the length
                    // of the protein sequence
                    //  minus the value for 'refine, potential N-terminus
                    //  modification position limit' before performing cleavage
  vector<int> m_viQuality; // contains the data quality scoring vector
  map<uint64_t, size_t> m_mapCrc;
  size_t m_tTemp;
  bool m_bReversedOnly;
  bool m_bSaps;
  bool m_bAnnotation;
  bool m_bMinimalAnnotation;
  bool m_bSerialize;
  bool m_bCheckNg;
  bool m_bSkyline;
  bool m_bUseCrc;
  string m_strSkyline;
  double m_dNt;
  double m_dNtAve;
  double m_dNg;
  double m_dNgAve;
  float m_fMaxMass;
  float m_fMaxZ;
  enum
  {
    I_Y = 0x01,
    I_B = 0x02,
    I_X = 0x04,
    I_A = 0x08,
    I_C = 0x10,
    I_Z = 0x20,
  } ion_type; // enum for referencing information about specific ion types.

  protected:
  /*
   * refinement classes are declared friend classes so they can access private
   * member variables and functions. If a new refinement class is added to the
   * project, include a declaration here
   */
  friend class mrefine;
  friend class mpmods;
  friend class mxxcleavage;
  friend class mtermmods;
  friend class mpam;

  protected:
  pappso::MsFileAccessor *mp_msFileAccessor = nullptr;
  QString m_sampleName;
  string m_strLastMods;
  int m_iCurrentRound;
  bool m_bPermute;
  bool m_bPermuteHigh;
  bool m_bCrcCheck;
  bool m_bQuickAcetyl;
  bool m_bQuickPyro;
  double m_dEsum;
  bool m_bChargeDither;
  float m_fSavResolve;
  bool m_bSavResolve;
  set<size_t> m_setRound;
  vector<string> m_vstrSaps;
  vector<string> m_vstrMods;
  map<string, string> m_mapAnnotation;
  map<string, string> *m_pmapAnnotation;
  msemistate m_semiState; // maintains the state of the semi-enzymatic cleavage
                          // state machine
  mpyrostate m_pyroState; // maintains the state of the pyrolidone carboxylic
                          // acid detection state machine
  merrors m_errValues;
  double m_dSearchTime; // total time elapsed during a protein modeling session
                        // process
  long m_lIonCount; // minimum sum of detected ions that are significant enough
                    // to store a sequence
  unsigned long m_lThread;  // thread number of this object
  unsigned long m_lThreads; // the total number of threads current active
  long m_lReversed;     // the total number of peptides found where the reversed
                        // sequence was better than the forward sequence
  double m_dThreshold;  // the current expectation value threshold
  double m_tContrasted; // the number of spectra subtracted using contrast angle
                        // redundancy detection
  long m_lStartMax; // set the maximum distance from N-terminus for a peptide
                    // normally set at an impossibly large value = 100,000,000
                    // for ragged N-terminii with potential modifications, set
                    // at a low but plausible value = 50
  long m_lCStartMax;
  char *m_pSeq; // a character pointer, used for temporary sequence information
  bool m_bUn;   // if true, cleave at all residues. if false, use cleavage
                // specification in data input.
  bool m_bUseHomologManagement; // set to true to use homologue management
  size_t m_tMinResidues; // the minimum peptide length that will be scored
  size_t m_tMaxResidues; // the maximum peptide length that will be scored
  size_t
    m_tMissedCleaves; // the maximum number of cleavage sites that can be missed
  size_t m_tPeptideCount; // the total number of peptide sequences generated
                          // during a process
  size_t m_tPeptideScoredCount; // the total number of peptide sequences scored
                                // during a process
  size_t m_tProteinCount;  // the total number of protein sequences considered
                           // during a process
  size_t m_tSpectra;       // the total number of spectra being modeled
  size_t m_tSpectraTotal;  // the total number of spectra in the input file
  size_t m_tValid;         // the number of valid peptide models
  size_t m_tTotalResidues; // the number of residues read
  size_t m_tSeqSize;       // current length of the m_pSeq character array
  size_t m_tUnique;        // the number of unique peptides found in a result
  string m_strOutputPath;  // the path name of the XML output file
  mcleave m_Cleave;        // the specification for a cleavage peptide bond
  msequence m_seqCurrent; // the msequence object that is currently being scored
#ifdef X_P3
  p3msequenceServer m_svrSequences; // the msequenceServer object that provides
                                    // msequences to msequenceCollection
#else
  msequenceServer m_svrSequences; // the msequenceServer object that provides
                                  // msequences to msequenceCollection
#endif
  mspectrumcondition
    m_specCondition;  // the mspectrumcondition object that cleans up and
                      // normalized spectra for further processing
  mscore *m_pScore;   // the object that is used to score sequences and spectra
  mrefine *m_pRefine; // the object that is used to refine models

  bool charge(void); // adds additional charge states to the list of spectra,
                     // resulting in +1, +2 and +3 all being represented
  virtual bool clean_sequences(
    void); // remove sequences no longer required from m_mapSequences
  double
  dot(const size_t _f,
      const size_t _s,
      const float _r,
      const bool _t); // calculated inner product between two spectrum vectors
  bool
  create_rollback(vector<mspectrum> &_v); // create a temporary rollback vector
  virtual bool create_score(const msequence &_s,
                            const size_t _v,
                            const size_t _w,
                            const long _m,
                            const bool _p); // generates scores for a sequence
  double
  expect_protein(const unsigned long _c,
                 const unsigned long _t,
                 const unsigned long _n,
                 const double _d); // assigns an expectation value for a protein
  bool load_best_vector(void);     // creates a new m_vseqBest vector and marks
                                   // assigned spectra inactive
  bool mark_repeats(
    void); // checks for repeated assignments of the same peptide sequence
  bool modify(void); // sets the initial residue modification parameters in the
                     // m_seqUtil object
  bool refine_model(void); // the method that refines protein models
  bool report_all();       // create a report that contains all peptide models,
                           // regardless of expectation value
  bool
  report_valid(const double _d); // create a report that only contains valid
                                 // peptide models (expect < m_dThreshold)
  bool report_stochastic(
    const double _d); // create a report that contains only stochastic peptide
                      // models (expect > m_dThreshold)
  bool report_expect(
    const double _m);     // calculates expectation values for the report
  bool report_sort(void); // sorts peptide models for use in the report
  bool residues(); // estimates the minimum number of residues that can possibly
                   // be contained in a peptide
  bool rollback(vector<mspectrum> &_v, const double _m, const double _f);
  bool score(const msequence &_s); // generates scores of an msequence object
  bool
  score_single(const msequence &_s); // generates scores of an msequence object
  bool score_terminus(
    const string &_s); // attempts to find N or C terminal modified peptides
  //	bool score_terminus_single(const string &_s); // attempts to find N or C
  // terminal modified peptides
  bool
  spectra(void); // loads the m_vSpectra object using the m_specCondition object
  bool subtract(void); // remove redundant mass spectra
  uint64_t crc(const string &_s);
  uint64_t *m_pCrcTable;
  bool initialize_crc(void);
  MSEQMAP m_mapDups;
  //	SEQMAP m_mapTest;	used to test the utility of the crc function, see
  // commented code in mprocess::score
  size_t m_tDuplicates;
  size_t m_tDuplicateIds;
  bool insert_dups(void);
  virtual bool
  taxonomy(void); // loads the taxonomy setting into the m_svrSequences object
};

#include "p3mprocess.h"
