// $Id: StrandPairSequence.h 1247 2021-12-13 18:08:41Z ge $
/// \file StrandPairSequence.h
/// \brief contains the StrandPairSequence class
///
/// $Revision: 1247 $
/// \author Gerald Weber

#ifndef GBC_EXP_STRANDPAIRSEQUENCE_H
#define GBC_EXP_STRANDPAIRSEQUENCE_H "$Id: StrandPairSequence.h 1247 2021-12-13 18:08:41Z ge $"

#include "StrandPair.h"
#include "NeighbourSequence.h"
#include "ErrorCodes.h"

namespace gbc {

using namespace gbc;

template<class _InternalTp=char>
/// \brief Sequence (std::deque) of strand pairs.
///
class StrandPairSequence: public std::deque<StrandPair<_InternalTp> >
  {
  public:
    typedef _InternalTp                               internal_type;             ///< type of _InternalTp
    typedef StrandPair<internal_type>                 strand_pair_type;             ///< type of _InternalTp
    typedef StrandPair<internal_type>                 base_pair_type;             ///< type of _InternalTp
    typedef StrandPair<internal_type>                 pair_type;
    typedef std::deque<strand_pair_type>              base_class_type;
    typedef std::deque<strand_pair_type>              deque_type;                ///< the type of this class
    typedef unsigned long int                         index_type;

    typename strand_pair_type::SymmetryActionsType Symmetry_action;    ///< Flags which symmetry action will be performed
    bool Periodic;                  ///< If true we consider the first and the last base-pairs as neighbours
    const deque_type* Reference_StrandPairSequence;

  StrandPairSequence(void)
    : Symmetry_action(strand_pair_type::do_not_simplify_symmetry), Periodic(true), 
      Reference_StrandPairSequence(NULL) {}

  inline StrandPairSequence& operator+=(strand_pair_type sp)
    {
    if (Symmetry_action == strand_pair_type::simplify_symmetry) sp.reduce_to_smallest_symmetry();
    if (Reference_StrandPairSequence != NULL)
      {
      typename deque_type::const_iterator dit;
      dit=std::find(Reference_StrandPairSequence->begin(),Reference_StrandPairSequence->end(),sp);
      if (dit != Reference_StrandPairSequence->end()) back_inserter(*this)=*dit;
      else 
        {
        CERR_WARN(WNRFSPF) << "Warning no reference for " << sp << " found" << std::endl;
        back_inserter(*this)=sp;
        }
      }
    else back_inserter(*this)=sp;
    return *this;
    }

  /// Takes a Duplex (which stores base-pairs) and transforms it into nearest neighbours.
  inline StrandPairSequence& operator=(const NeighbourSequence<_InternalTp>& dup)
    {
    deque_type::clear();
    typename NeighbourSequence<internal_type>::const_iterator dit1;
    for (dit1=dup.begin(); dit1 != dup.end(); dit1++)
      {
      strand_pair_type spm(Symmetry_action),spc(Symmetry_action);
      dit1->strands(spm,spc);
      *this += spm;
      *this += spc;
      }
    
    return *this;
    }

  template<class _VTp>
  inline operator std::deque<_VTp>(void) const
    {
    std::deque<_VTp> pd;
    std::copy(this->begin(),this->end(),std::back_inserter(pd));
    return pd;
    }

  /// Extractor for printing BasePair symbols
  inline operator std::string(void) const
    { 
    std::string res;
    typename StrandPairSequence<_InternalTp>::const_iterator dit;
    for(dit=this->begin(); dit != this->end(); ++dit)
      {
      res+=(std::string)(*dit)+" ";
      }
    return res;
    }

  /// Extractor for printing BasePair symbols
  inline friend std::ostream& operator<<(std::ostream &out,const StrandPairSequence<_InternalTp> &nbs)
    { 
    std::copy(nbs.begin(),nbs.end(),std::ostream_iterator<strand_pair_type>(out," "));
    return out;
    }

  /// Reduces to cheapest symmetry regardless of the setting Use_cheapest_symmetry
  inline bool reduce_to_smallest_symmetry(void)
    {
    bool reduced=false;
    typename base_class_type::iterator it;
    for (it=this->begin(); it != this->end(); ++it)
      {
      bool bp_reduced=it->reduce_to_smallest_symmetry();
      reduced=reduced || bp_reduced;
      }
    return reduced;
    }

  };//ends class


};//ends namespace
#endif
