// $Id: timelist.h,v 1.31 2018/01/03 21:33:04 mkkuhner Exp $

/*
Copyright 2002-2005 Mary K. Kuhner, Peter Beerli, and Joseph Felsenstein

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

*/

/* Authors: Peter Beerli, Mary Kuhner, Jon Yamato and Joseph Felsenstein */


// This files defines a time ordered container of Branch_ptr,
// TimeList.  The TimeList is a wrapped std::list of all Branch
// derived things associated with a particular tree.
// Because of the use of shared_ptr to hold Branch objects,
// no deletes should ever be needed.

#ifndef TIMELIST_H
#define TIMELIST_H

#include <map>
#include <vector>

#include "vectorx.h"
#include "defaults.h"
#include "definitions.h"
#include "types.h"
#include "branch.h"
#include "locus.h"      // for DataModel
#include "dlmodel.h"    // for DataModel

// #include "branch.h"--to create the "base" branch, TimeList constructor
//                      to create a Tbranch, CreateTip
//                      to initialize branch innards, both of the above
//                      to maintain tree child and parent relationships
//                         in CopyBody()
//                      to maintain tree child and parent relationships
//                         in CopyPartialBody()
//                      to track ncuttable via branch.Cuttable()
//                      to track marked status via branch.marked,
//                         branch.SetUpdateDL()

//------------------------------------------------------------------------------------

class TipData;
class DataPack;

class TiXmlDocument;

typedef std::map<branch_type, long> BranchMap;
typedef Branchlist::const_reverse_iterator       Branchconstriter;

//------------------------------------------------------------------------------------

class TimeList
{
  private:
    Branch_ptr m_base;
    Branchlist m_branches;                 // TimeList owns the branches!!!
    BranchMap m_branchmap;                 // counts branches of each type

    // cached start-points, for speed
    Branchiter m_firsttip;
    Branchiter m_firstbody;
    Branchiter m_firstcoal;

    long m_ntips, m_ncuttable;

    void CountBranch(const branch_type tag);
    void UncountBranch(const branch_type tag);
    void ClearBranchCount();

    // the argument is non-const since it may be assigned to a non-const member
    void UpdateFirstsIfNecessary(Branchiter & brit);

    Branchiter CollateAndSetCrossrefs(Branch_ptr newbranch, Branch_ptr oldbranch);
    void UpdateBookkeeping(Branchiter newbranch);

  public:

    TimeList();
    TimeList(const TimeList & src);
    TimeList & operator=(const TimeList & src);
    ~TimeList();
    void Clear();
    void ClearBody();
    void ClearPartialBody(Branchiter firstinvalid);

    // "Copies" tree linkage as well; destroys previous elements.
    void CopyTips(const TimeList & src);
    void CopyBody(const TimeList & src);  // Destroys previous elements.
    // "Copies" tree linkage as well; assumes tips are already identical.
    void CopyPartialBody(const TimeList & src, const Branchiter & srcstart, Branchiter & mystart);
    // Destroys previous elements from start on, "copies" tree linkage as well;
    // assumes tips are already identical.

    Branchiter FindEquivBranch(const Branch_ptr target);

    long HowMany(const branch_type tag) const; // How many branchs of type 'tag'?

    // Following functions used to prevent Newick writing of non-Newick-legal trees.
    bool ContainsOnlyTipsAndCoals() const; // any branches other than tips & coals?
    bool ContainsOnlyTipsCoalsAndRecombs() const; // any branches other than tips, coals &
                                                  //   recombinations?
    Branchiter Collate(Branch_ptr newbranch);
    Branchiter Collate(Branch_ptr newbranch, Branch_ptr spouse);
    void Remove(Branch_ptr badbranch);

    Branchiter       BeginBranch()        { return m_branches.begin(); };
    Branchconstiter  BeginBranch()  const { return m_branches.begin(); };
    Branchiter       EndBranch()          { return m_branches.end(); };
    Branchconstiter  EndBranch()    const { return m_branches.end(); };
    Branchconstriter RBeginBranch() const { return m_branches.rbegin(); };
    Branchconstriter REndBranch()   const { return m_branches.rend(); };

    // Return the first branch of a specific sort.
    Branchiter       FirstTip() { return m_firsttip; };
    Branchconstiter  FirstTip() const { return m_firsttip; };
    Branchiter       FirstBody() { return m_firstbody; };
    Branchconstiter  FirstBody() const { return m_firstbody; };
    Branchiter       FirstCoal() { return m_firstcoal; };
    Branchconstiter  FirstCoal() const { return m_firstcoal; };

    // Return the next branch of a specific sort.
    Branchiter       NextTip(Branchiter & it);
    Branchconstiter  NextTip(Branchconstiter & it) const;
    Branchiter       NextBody(Branchiter & it);
    Branchconstiter  NextBody(Branchconstiter & it) const;
    Branchiter       NextCoal(Branchiter & it);
    Branchconstiter  NextCoal(Branchconstiter & it) const;
    Branchiter       PrevBodyOrTip(Branchiter & it);
    Branchiter       NextNonTimeTiedBranch(Branchiter & it);
    Branchconstiter  NextNonTimeTiedBranch(Branchconstiter & it) const;

    Branch_ptr       Base()            { return m_base; };
    const Branch_ptr Base()      const { return m_base; };
    Branch_ptr       Root()      const { return m_base->Child(0); };
    double           RootTime()  const { return Root()->m_eventTime; };
    Branchiter       FindIter(Branch_ptr br);
    Branchconstiter  FindIter(Branch_ptr br) const;

    long          GetNTips()     const { return m_ntips; };
    long          GetNCuttable() const { return m_ncuttable; };
    long          GetNBranches() const { return m_branches.size(); };
    long          GetNBodyBranches() const;
    TBranch_ptr   CreateTip(const TipData & tipdata, const vector<LocusCell> & cells,
                            const vector<LocusCell> & movingcells, long nsites, rangeset diseasesites);
    TBranch_ptr   CreateTip(const TipData & tipdata, const vector<LocusCell> & cells,
                            const vector<LocusCell> & movingcells, long nsites, rangeset diseasesites,
                            const vector<Locus> & loci);

    TBranch_ptr   GetTip(const string & name) const;
    void          Prune();
    void          SetUpdateDLs(Branch_ptr);
    void          ClearUpdateDLs();
    void          SetAllUpdateDLs();

    bool operator==(const TimeList & src) const;
    bool operator!=(const TimeList & src) const { return !(*this == src); };

    Branchiter ResolveTiedTimes(Branchconstiter firstinvalid);

    // Debugging functions.
    void PrintTimeList() const;
    void PrintTimeList(std::ostream & of) const;
    void PrintTreeList() const;
    void PrintTreeList(std::ostream & of) const;
    TiXmlDocument * AssembleGraphML() const;
    void PrintIDs() const;
    void PrintTips();
    bool IsValidTimeList() const;
    void MakeCoalescent(double theta);
    string DLCheck(const TimeList & other) const;
    void CloneCheck() const;
    bool IsPresent(const Branchiter & branch) const;
    bool IsSameExceptForTimes(const TimeList & other) const;
    void PrintIntervalLengthsToFile() const;
    void PrintDirectionalMutationEventCountsToFile(std::ofstream & of) const;
    void PrintTimeTilFirstEventToFile(std::ofstream & of) const;
    void PrintTraitPhenotypeAtLastCoalescence(std::ofstream & of) const;
    void PrintNodeInfo(long nodenumber) const;
    bool RevalidateAllRanges() const;
    void CorrectAllRanges();
};

#endif // TIMELIST_H

//____________________________________________________________________________________
