// $Id: NeighbourSequence.h 998 2017-12-23 09:52:48Z ge $
/// \file NeighbourSequence.h
/// \brief contains the NeighbourSequence class
///
/// $Revision: 998 $
/// \author Gerald Weber

#ifndef GBC_EXP_NeighbourSequence_H
#define GBC_EXP_NeighbourSequence_H "$Id: NeighbourSequence.h 998 2017-12-23 09:52:48Z ge $"

#include "BasePairNeighbours.h"
#include "Duplex.h"

namespace gbc {

using namespace gbc;

template<class _InternalTp=char>
/// \brief Sequence (std::deque) of base pair neighbours.
///
/// This class differs from Duplex in several ways.
/// Duplex contains a std::deque of objects of type BasePair, while
/// NeighbourSequence contains a sequence of BasePairNeighbours.
class NeighbourSequence: public std::deque<BasePairNeighbours<_InternalTp> >
  {
  public:
    typedef _InternalTp                                       internal_type;             ///< type of _InternalTp
    typedef BasePair<_InternalTp>                             base_pair_type;             ///< type of _InternalTp
    typedef BasePairNeighbours<internal_type>                 base_pair_neighbours_type;
    typedef BasePairNeighbours<internal_type>                 pair_type;
    typedef std::deque<BasePairNeighbours<_InternalTp> >      base_class_type;
    typedef std::deque<base_pair_neighbours_type>             deque_type;                ///< the type of this class
    typedef unsigned long int                                 index_type;

    typename base_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_NeighbourSequence;

  NeighbourSequence(void)
    : Symmetry_action(base_pair_type::do_not_simplify_symmetry), Periodic(true), 
      Reference_NeighbourSequence(NULL) {}

  inline NeighbourSequence& operator+=(base_pair_neighbours_type bpn)
    {
    if (Symmetry_action == base_pair_type::simplify_symmetry) bpn.reduce_to_smallest_symmetry();
    if (Reference_NeighbourSequence != NULL)
      {
      typename deque_type::const_iterator dit;
      dit=std::find(Reference_NeighbourSequence->begin(),Reference_NeighbourSequence->end(),bpn);
      if (dit != Reference_NeighbourSequence->end()) back_inserter(*this)=*dit;
      else 
        {
        std::cerr << "Warning no reference for " << bpn << " found" << std::endl;
        back_inserter(*this)=bpn;
        }
      }
    else back_inserter(*this)=bpn;
    return *this;
    }

  /// Takes a Duplex (which stores base-pairs) and transforms it into nearest neighbours.
  inline NeighbourSequence& operator=(const Duplex<_InternalTp>& dup)
    {
    deque_type::clear();
    typename Duplex<_InternalTp>::const_iterator dit1=dup.begin(), dit2=dup.begin();
    for (dit2++; dit1 != dup.end(); dit1++, dit2++)
      {
      if (dit2 == dup.end()) 
        {
        if (Periodic) dit2=dup.begin();
        else          return *this;
        } // For constructing NeighbourSequence we consider sequenceas periodic
      BasePairNeighbours<_InternalTp> bpn(*dit1,*dit2);
      if (Symmetry_action == base_pair_type::simplify_symmetry) bpn.reduce_to_smallest_symmetry();
      *this+=bpn;
      }
    
    return *this;
    }

  inline NeighbourSequence& operator=(const NucleotideSequence<_InternalTp>& ns)
    {
    Duplex<_InternalTp> dup;
    dup=ns;
    (*this)=dup;
    return *this;
    }
    
  //Returns the last NN which is due to periodic boundary conditions
  //If Periodic=false, returns nothing
  inline base_pair_neighbours_type get_periodicity_neighbour(void)
    {
    if (Periodic) return *(this->rbegin());
    else          return base_pair_neighbours_type();
    }

  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 NeighbourSequence<_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 NeighbourSequence<_InternalTp> &nbs)
    { 
    std::copy(nbs.begin(),nbs.end(),std::ostream_iterator<base_pair_neighbours_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
