//
// This file is part of MorphoGraphX - http://www.MorphoGraphX.org
// Copyright (C) 2012-2015 Richard S. Smith and collaborators.
//
// If you use MorphoGraphX in your work, please cite:
//   http://dx.doi.org/10.7554/eLife.05864
//
// MorphoGraphX is free software, and is licensed under under the terms of the 
// GNU General (GPL) Public License version 2.0, http://www.gnu.org/licenses.
// 
#ifndef MEMBER_ITERATOR_HPP
#define MEMBER_ITERATOR_HPP
/**
 * \file MemberIterator.hpp
 *
 * Defines the SelectMemberIterator class template
 */

#include <Config.hpp>

#include <iterator>

namespace mgx 
{
  template <class Iterator, class Derived> struct BaseIterator 
  {
    typedef Iterator base_iterator;
    /**
     * Type of the difference between two iterators
     */
    typedef typename std::iterator_traits<Iterator>::difference_type difference_type;
  
    /**
     * Default constructor
     */
    BaseIterator() : it() {}
  
    /**
     * Conversion from the base iterator
     */
    BaseIterator(const base_iterator& i) : it(i) {}
  
    /**
     * Copy constructor
     */
    BaseIterator(const BaseIterator& other) : it(other.it) {}
  
    ///\name 1 - Forward/input iterator methods
    //@{
  
    bool operator==(const Derived& other) const { return it == other.it; }
  
    bool operator!=(const Derived& other) const { return it != other.it; }
  
    /**
     * Assignment operator
     */
    Derived& operator=(const Derived& copy)
    {
      it = copy.it;
      return static_cast<Derived&>(*this);
    }
  
    /**
     * Prefix increment operator
     */
    Derived& operator++()
    {
      ++it;
      return static_cast<Derived&>(*this);
    }
    /**
     * Postfix increment operator
     */
    Derived& operator++(int)
    {
      Derived tmp(*this);
      ++it;
      return tmp;
    }
    //@}
  
    ///\name 2 - Bidirectional iterator methods
    //@{
    /**
     * Prefix decrement operator
     */
    Derived& operator--()
    {
      --it;
      return static_cast<Derived&>(*this);
    }
    /**
     * Postfix decrement operator
     */
    Derived& operator--(int)
    {
      Derived tmp(*this);
      --it;
      return tmp;
    }
    //@}
  
    ///\name 3 - Random access methods
    //@{
    /**
     * In-place random increment operator
     */
    Derived& operator+=(difference_type n)
    {
      it += n;
      return static_cast<Derived&>(*this);
    }
  
    /**
     * In-place random decrement operator
     */
    Derived& operator-=(difference_type n)
    {
      it -= n;
      return static_cast<Derived&>(*this);
    }
  
    bool operator<(const Derived& other) const {
      return it < other.it;
    }
  
    bool operator>(const Derived& other) const {
      return it > other.it;
    }
  
    bool operator<=(const Derived& other) const {
      return it <= other.it;
    }
  
    bool operator>=(const Derived& other) const {
      return it >= other.it;
    }
    //@}
  
    ///\name Functions for random access iterators
    //@{
    friend Derived operator+(const Derived& it, difference_type n)
    {
      Derived tmp(it);
      tmp += n;
      return tmp;
    }
  
    friend Derived operator+(difference_type n, const Derived& it)
    {
      Derived tmp(it);
      tmp += n;
      return tmp;
    }
  
    friend Derived operator-(const Derived& it, difference_type n)
    {
      Derived tmp(it);
      tmp -= n;
      return tmp;
    }
  
    friend Derived operator-(difference_type n, const Derived& it)
    {
      Derived tmp(it);
      tmp -= n;
      return tmp;
    }
  
    /**
     * Distance between two iterators
     */
    friend difference_type operator-(const Derived& last, const Derived& first) {
      return last.it - first.it;
    }
    //@}
  
    /**
     * Direct access to the base iterator
     */
    base_iterator base() const {
      return it;
    }
  
  protected:
    /**
     * Underlying iterator
     */
    base_iterator it;
  };
  
  /**
   * \class SelectMemberIterator MemberIterator.hpp <MemberIterator.hpp>
   *
   * \brief Iterate over a container of structure, dereferencing only a member
   * of it.
   *
   * This iterator is used when iterating over a container of a complexe
   * structure. It allows for dereferencing only a member of that structure.
   * A typical example is iterating over a map but dereferencing either the key
   * or the value:
   * \code
   * std::map<int,double> my_map;
   * typedef std::map<int,double>::iterator map_iterator;
   * typedef std::map<int,double>::value_type map_value;
   * SelectMemberIterator<map_iterator,const int, &map_value::first> key_iterator;
   * SelectMemberIterator<map_iterator,int,&map_value::second> data_iterator;
   * SelectMemberIterator<map_iterator,int,&map_value::second,const int&, const int*> data_const_iterator;
   * // Fill in my_map
   * for(key_iterator it = my_map.begin() ; it != my_map.end() ; ++it)
   * {
   *   cout << *it << endl; // Output the keys !
   * }
   * for(data_iterator it = my_map.begin() ; it != my_map.end() ; ++it)
   * {
   *   *it += 5;
   *   cout << *it << endl; // Output the data !
   * }
   * for(data_const_iterator it = my_map.begin() ; it != my_map.end() ; ++it)
   * {
   *   // It is not possible to modify the value pointed
   *   cout << *it << endl; // Output the data !
   * }
   * \endcode
   */
  template <class Iterator, class T, T std::iterator_traits<Iterator>::value_type::*member, class Reference = T&,
            class Pointer = T*>
  struct SelectMemberIterator
    : public BaseIterator<Iterator, SelectMemberIterator<Iterator, T, member, Reference, Pointer> > {
    /**
     * Type of the base class
     */
    typedef BaseIterator<Iterator, SelectMemberIterator<Iterator, T, member, Reference, Pointer> > Super;
    /**
     * Type of the underlying iterator
     */
    typedef Iterator base_iterator;
    /**
     * Category of the iterator
     */
    typedef typename std::iterator_traits<Iterator>::iterator_category iterator_category;
    /**
     * Type of the value iterated on
     */
    typedef T value_type;
    /**
     * Type of the difference between two iterators
     */
    typedef typename std::iterator_traits<Iterator>::difference_type difference_type;
    /**
     * Type of a reference on the values
     */
    typedef Reference reference;
    /**
     * Type of a pointer on the values
     */
    typedef Pointer pointer;
  
    /**
     * Default constructor
     */
    SelectMemberIterator() {
    }
  
    /**
     * Conversion from the base iterator
     */
    SelectMemberIterator(const base_iterator& i)
      : Super(i)
    {
    }
  
    /**
     * Copy constructor
     */
    SelectMemberIterator(const SelectMemberIterator& other)
      : Super(other)
    {
    }
  
    /**
     * Dereference operator
     */
    reference operator*() {
      return (*this->it).*member;
    }
    /**
     * Constant dereference operator
     */
    const reference operator*() const {
      return (*this->it).*member;
    }
  
    /**
     * Pointer-like arrow operator
     */
    pointer operator->() {
      return &((*this->it).*member);
    }
    /**
     * Pointer-like constant arrow operator
     */
    const pointer operator->() const {
      return &((*this->it).*member);
    }
  };
  
  template <typename T> struct remove_pointer;
  
  template <typename T> struct remove_pointer<T*> {
    typedef T type;
  };
  
  template <class Iterator, class T, T remove_pointer<typename std::iterator_traits<Iterator>::value_type>::type::*member,
            class Reference = T&, class Pointer = T*>
  struct SelectMemberPointerIterator
    : public BaseIterator<Iterator, SelectMemberPointerIterator<Iterator, T, member, Reference, Pointer> > {
    /**
     * Type of the base class
     */
    typedef BaseIterator<Iterator, SelectMemberPointerIterator<Iterator, T, member, Reference, Pointer> > Super;
    /**
     * Type of the underlying iterator
     */
    typedef Iterator base_iterator;
    /**
     * Category of the iterator
     */
    typedef typename std::iterator_traits<Iterator>::iterator_category iterator_category;
    /**
     * Type of the value iterated on
     */
    typedef T value_type;
    /**
     * Type of the difference between two iterators
     */
    typedef typename std::iterator_traits<Iterator>::difference_type difference_type;
    /**
     * Type of a reference on the values
     */
    typedef Reference reference;
    /**
     * Type of a pointer on the values
     */
    typedef Pointer pointer;
  
    /**
     * Default constructor
     */
    SelectMemberPointerIterator() {
    }
  
    /**
     * Conversion from the base iterator
     */
    SelectMemberPointerIterator(const base_iterator& i)
      : Super(i)
    {
    }
  
    /**
     * Copy constructor
     */
    SelectMemberPointerIterator(const SelectMemberPointerIterator& other)
      : Super(other)
    {
    }
  
    /**
     * Dereference operator
     */
    reference operator*() {
      return (**this->it).*member;
    }
    /**
     * Constant dereference operator
     */
    const reference operator*() const {
      return (**this->it).*member;
    }
  
    /**
     * Pointer-like arrow operator
     */
    pointer operator->() {
      return &((**this->it).*member);
    }
    /**
     * Pointer-like constant arrow operator
     */
    const pointer operator->() const {
      return &((**this->it).*member);
    }
  };
}
#endif
