// $Id: StrandPair.h 1367 2024-08-30 21:00:18Z ge $
/// \file StrandPair.h
/// \brief contains the StrandPair class
/// $Revision: 1367 $
/// \author Gerald Weber

#ifndef GBC_EXP_STRANDPAIR_H
#define GBC_EXP_STRANDPAIR_H "$Id: StrandPair.h 1367 2024-08-30 21:00:18Z ge $"

#include <boost/regex.hpp>

#include "Nucleotide.h"
#include "RegexPattern.h"

namespace gbc {
///  StrandPair are two nucleotides along a strand. For example, an AT base pair followed
///  by a CG base pair would have as main strand pair AC (5'->3') 
///  and opposite strand pair TG (3'->5')
template<class _InternalTp=char>
class StrandPair: public std::pair<Nucleotide<_InternalTp>,Nucleotide<_InternalTp> >
  {
  public:
    typedef _InternalTp internal_type;                ///< type of _InternalTp
    typedef Nucleotide<_InternalTp> nucleotide_type;  ///< type of Nucleotide<_InternalTp>
    typedef std::pair<Nucleotide<_InternalTp>,Nucleotide<_InternalTp> > pair_type;
    typedef std::string version_type;
    typedef StrandPair<_InternalTp> strandpair_type;
    enum SymmetryActionsType {do_not_simplify_symmetry = 1 /* formely context_dependent=true */, 
                              simplify_symmetry        = 0 /* formely not_context_dependent=false */};
    enum direction_type { dir53=53, dir35=35 };
    enum origin_type { origin_unknown=0, origin_main=1, origin_opposite=2 };
    const static char separation_char_53='>'; ///< Separator string for the string representation.
    const static char separation_char_35='<'; ///< Separator string for the string representation.
    static const SymmetryActionsType default_symmetry_action=simplify_symmetry;

    SymmetryActionsType Symmetry_action; ///< Strand pairs may simplified according to their 5´->3' directions
    direction_type StrandDirection; //Holds the direction to which the strand is represented, dir53 is default
    origin_type    Origin;          //Which strand originated this StrandPair

  StrandPair(void): Symmetry_action(do_not_simplify_symmetry), StrandDirection(dir53), Origin(origin_unknown)
    {
    }
    
  StrandPair(std::string nm): Symmetry_action(do_not_simplify_symmetry), StrandDirection(dir53), Origin(origin_unknown)
    {
    this->assign(nm);
    }

    
  StrandPair(SymmetryActionsType symmetry_action): Symmetry_action(symmetry_action), StrandDirection(dir53), Origin(origin_unknown)
    {
    }
    
  //This is the only symmetry action for this class, here we swap the Nucleotides and reverse
  //the direction. For instance A>C becomes C<A. Origin is left unchanged, therefore
  //we always know where this StrandPair came from.
  inline void reverse(void)
    {
    nucleotide_type tmp(this->second);
    this->second=this->first;
    this->first=tmp;

    if (StrandDirection == dir53) StrandDirection=dir35; else StrandDirection=dir53;
    }

  void assign(std::string n1, std::string n2, 
              direction_type dir=dir53, origin_type org=origin_unknown)
    {
    this->first=n1[0];
    this->second=n2[0];
    StrandDirection=dir53;
    Origin=org;
    if (Symmetry_action==simplify_symmetry) this->reduce_to_smallest_symmetry();//if Symmetry_action==simplify_symmetry
    }
  
  void assign(std::string nm, origin_type org=origin_unknown)
    {
    const boost::regex name(STRANDPAIR_ASSIGN_PATTERN);
    boost::smatch found;
    if (boost::regex_search(nm,found,name))
      {
      std::string n1=found[1], dir=found[2], n2=found[3];
      this->first=n1[0];
      this->second=n2[0];
      if (dir[0] == separation_char_53) this->StrandDirection=dir53;
      else                              this->StrandDirection=dir35;
      if (Symmetry_action==simplify_symmetry) this->reduce_to_smallest_symmetry();//if Symmetry_action==simplify_symmetry
      }
    }
  
        
  template<class _Tp>
  void assign(const Nucleotide<_Tp>& n1, const Nucleotide<_Tp>& n2, 
              direction_type dir=dir53, origin_type org=origin_unknown)
    {
    this->first=n1;
    this->second=n2;
    StrandDirection=dir;
    Origin=org;
    if (Symmetry_action==simplify_symmetry) this->reduce_to_smallest_symmetry();//if Symmetry_action==simplify_symmetry
    }
    

     /// Operator which returns a concatenated string of symbols representing the sequence.
  operator std::string(void) const
    {
    std::string out=std::string();
    out += this->first.symbol();
    if (StrandDirection == dir53) out += separation_char_53;
    else out += separation_char_35;
    out += this->second.symbol();
    return out;
    }
    
  //Convert this StrandPair to 5'->3'direction, this is not quite a symmetry opperation
  //as for BasePair, but allows to place all StrandPairs into the same direction for comparison
  inline bool reduce_to_smallest_symmetry(void)
    {
    bool reduced=false;
    if (StrandDirection == dir35) {this->reverse(); reduced=true;}
    return reduced;
    }

  friend std::ostream& operator<<(std::ostream &out,const StrandPair &sp)
    { 
    out << static_cast<std::string>(sp);
    return out;
    }

  //This solves insertion problems with ReferenceSet
  //Without overloading this operator bp1 and bp2 may return equal even if they have different versions
  friend bool operator<(const StrandPair& bp1, const StrandPair& bp2)
    {
    std::string p1=static_cast<std::string>(bp1), p2=static_cast<std::string>(bp2);
    return p1 < p2;
    }

    
  };
  
};
#endif
