/*
    Copyright (C) 2000 PARAPET partners
    Copyright (C) 2000-2011, Hammersmith Imanet Ltd
    This file is part of STIR.

    SPDX-License-Identifier: Apache-2.0 AND License-ref-PARAPET-license

    See STIR/LICENSE.txt for details
*/
#ifndef __stir_FullArrayIterator__H__
#define __stir_FullArrayIterator__H__

/*!
  \file 
  \ingroup Array  
  \brief This file declares the stir::FullArrayIterator class.

  \author Kris Thielemans
  \author Alexey Zverovich
  \author PARAPET project
*/

#include "stir/common.h"
#include <iterator>

START_NAMESPACE_STIR

/*!
  \ingroup Array
  \brief Class FullArrayIterator implements (forward) iterators that go 
  through all elements of an Array.

  \warning This class should never be used explicitly, but only through
  the typedefs Array::full_iterator et al.

  \warning This class is effectively superseded by NestedIterator, although Array still uses FullArrayIterator.

  This implementation assumes that \a restiterT has begin_all() and end_all() members.
  Moreover, for the usual \c for loops to work, there is a requirement on how the 
  class that uses FullArrayIterator implements end_all(). See the implementation of
  Array::end_all(). 
  
  \internal
*/
template <typename topleveliterT, typename restiterT, typename elemT, typename _Ref, typename _Ptr>
class FullArrayIterator
{
public:
  typedef std::forward_iterator_tag iterator_category;
  typedef std::ptrdiff_t difference_type;

  typedef elemT value_type;
  typedef _Ref reference;
  typedef _Ptr pointer;  

public:
  //! default constructor
  inline FullArrayIterator();

  //! copy constructor
  inline FullArrayIterator(const FullArrayIterator&);

  //! constructor to initialise the members
  inline FullArrayIterator(const topleveliterT& top_level_iter, 
                           const topleveliterT& last_top_level_iter,
                           const restiterT& rest_iter,
                           const restiterT& last_rest_iter);

  //! some magic trickery to be able to assign iterators to const iterators etc, but not to incompatible types
  /*! Ignore the 2nd and 3rd argument. They are there to let the compiler check if the types are 
      convertible (using the SFINAE principle).
  */
  template <typename othertopleveliterT, typename otherrestiterT, typename _otherRef, typename _otherPtr>
    FullArrayIterator(
                          FullArrayIterator<othertopleveliterT, otherrestiterT, elemT,  _otherRef, _otherPtr> const& other,
                          typename boost::enable_if_convertible<othertopleveliterT, topleveliterT>::type* = 0,
                          typename boost::enable_if_convertible<otherrestiterT, restiterT>::type* = 0)
    : current_top_level_iter(other.current_top_level_iter), 
      last_top_level_iter(other.last_top_level_iter), 
      current_rest_iter(other.current_rest_iter), 
      last_rest_iter(other.last_rest_iter) 
    {}


  //! constructor with 0 (only 0, not another number)
  /*! This is necessary to be able to set \a current_rest_iter in the case of 
      an empty range.  This should be used by Array::begin_all() and Array::end_all() 
      such that for an empty array, these 2 (full)iterators are guaranteed to be equal.
  
      Ideally, Array::end_all() would just call restiterT().
      However, when going down the recursion, you will end up with the '1D' case 
      where restiterT is an ordinary iterator, which in the case of Array<1,elemT> 
      is just an elemT*. Its default constructor does nothing.

      All these complications could be avoided by rewriting operator== as 
      indicated in the code there, but at the expense of worse performance.
  */    
  inline FullArrayIterator(unsigned);
  
  // assignment is generated by compiler
  //inline FullArrayIterator& operator=(const FullArrayIterator&);
  
  //! prefix increment
  inline FullArrayIterator& operator++();

  //! postfix increment
  inline FullArrayIterator operator++(int);

  //! comparison operator
  inline bool operator==(const FullArrayIterator&) const;
  inline bool operator!=(const FullArrayIterator&) const;
  
  //! dereferencing operator
  inline reference operator*() const;

  //! member-selection operator 
  inline pointer operator->() const;

# ifdef BOOST_NO_MEMBER_TEMPLATE_FRIENDS
public:
#else
private:   
  // needed for conversion constructor
  template <class,class,class,class,class> friend class FullArrayIterator;
#endif

  //! the \c topleveliterT iterator pointing to the current \a row
  topleveliterT current_top_level_iter;

  //! a \c topleveliterT iterator marking the end of the \a column
  topleveliterT last_top_level_iter;

  //! a \c restiterT iterator pointing to the current \a element in the \a row
  restiterT current_rest_iter;

  //! a \c restiterT iterator pointing to the end of the current \a row
  restiterT last_rest_iter;

};

END_NAMESPACE_STIR

#include "stir/FullArrayIterator.inl"

#endif
