/*!
 * \file    utl/dev/dev_iterators.h
 * \brief   Iterator collection for devices.
 *
 * Copyright (C) 2018 Christos Choutouridis
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation, either version 3
 * of the License, or (at your option) any later version.
 *
 * This program is 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 Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 */
#ifndef __utl_dev_dev_iterators_h__
#define __utl_dev_dev_iterators_h__

#include <utl/core/impl.h>
#include <utl/meta/meta.h>

namespace utl {

/*!
 * \ingroup Devices
 * \brief   Device iterator collection.
 */
//!@{

   /*!
    * \brief  Traits class for dev_iterators.
    *
    * This class does nothing but define nested typedefs.  The general
    * version simply @a forwards the nested typedefs from the Iterator
    * argument.
    */
   template <typename _Cat, typename _Tp, typename _Diff =ptrdiff_t>
   struct dev_iterator_traits {
      using iterator_category = _Cat;
      using value_type        = _Tp;
      using difference_type   = _Diff;
      using pointer           = _Tp*;
      using reference         = _Tp&;
   };

   /*!
    * \brief  Iterator tags [std.iterator.tags]
    * Extension: contiguous_iterator_tag for denoting contiguous iterators.
    */
   struct output_iterator_tag {};
   struct input_iterator_tag {};
   struct forward_iterator_tag       : input_iterator_tag {};
   struct bidirectional_iterator_tag : forward_iterator_tag {};
   struct random_access_iterator_tag : bidirectional_iterator_tag {};
   struct contiguous_iterator_tag    : random_access_iterator_tag {};



   /*
    * ================ Output device Iterator =================
    */

   template <typename cont_t, typename data_t>
   class ostreamdev_it {
      using iterator_t = ostreamdev_it <cont_t, data_t>; //! iterator type local name
   public:
      using type       = iterator_t;                     //!< Export type as identity meta-function
   //! STL iterator traits "forwarding"
   //!@{
   public:
      using iterator_category = typename dev_iterator_traits <std::output_iterator_tag, data_t, void>::iterator_category;
      using value_type        = typename dev_iterator_traits <std::output_iterator_tag, data_t, void>::value_type;
      using difference_type   = typename dev_iterator_traits <std::output_iterator_tag, data_t, void>::difference_type;
      using pointer           = typename dev_iterator_traits <std::output_iterator_tag, data_t, void>::pointer;
      using reference         = typename dev_iterator_traits <std::output_iterator_tag, data_t, void>::reference;
   //!@}

   private:
      cont_t*  owner_ {nullptr}; /*!<
                                  * Pointer to parent/owner device class. Usable iterator demands
                                  * owner container in order to access data. Considering the data
                                  * don't "live" in memory. A default constructed iterator will behave
                                  * like end() just like the input version does.
                                  */
   /*!
    * \name Constructor / Destructor
    */
   //!@{
   public:
      //! \brief   Basic constructor
      ostreamdev_it (cont_t* owner =nullptr) noexcept
         : owner_ {owner} { }
      //! \brief   Basic copy constructor
      ostreamdev_it (const iterator_t& it) noexcept
         : owner_ {const_cast<cont_t*>(it.owner_)} { }
      //! \brief   Basic copy assignment operator
      iterator_t& operator= (const iterator_t& it) noexcept {
         owner_ = const_cast<cont_t*>(it.owner_);
         return *this;
      }
   //!@}

   //!@{ \name Public interface
   public:
      iterator_t& operator* () noexcept { return *this; }
      /*!
       * \brief
       *    Value-assignment operation. Where the output method is invoked
       *  \param  value    An instance of Cont_t::data_type
       *  \return This %iterator, for chained operations.
       */
      iterator_t& operator= (const value_type& value) {
         if (owner_ != nullptr)
            owner_->put (value);
         return *this;
      }
      iterator_t& operator++ () noexcept { return *this; }
      iterator_t& operator++ (int) noexcept { return *this; }
   //!@}
   };

   template <typename cont_t, typename data_t>
   class istreamdev_it {
      using iterator_t = istreamdev_it <cont_t, data_t>; //! iterator type local name
   public:
      using type       = iterator_t;                     //!< Export type as identity meta-function
   //! STL iterator traits "forwarding"
   //!@{
   public:
      using iterator_category = typename dev_iterator_traits <std::output_iterator_tag, data_t, void>::iterator_category;
      using value_type        = typename dev_iterator_traits <std::output_iterator_tag, data_t, void>::value_type;
      using difference_type   = typename dev_iterator_traits <std::output_iterator_tag, data_t, void>::difference_type;
      using pointer           = typename dev_iterator_traits <std::output_iterator_tag, data_t, void>::pointer;
      using reference         = typename dev_iterator_traits <std::output_iterator_tag, data_t, void>::reference;
   //!@}
      //! #define-like enumerator for Cursor-like behavior
      enum Cursor {
         init = 0,
         valid = 1,
         eos = 2
      };
   //!@{ \name Data members
   private:
      cont_t*    owner_ {nullptr};  /*!<
                                     * Pointer to parent/owner device class. Usable iterator demands
                                     * owner container in order to access data. Considering the data
                                     * don't "live" in memory.
                                     */
      value_type value_ {};
      Cursor     cursor_ {init};
   //!@}
   /*!
    * \name Constructor / Destructor
    */
   //!@{
   public:
      //! \brief   Basic constructor
      istreamdev_it (cont_t* owner =nullptr, Cursor cursor =eos) noexcept :
         owner_ {owner},
         value_ {},
         cursor_{cursor} { }
      //! \brief   Basic copy constructor
      istreamdev_it (const iterator_t& it) noexcept :
         owner_ {const_cast<cont_t*>(it.owner_)},
         value_ {it.value_},
         cursor_ {it.cursor_} { }
      //! \brief   Basic copy assignment operator
      iterator_t& operator= (const iterator_t& it) noexcept {
         owner_ = const_cast<cont_t*>(it.owner_);
         value_ = it.value_;
         cursor_ = it.cursor_;
         return *this;
      }
   //!@}

   //!@{ \name Public interface
   public:
      value_type& operator* () noexcept {
         if (cursor_ == init)
            ++*this;
         return value_;
      }
      value_type* operator->() noexcept {
         if (cursor_ == init)
            ++*this;
         return &value_;
      }
      iterator_t& operator++ () noexcept {
         _get(value_);
         return *this;
      }
      iterator_t operator++ (int) noexcept {
         iterator_t r = *this;
         _get(value_);
         return r;
      }

      //! Export container for comparison
      const cont_t* owner () const noexcept { return owner_; }
      //! Export value for comparison
      const value_type& value () const noexcept { return value_; }
      //! Export cursor for comparison
      const Cursor cursor () const noexcept { return cursor_; }
   //!@}

   //!@{ \name private api
   private:
      void _get(value_type& v) {
         owner_->get(v);
         cursor_ = (v) ? valid : eos;
      }
   //!@}
   };

   /*!
    * \brief
    *    Equality comparison template so that comparison between cv-qualified and
    *    non-cv-qualified iterators be valid
    * \param lhs  Left hand site iterator
    * \param rhs  Right hand site iterator
    * \return     True in equality
    */
   template<typename _C1, typename _D1, typename _C2, typename _D2>
   inline bool operator== (const istreamdev_it<_C1, _D1>& lhs,
                           const istreamdev_it<_C2, _D2>& rhs) noexcept {
      return ((lhs.owner() == rhs.owner()) &&
              (lhs.value() == rhs.value()) &&
              (lhs.cursor() == rhs.cursor()));
   }
   template<typename _C1, typename _D1, typename _C2, typename _D2>
   inline bool operator!= (const istreamdev_it<_C1, _D1>& lhs,
                           const istreamdev_it<_C2, _D2>& rhs) noexcept {
      return !operator==(lhs, rhs);
   }

   /*!
    * \brief
    *    Output device iterator type.  We "future call" interface methods
    *    from owner class to provide iterator functionality.
    * \param   cont_t      Container/parent type
    * \param   iter_t      Iterator data type (pointer to container_t::value_type)
    * \param   devsize     Device's address space size
    */
   template<typename cont_t, typename iter_t, index_t _beg =0, index_t _end =static_cast<index_t>(-1)>
   class outdev_it {
      //! iterator type local name
      using iterator_t = outdev_it <cont_t, iter_t, _beg, _end>;
   public:
      using type       = iterator_t;    //!< Export type as identity meta-function
   //! STL iterator traits "forwarding"
   //!@{
   public:
      using iterator_category = typename dev_iterator_traits <std::output_iterator_tag, iter_t>::iterator_category;
      using value_type        = typename dev_iterator_traits <std::output_iterator_tag, iter_t>::value_type;
      using difference_type   = typename dev_iterator_traits <std::output_iterator_tag, iter_t>::difference_type;
      using pointer           = typename dev_iterator_traits <std::output_iterator_tag, iter_t>::pointer;
      using reference         = typename dev_iterator_traits <std::output_iterator_tag, iter_t>::reference;
   //!@}
      //! #define-like enumerator for Cursor
      enum Cursor {
         beg   = _beg,     //!< Points the first item (relative address)
         eod   = _end,     //!< Points one place after last item (relative address)
      };
   private:
      cont_t*  owner_ {nullptr}; /*!<
                                  * Pointer to parent/owner device class. Usable iterator demands
                                  * owner container in order to access data. Considering the data
                                  * don't "live" in memory. A default constructed iterator will behave
                                  * like end() just like the input version does.
                                  */
      index_t  cursor_ {eod};    //!< virtual cursor for comparison operators
   /*!
    * \name Constructor / Destructor
    */
   //!@{
   public:
      //! \brief  Default constructor results to end()
      explicit outdev_it() noexcept :
         owner_ {nullptr},
         cursor_{eod} { }
      //! \brief   Basic constructor
      explicit outdev_it (cont_t* owner, index_t cursor =eod) noexcept :
         owner_ {owner},
         cursor_{cursor} { }
      //! \brief   Basic copy constructor
      explicit outdev_it (const iterator_t& it) noexcept :
         owner_ {const_cast<cont_t*>(it.owner_)},
         cursor_{it.cursor_} { }
      //! \brief   Basic copy assignment operator
      iterator_t& operator= (const iterator_t& it) noexcept {
         owner_ = const_cast<cont_t*>(it.owner_);
         cursor_ = it.cursor_;
      }
      //! \brief Iterator to const-iterator conversion (as STL requires)
      //! \param   it    Iterator reference
//      template<typename _It>
//      outdev_it (const outdev_it<
//                          use_if_same_t <_It, typename container_t::pointer_type, container_t>,
//                          _It,
//                          streamsize
//                       >& it) noexcept
//         : owner_ {const_cast<container_t*>(it.owner())},
//           cursor_ {it.cursor()} { }
   //!@}


   //!@{ \name Public interface
   public:
      iterator_t& operator* () noexcept { return *this; }
      /*!
       * \brief
       *    Value-assignment operation. Where the output method is invoked
       *  \param  value    An instance of Cont_t::data_type
       *  \return This %iterator, for chained operations.
       */
      iterator_t& operator= (const value_type& value) {
         // end() and default constructible iterators are not dereferenceable
         if (cursor_ != eod)
            owner_->put (value, cursor_);
         return *this;
      }

      //!@{ \name ++ operators
      iterator_t& operator++ () noexcept {
         ++cursor_;
         return *this;
      }
      iterator_t operator++ (int) noexcept {
         iterator_t ret = *this;
         ++cursor_;
         return ret;
      }
      //!@}

      //! Export container for comparison
      const cont_t*  owner () const noexcept { return owner_; }
      //! Export cursor for comparison
      const index_t cursor () const noexcept { return cursor_; }
   //!@}
   };

   /*!
    * \note
    *    The following are not requirements for output iterator.
    *    We provide them nevertheless.
    * \warn
    *    Required: The rhs and lhs MUST belong to the same owner or
    *    the result is undefined.
    */
   //!@{
   /*!
    * \brief
    *    Equality comparison template so that comparison between cv-qualified and
    *    non-cv-qualified iterators be valid
    * \param lhs  Left hand site iterator
    * \param rhs  Right hand site iterator
    * \return     True in equality
    */
   template<typename _Cont1, typename _It1, index_t _beg1, index_t _end1,
            typename _Cont2, typename _It2, index_t _beg2, index_t _end2>
   inline bool operator== (const outdev_it<_Cont1, _It1, _beg1, _end1>& lhs,
                           const outdev_it<_Cont2, _It2, _beg2, _end2>& rhs) noexcept {
      return ((lhs.cursor() == rhs.cursor()) &&
              (lhs.owner() == rhs.owner()));
   }

   /*!
    * \brief
    *    Inequality comparison template so that comparison between cv-qualified and
    *    non-cv-qualified iterators be valid
    * \param lhs  Left hand site iterator
    * \param rhs  Right hand site iterator
    * \return     True in inequality
    */
   template<typename _Cont1, typename _It1, index_t _beg1, index_t _end1,
            typename _Cont2, typename _It2, index_t _beg2, index_t _end2>
   inline bool operator!= (const outdev_it<_Cont1, _It1, _beg1, _end1>& lhs,
                           const outdev_it<_Cont2, _It2, _beg2, _end2>& rhs) noexcept {
      return !(lhs == rhs);
   }
   //!@}

   /*!
    * Output device iterator concept
    */
   //! @{
   #if defined _utl_have_concepts
      template <typename T>
      concept bool Outdev_it = requires (T t) {
         // STL compatibility
         typename T::value_type;
         typename T::difference_type;
         typename T::pointer;
         typename T::reference;
         requires same_<
            typename T::iterator_category,
            std::output_iterator_tag
         >::value;
         {*t} -> auto&&;   // is dereferencable, and return non-void
         {++t} -> T&;      // is incrementable
         {t++} -> T;
         // Extras
         {t.owner()} ->auto&&;
         {t.cursor()} -> size_t;
      };
   #else
      namespace outdev_it_details {
         using std::declval;

         //! Primary template to catch any non SPI interface types
         template <typename _Tp, typename =void>
         struct is_outdev_it_ : meta::false_ {};

         //! template to catch a proper SPI interface type
         template <typename _Tp>
         struct is_outdev_it_ <
            _Tp,
            meta::void_t <
               typename _Tp::value_type,
               typename _Tp::difference_type,
               typename _Tp::pointer,
               typename _Tp::reference,
//               meta::use_if_same_t <
//                  typename _Tp::iterator_category,
//                  std::output_iterator_tag
//               >
            void
            >
         > : meta::true_ {};
      }
      /*!
       * Value meta-programming function for SPI interface checking
       * \param   _Tp  Type to check
       * \return  True if _Tp is a spi interface
       */
//      template <typename _Tp>
//      constexpr bool Outdev_it = outdev_it_details::is_outdev_it_<_Tp>::value;
   #endif
   //! @}


   /*
    * ================ Input device Iterator =================
    */

   /*!
    * \brief
    *    Input device iterator type.  We "future call" interface methods
    *    from owner class to provide iterator functionality.
    * \param   cont_t      Container/parent type
    * \param   iter_t      Iterator data type (pointer to container_t::value_type)
    * \param   _beg        Device starting address
    * \param   _size       Device's address space size
    */
   template<typename cont_t, typename iter_t, index_t _beg =0, size_t _size = static_cast<size_t>(-1)-_beg>
   class indev_it {
      //!< iterator type local name
      using iterator_t = indev_it <cont_t, iter_t, _beg, _size>;
   public:
      using type       = iterator_t;    //!< Export type as identity meta-function
   //! STL iterator traits "forwarding"
   //!@{
   public:
      using iterator_category = typename dev_iterator_traits <std::output_iterator_tag, iter_t>::iterator_category;
      using value_type        = typename dev_iterator_traits <std::output_iterator_tag, iter_t>::value_type;
      using difference_type   = typename dev_iterator_traits <std::output_iterator_tag, iter_t>::difference_type;
      using pointer           = typename dev_iterator_traits <std::output_iterator_tag, iter_t>::pointer;
      using reference         = typename dev_iterator_traits <std::output_iterator_tag, iter_t>::reference;
   //!@}

      //! #define-like enumerator for Cursor
      enum Cursor {
         beg = _beg,          //!< Points the first item
         eod = _beg + _size,  //!< Points one place after last item
      };

   private:
      cont_t*      owner_{nullptr}; /*!<
                                     * Pointer to parent/owner device class. Constructor demands
                                     * owner container in order to access data. Considering the data
                                     * don't "live" in memory.
                                     */
      index_t      cursor_{eod};    //!< virtual cursor for comparison operators
      value_type   value_ {};       //!< The current value, used as cache

   /*!
    * \name Constructor / Destructor
    * \note
    *    We can not provide a default constructor as long as we depend
    *    on container_t (the owner type).
    */
   //!@{
   public:
      //! \brief  Default constructor
      indev_it () noexcept :
         owner_ {nullptr},
         cursor_{eod},
         value_ {} { }
      //! \brief  Basic constructor
      explicit indev_it (cont_t* own, size_t cur =eod) noexcept :
         owner_ {own},
         cursor_{cur},
         value_ {} { }
      //! \brief   Basic copy constructor
      explicit indev_it (const iterator_t& it) noexcept :
         owner_ {const_cast<cont_t*>(it.owner_)},
         cursor_{it.cursor_},
         value_ {it.value_} { }
      //! \brief   Basic copy assignment operator
      iterator_t& operator= (const iterator_t& it) noexcept {
        owner_  = const_cast<cont_t*>(it.owner_);
        cursor_ = it.cursor_;
        value_  = it.value_;
      }
      //! \brief Iterator to const-iterator conversion (as STL requires)
      //! \param   it    Iterator reference
//      template<typename _It>
//      indev_it (const indev_it<
//                         use_if_same_t <_It, typename container_t::pointer_type, container_t>,
//                         _It,
//                         streamsize
//                      >& it) noexcept
//         : owner_ {const_cast<container_t*>(it.owner())},
//           cursor_{it.cursor()},
//           value_ {it.value()} { }

   //!@}

   //!@{ \name Public interface
   public:
      //! De-reference operator. No end() place dereference check is made.
      reference operator* () {
         owner_->get (value_, cursor_);
         return value_;
      }
      //! Arrow operator. No end() place dereference check is made.
      pointer operator-> () {
         owner_->get (value_, cursor_);
         return &value_;
      }

      //! Pre increment.
      iterator_t& operator++ () {
         ++cursor_;
         return *this;
      }
      //! Post increment.
      iterator_t operator++ (int) {
         iterator_t ret(*this);
         ++cursor_;
         return *this;
      }

      //! Pre decrement.
      iterator_t& operator-- () {
         --cursor_;
         return *this;
      }
      //! Post decrement.
      iterator_t operator-- (int) {
         iterator_t ret(*this);
         --cursor_;
         return *this;
      }
      //! [] operator. Is a combination of input method and dereference
      reference operator[] (difference_type n) {
         owner_->get (value_, cursor_ += n);
         return value_;
      }

      iterator_t& operator+= (difference_type n) {
         cursor_ += n;
         return *this;
      }
      iterator_t operator+ (difference_type n) {
         return iterator_t (owner_, cursor_ + n);
      }
      iterator_t& operator-= (difference_type n) {
         cursor_ -= n;
         return *this;
      }
      iterator_t operator- (difference_type n) {
         return iterator_t (owner_, cursor_ - n);
      }
      //! Export container for comparison
      const cont_t*  owner () const noexcept { return owner_; }
      //! Export cursor for comparison
      const index_t cursor () const noexcept { return cursor_; }
      //! Export value for comparison
      //const reference value () const noexcept { return value_; }
   //!@}
   };

   /*!
    *  \name indev_it EqualityComparable && LessThanComparable requirements
    *  comparison template so that comparison between cv-qualified and
    *  non-cv-qualified iterators be valid
    */
   //!@{
   //! Equality
   template<typename _Cont1, typename _It1, index_t _beg1, size_t _size1,
            typename _Cont2, typename _It2, index_t _beg2, size_t _size2>
   inline bool operator== (const indev_it<_Cont1, _It1, _beg1, _size1>& x,
                           const indev_it<_Cont2, _It2, _beg2, _size2>& y) noexcept {
      return (x.owner()  == y.owner()   &&
             (x.cursor() == y.cursor()));
   }
   //! Less than
   template<typename _Cont1, typename _It1, index_t _beg1, size_t _size1,
            typename _Cont2, typename _It2, index_t _beg2, size_t _size2>
   inline bool operator< (const indev_it<_Cont1, _It1, _beg1, _size1>& x,
                          const indev_it<_Cont2, _It2, _beg2, _size2>& y) noexcept {
      return (x.cursor() < y.cursor());
   }

   // relative ops
   template<typename _Cont1, typename _It1, index_t _beg1, size_t _size1,
            typename _Cont2, typename _It2, index_t _beg2, size_t _size2>
   inline bool operator!= (const indev_it<_Cont1, _It1, _beg1, _size1>& x,
                           const indev_it<_Cont2, _It2, _beg2, _size2>& y) noexcept {
      return !(x == y);
   }
   template<typename _Cont1, typename _It1, index_t _beg1, size_t _size1,
            typename _Cont2, typename _It2, index_t _beg2, size_t _size2>
   inline bool operator<= (const indev_it<_Cont1, _It1, _beg1, _size1>& x,
                           const indev_it<_Cont2, _It2, _beg2, _size2>& y) noexcept {
      return !(y < x);
   }
   template<typename _Cont1, typename _It1, index_t _beg1, size_t _size1,
            typename _Cont2, typename _It2, index_t _beg2, size_t _size2>
   inline bool operator> (const indev_it<_Cont1, _It1, _beg1, _size1>& x,
                          const indev_it<_Cont2, _It2, _beg2, _size2>& y) noexcept {
      return (y < x);
   }
   template<typename _Cont1, typename _It1, index_t _beg1, size_t _size1,
            typename _Cont2, typename _It2, index_t _beg2, size_t _size2>
   inline bool operator>= (const indev_it<_Cont1, _It1, _beg1, _size1>& x,
                           const indev_it<_Cont2, _It2, _beg2, _size2>& y) noexcept {
      return !(x < y);
   }
   //!@}

   //! @{ \name iterator arithmetic
   template<typename _Cont1, typename _It1, index_t _beg1, size_t _size1,
            typename _Cont2, typename _It2, index_t _beg2, size_t _size2>
   inline auto operator- (indev_it<_Cont1, _It1, _beg1, _size1>& x,
                          indev_it<_Cont1, _It1, _beg1, _size1>& y) noexcept
   -> decltype (x.cursor() - y.cursor()) {
      return (x.cursor() - y.cursor());
   }

   template<typename _Cont, typename _It, index_t _beg, size_t _size>
   inline indev_it<_Cont, _It, _beg, _size>
      operator+ (typename _Cont::difference_type n,
                 indev_it<_Cont, _It, _beg, _size>& it) noexcept {
      return indev_it<_Cont, _It, _beg, _size>(it.owner(), it.cursor() + n);
   }
   //! @}

   /*!
    * Input device iterator concept
    */
   //! @{
   #if defined _utl_have_concepts
      template <typename T>
      concept bool Indev_it = requires (T t, const T ct) {
         // STL compatibility
         typename T::value_type;
         typename T::difference_type;
         typename T::pointer;
         typename T::reference;
         requires same_ <
            typename T::iterator_category,
            std::input_iterator_tag
         >::value;
         {*t} -> typename T::value_type;   // is dereferencable
         {++t} -> T&;      // is incrementable
         {t++} -> T;
         // Extras
         {ct.owner()} ->auto&&;
         {ct.cursor()} -> const size_t;
         {ct.value()} -> auto&&;
      };
   #else
      namespace indev_it_details {
         using std::declval;

         //! Primary template to catch any non SPI interface types
         template <typename _Tp, typename =void>
         struct is_indev_it_ : meta::false_ {};

         //! template to catch a proper SPI interface type
         template <typename _Tp>
         struct is_indev_it_ <
            _Tp,
            meta::void_t <
               typename _Tp::value_type,
               typename _Tp::difference_type,
               typename _Tp::pointer,
               typename _Tp::reference,
//               meta::use_if_same_t <
//                  typename _Tp::iterator_category,
//                  std::input_iterator_tag
//               >
               void
            >
         > : meta::true_ {};
      }
      /*!
       * Value meta-programming function for SPI interface checking
       * \param   _Tp  Type to check
       * \return  True if _Tp is a spi interface
       */
//      template <typename _Tp>
//      constexpr bool Indev_it = indev_it_details::is_indev_it_<_Tp>::value;
   #endif
   //! @}

   /*
    * ================= Indexed device Iterator =================
    */

   /*!
    * \brief
    *    Indexed device iterator type. We "future call" interface methods
    *    from owner class to provide iterator functionality.
    * \note
    *    This is a contiguous iterator
    * \param   cont_t   Container/parent type
    * \param   iter_t   Iterator data type (pointer to container_t::value_type)
    * \param   _beg     Starting address of the device
    * \param   _size    Device's address space size
    */
   template<typename cont_t, typename iter_t, index_t _beg =0, size_t _size = static_cast<size_t>(-1)-_beg>
   class iodev_it {
      //!< iterator type local name
      using iterator_t = iodev_it <cont_t, iter_t, _beg, _size>;
   public:
      using type       = iterator_t;    //!< Export type as identity meta-function
   //! STL iterator traits "forwarding"
   //!@{
   public:
      using iterator_category = typename dev_iterator_traits <std::output_iterator_tag, iter_t>::iterator_category;
      using value_type        = typename dev_iterator_traits <std::output_iterator_tag, iter_t>::value_type;
      using difference_type   = typename dev_iterator_traits <std::output_iterator_tag, iter_t>::difference_type;
      using pointer           = typename dev_iterator_traits <std::output_iterator_tag, iter_t>::pointer;
      using reference         = typename dev_iterator_traits <std::output_iterator_tag, iter_t>::reference;
   //!@}

      //! #define-like enumerator for Cursor
      enum Cursor {
         beg = _beg,          //!< Points the first item
         eod = _beg+_size,    //!< Points one place after last item
      };

   private:
      cont_t*      owner_{nullptr}; /*!<
                                     * Pointer to parent/owner device class. Constructor demands
                                     * owner container in order to access data. Considering the data
                                     * don't "live" in memory.
                                     */
      index_t      cursor_{eod};    //!< virtual cursor for comparison operators
      /*!
       * Current value wrapper type, to used as put event catcher
       * owner_->get    : Use v_ directly
       * owner_->put    : Use operator= to a dereferenced iterator
       */
      struct value_type_t {
         value_type v_;                //!< Current value buffer to access via get
         value_type_t(value_type v =value_type{}) :
            v_{v} { }
         operator value_type() { return v_; }
         operator reference()  { return v_; }
         value_type& operator= (const value_type& v) {
            //!< Catch any attempt to write to value_ and sync the data back to device
            owner_->put (v_ =v, cursor_);
         }
      } value_ {};
   /*!
    * \name Constructor / Destructor
    */
   //!@{
   public:
      //! \brief  Default constructor
      iodev_it () noexcept :
         owner_ {nullptr},
         cursor_{eod},
         value_ {} { }
      //! \brief  Basic constructor
      explicit iodev_it (cont_t* owner, size_t cursor =eod) noexcept :
         owner_ {owner},
         cursor_{cursor},
         value_ {} { }
      //! \brief   Basic copy constructor
      explicit iodev_it (const iterator_t& it) noexcept :
         owner_ {const_cast<cont_t*>(it.owner_)},
         cursor_{it.cursor_},
         value_ {it.value_} { }
      //! \brief   Basic copy assignment operator
      iterator_t& operator= (const iterator_t& it) noexcept {
        owner_  = const_cast<cont_t*>(it.owner_);
        cursor_ = it.cursor_;
        value_  = it.value_;
      }
   //!@}

   //!@{ \name Public interface
   public:
      /*!
       * De-reference operator. This is where the input method is invoked
       * \note
       *    No end() place dereference check is made.
       */
      reference operator* () noexcept {
         owner_->get (value_.v_, cursor_);
         return value_;
      }
      //! Arrow operator. This is where the input method is invoked
      pointer operator-> () noexcept {
         owner_->get (value_.v_, cursor_);
         return &value_;
      }

      //! Pre increment.
      iterator_t& operator++ () noexcept {
         ++cursor_;
         return *this;
      }
      //! Post increment.
      iterator_t operator++ (int) noexcept {
         iterator_t ret = *this;
         ++cursor_;
         return *this;
      }
      //! Pre decrement.
      iterator_t& operator-- () noexcept {
         --cursor_;
         return *this;
      }
      //! Post decrement.
      iterator_t operator-- (int) noexcept {
         iterator_t ret = *this;
         --cursor_;
         return *this;
      }
      //! Random access through iterator
      reference operator[] (difference_type n) noexcept {
         owner_->get (value_.v_, cursor_ = n);
         return value_;
      }
      //! Random cursor increment
      iterator_t& operator+= (difference_type n) noexcept {
         cursor_ += n;
         return *this;
      }
      //! Addition operation
      iterator_t operator+ (difference_type n) const noexcept {
         return iterator_t (owner_, cursor_ + n);
      }
      //! Random cursor decrement
      iterator_t& operator-= (difference_type n) noexcept {
         cursor_ -= n;
         return *this;
      }
      //! Subtraction operation
      iterator_t operator- (difference_type n) const noexcept {
         return iterator_t (owner_, cursor_ - n);
      }

      //! Export container for comparison
      const cont_t*   owner () const noexcept { return owner_; }
      //! Export cursor for comparison
      const index_t& cursor () const noexcept { return cursor_; }
      //! Export value for comparison
      //const value_type_t& value () const noexcept { return value_; }
   //!@}
   };

   /*!
    *  \name indev_it EqualityComparable && LessThanComparable requirements
    *  comparison template so that comparison between cv-qualified and
    *  non-cv-qualified iterators be valid
    */
   //!@{
   //! Equality
   template<typename _Cont1, typename _It1, index_t _beg1, size_t _size1,
            typename _Cont2, typename _It2, index_t _beg2, size_t _size2>
   inline bool operator== (const iodev_it<_Cont1, _It1, _beg1, _size1>& x,
                           const iodev_it<_Cont2, _It2, _beg2, _size2>& y) noexcept {
      return (x.owner()  == y.owner()   &&
             (x.cursor() == y.cursor()));
   }
   //! Less than
   template<typename _Cont1, typename _It1, index_t _beg1, size_t _size1,
            typename _Cont2, typename _It2, index_t _beg2, size_t _size2>
   inline bool operator< (const iodev_it<_Cont1, _It1, _beg1, _size1>& x,
                          const iodev_it<_Cont2, _It2, _beg2, _size2>& y) noexcept {
      return (x.cursor() < y.cursor());
   }

   // relative ops
   template<typename _Cont1, typename _It1, index_t _beg1, size_t _size1,
            typename _Cont2, typename _It2, index_t _beg2, size_t _size2>
   inline bool operator!= (const iodev_it<_Cont1, _It1, _beg1, _size1>& x,
                           const iodev_it<_Cont2, _It2, _beg2, _size2>& y) noexcept {
      return !(x == y);
   }
   template<typename _Cont1, typename _It1, index_t _beg1, size_t _size1,
            typename _Cont2, typename _It2, index_t _beg2, size_t _size2>
   inline bool operator<= (const iodev_it<_Cont1, _It1, _beg1, _size1>& x,
                           const iodev_it<_Cont2, _It2, _beg2, _size2>& y) noexcept {
      return !(y < x);
   }
   template<typename _Cont1, typename _It1, index_t _beg1, size_t _size1,
            typename _Cont2, typename _It2, index_t _beg2, size_t _size2>
   inline bool operator> (const iodev_it<_Cont1, _It1, _beg1, _size1>& x,
                          const iodev_it<_Cont2, _It2, _beg2, _size2>& y) noexcept {
      return (y < x);
   }
   template<typename _Cont1, typename _It1, index_t _beg1, size_t _size1,
            typename _Cont2, typename _It2, index_t _beg2, size_t _size2>
   inline bool operator>= (const iodev_it<_Cont1, _It1, _beg1, _size1>& x,
                           const iodev_it<_Cont2, _It2, _beg2, _size2>& y) noexcept {
      return !(x < y);
   }
   //!@}

   //! @{ \name iterator arithmetic
   template<typename _Cont1, typename _It1, index_t _beg1, size_t _size1,
            typename _Cont2, typename _It2, index_t _beg2, size_t _size2>
   inline auto operator- (iodev_it<_Cont1, _It1, _beg1, _size1>& x,
                          iodev_it<_Cont1, _It1, _beg1, _size1>& y) noexcept
   -> decltype (x.cursor() - y.cursor()) {
      return (x.cursor() - y.cursor());
   }

   template<typename _Cont, typename _It, index_t _beg, size_t _size>
   inline iodev_it<_Cont, _It, _beg, _size>
      operator+ (typename _Cont::difference_type n,
                 iodev_it<_Cont, _It, _beg, _size>& it) noexcept {
      return iodev_it<_Cont, _It, _beg, _size>(it.owner(), it.cursor() + n);
   }
   //! @}

   /*!
    *  Index device iterator concept
    */
   //! @{
   #if defined _utl_have_concepts
      template <typename T>
      concept bool Idxdev_it = requires (T t, const T ct) {
         // STL compatibility
         typename T::value_type;
         typename T::difference_type;
         typename T::pointer;
         typename T::reference;
         requires same_<
            typename T::iterator_category,
            std::input_iterator_tag
         >::value;
         {*t} -> typename T::value_type;   // is dereferencable
         {++t} -> T&;      // is incrementable
         {t++} -> T;
         // Extras
         {ct.owner()} ->auto&&;
         {ct.cursor()} -> const size_t;
         {ct.value()} -> auto&&;
      };
   #else
      namespace idxdev_it_details {
         using std::declval;

         //! Primary template to catch any non SPI interface types
         template <typename _Tp, typename =void>
         struct is_idxdev_it_ : meta::false_ {};

         //! template to catch a proper SPI interface type
         template <typename _Tp>
         struct is_idxdev_it_ <
            _Tp,
            meta::void_t <
               typename _Tp::value_type,
               typename _Tp::difference_type,
               typename _Tp::pointer,
               typename _Tp::reference,
//               meta::use_if_same_t <
//                  typename _Tp::iterator_category,
//                  std::input_iterator_tag
//               >
               void
            >
         > : meta::true_ {};
      }
      /*!
       * Value meta-programming function for SPI interface checking
       * \param   _Tp  Type to check
       * \return  True if _Tp is a spi interface
       */
//      template <typename _Tp>
//      constexpr bool Idxdev_it = idxdev_it_details::is_idxdev_it_<_Tp>::value;
   #endif
   //! @}

}
//!@}
#endif /* __utl_dev_dev_iterators_h__ */
