/***************************************************************************
 *   Copyright (C) 2005 by Andreas Pokorny                                 *
 *   andreas.pokorny@biozentrum.uni-wuerzburg.de                           *
 *                                                                         *
 *   This file is part of profdist and cbcanalyzer                         *
 *                                                                         *
 *   Both profdist and cbcanalyzer are free software; you can redistribute * 
 *   it and/or modify it under the terms of the GNU General Public License * 
 *   as published by the Free Software Foundation; either version 2 of the * 
 *   License, or (at your option) any later version.                       *
 *                                                                         *
 *   Profdist and cbcanalyzer are distributed in the hope that it will be  *
 *   useful, but WITHOUT ANY WARRANTY; without even the implied warranty   *
 *   of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the      *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 ***************************************************************************/

#ifndef PROFDIST_CONCEPTS_H_INCLUDED
#define PROFDIST_CONCEPTS_H_INCLUDED

#include <boost/iterator/iterator_facade.hpp>
#include "parsed_sequence.h"

template <typename IteratorT> 
struct base_adaptor
{
  public:
    typedef IteratorT parse_iterator;
    typedef std::list<sequence<IteratorT> > list_type;
    typedef typename std::list<sequence<IteratorT> >::const_iterator const_sequence_id;
    base_adaptor( list_type & list ) 
      : list_(list) {}

    inline const_sequence_id begin() const 
    { return list_.begin(); }
    inline const_sequence_id end() const
    { return list_.end(); }
  protected:
    list_type & dat() {return list_;}
    list_type const & dat() const {return list_;}
  private:
    list_type & list_;
};

template <typename Base>
struct num_sequences : public Base
{
  num_sequences( typename Base::list_type & list ) 
    : Base(list) { }

  inline std::size_t get_num_sequences() const { return this->dat().size(); } 
};

template <typename Base>
struct num_nuc : public Base
{
  num_nuc( typename Base::list_type & list ) 
    : Base(list) { }

  inline std::size_t get_num_nuc( typename Base::const_sequence_id const& id ) const { return id->get_sequence_length(); } 
};

template <typename Base>
struct brace_fold_data : public Base
{
  brace_fold_data( typename Base::list_type & list ) 
    : Base(list) { }

  struct const_brace_iterator 
    : public boost::iterator_facade<const_brace_iterator,const char, boost::bidirectional_traversal_tag > 
  {
    typename Base::parse_iterator it;
    typedef typename std::list<typename sequence<typename Base::parse_iterator>::string_range>::const_iterator range_iterator;
    range_iterator current_range;
    typename Base::const_sequence_id current_sequence;

    const_brace_iterator( ) {}
    const_brace_iterator( typename Base::parse_iterator i, range_iterator c_r, typename Base::const_sequence_id c_s )
      : it(i), current_range(c_r), current_sequence(c_s) {}

    inline char const& dereference() const{ return *it; }
    inline bool equal( const_brace_iterator const& y ) const { return it == y.it; }
    inline void increment() 
    {
      if( current_range->second == ++it ) 
        if( ++current_range != current_sequence->char_fold_data.end() )
          it = current_range->first;
    }
    inline void decrement()
    {
      if( current_range->first == it ) 
      {
        if( current_range != current_sequence->char_fold_data.begin() )
        {
          --current_range;
          it = current_range->second;
          --it;
        }
        else --it; // not quite sure
      }
      else 
        --it;
    }
  };
  
  inline const_brace_iterator brace_begin( typename Base::const_sequence_id & id ) const 
  { return const_brace_iterator( id->char_fold_data.begin()->first, id->char_fold_data.begin(), id ); } 
  inline const_brace_iterator brace_end( typename Base::const_sequence_id & id ) const
  { return const_brace_iterator( (--(id->char_fold_data.end()))->second, --(id->char_fold_data.end()), id ); } 
};


template <typename Base>
struct name_data : public Base
{
  name_data( typename Base::list_type & list ) 
    : Base(list) { }

  typedef typename Base::list_type::value_type::string_range string_range;
  inline string_range get_name( typename Base::const_sequence_id & id ) const 
  { return id->id; } 

  struct const_name_iterator
    : public boost::iterator_facade<const_name_iterator,const string_range, boost::bidirectional_traversal_tag > 
  {
    typename Base::const_sequence_id it;
    explicit const_name_iterator( typename Base::const_sequence_id i ) : it(i) {}
    inline string_range const& dereference() const {return it->id;}
    inline bool equal( const_name_iterator const& y ) const {return it == y.it;}
    inline void increment() {++it;}
    inline void decrement() {--it;}
  };

  inline const_name_iterator name_begin() const {return const_name_iterator(this->begin()); }
  inline const_name_iterator name_end() const {return const_name_iterator(this->end());}
};

template <typename Base>
struct sequence_data : public Base
{
  sequence_data( typename Base::list_type & list ) 
    : Base(list) { }

  struct const_sequence_iterator 
    : public boost::iterator_facade<const_sequence_iterator, const char, boost::bidirectional_traversal_tag > 
  {
    typename Base::parse_iterator it;
    typedef typename std::list<typename sequence<typename Base::parse_iterator>::string_range>::const_iterator range_iterator;
    range_iterator current_range;
    typename Base::const_sequence_id current_sequence;

    const_sequence_iterator( ) {}
    const_sequence_iterator( typename Base::parse_iterator i, range_iterator c_r, typename Base::const_sequence_id c_s )
      : it(i), current_range(c_r), current_sequence(c_s) {}

    inline char const& dereference() const { return *it; }
    inline bool equal( const_sequence_iterator const& y ) const { return it == y.it; }
    inline void increment() 
    {
      if( current_range->second == ++it ) 
        if( ++current_range != current_sequence->sequence_data.end() )
          it = current_range->first;
    }
    inline void decrement()
    {
      if( current_range->first == it ) 
      {
        if( current_range != current_sequence->sequence_data.begin() )
        {
          --current_range;
          it = current_range->second;
          --it;
        }
        else --it; // not quite sure
      }
      else 
        --it;
    }
  };
  
  inline const_sequence_iterator sequence_begin( typename Base::const_sequence_id & id ) const 
  { return const_sequence_iterator( id->sequence_data.begin()->first, id->sequence_data.begin(), id); } 
  inline const_sequence_iterator sequence_end( typename Base::const_sequence_id & id ) const
  { return const_sequence_iterator( (--(id->sequence_data.end()))->second, --(id->sequence_data.end()), id); } 
};


#endif

