/* vim: set et ft=cpp.doxygen sts=2 sw=2 ts=8 : */
/**
 * Copyright © 2013 Saleem Abdulrasool <compnerd@compnerd.org>
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 *
 * 3. The name of the author may not be used to endorse or promote products
 *    derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
 * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 **/

/*!
 * \note § 6.5.4 The range-based for statement [stmt.ranged]
 *
 * A range-based for statement is equivalent to:
 *
 * \code{c++}
 *  {
 *    auto && __range = range-init;
 *    for ( auto __begin = begin-expr,
 *               __end = end-expr;
 *          __begin != __end;
 *          ++__begin ) {
 *      for-range-declaration = *__begin;
 *      statement
 *    }
 *  }
 * \endcode
 *
 * where \c __range, \c __begin, and \c __end are variables defined for
 * exposition only, and \c _RangeT is the type of the expression, and
 * \c begin-expr and \c end-expr are determined as follows:
 *
 *  - if \c _RangeT is an array type, \c begin-expr and \c end-expr are
 *    \c __range and \c __range \c + \c __bound, respectively, where \c __bound
 *    is the array bound.  If \c _RangeT is an array of unknown size or an array
 *    of incomplete type, the program is ill-formed.
 *  - otherwise, \c begin-expr and \c end-expr are \c begin(__range) and
 *    \c end(__range), respectively, where \c begin and \c end are looked up
 *    with argument-dependent lookup.  For the purposes of this name lookup,
 *    namespace \c std is an associated namespace.
 */

#include <iterator>
#include <type_traits>

template <typename type_, int>
struct reverse_iteration_proxy;

template <typename type_, size_t bound_>
class reverse_iteration_proxy<type_[bound_], 0> {
  private:
    type_ (&range_)[bound_];

  public:
    typedef std::reverse_iterator<type_ *> iterator;

    reverse_iteration_proxy(type_ (&range)[bound_]) : range_(range) { }
    reverse_iteration_proxy(type_ (&&)[bound_]) = delete;

    iterator begin() const noexcept { return iterator(range_ + bound_); }
    iterator end() const noexcept { return iterator(range_); }
};

template <typename type_>
class reverse_iteration_proxy<type_, 1> {
  private:
    type_ & range_;

  public:
    typedef decltype(range_.rbegin()) iterator;

    reverse_iteration_proxy(type_ & range) : range_(range) { }
    reverse_iteration_proxy(type_ &&) = delete;

    iterator begin() const noexcept { return range_.rbegin(); }
    iterator end() const noexcept { return range_.rend(); }
};

template <typename type_>
class reverse_iteration_proxy<type_, 2> {
  private:
    type_ & range_;

  public:
    typedef std::reverse_iterator<decltype(std::end(range_))> iterator;

    reverse_iteration_proxy(type_ & range) : range_(range) { }
    reverse_iteration_proxy(type_ &&) = delete;

    iterator begin() const noexcept { return iterator(std::end(range_)); }
    iterator end() const noexcept { return iterator(std::begin(range_)); }
};

template <typename type_, size_t bound_>
inline constexpr reverse_iteration_proxy<type_[bound_], 0>
select_reverse_iteration_proxy(type_ (&range)[bound_], int) {
  return reverse_iteration_proxy<type_[bound_], 0>{ range };
}

template <typename type_, typename = decltype(std::declval<type_>().rbegin())>
inline constexpr reverse_iteration_proxy<type_, 1>
select_reverse_iteration_proxy(type_ & range, int) {
  static_assert(std::is_same<decltype(std::declval<type_>().rbegin()),
                             decltype(std::declval<type_>().rend())>::value,
                "iterator type mismatch");
  return reverse_iteration_proxy<type_, 1>{ range };
}

template <typename type_>
inline constexpr reverse_iteration_proxy<type_, 2>
select_reverse_iteration_proxy(type_ & range, long) {
  return reverse_iteration_proxy<type_, 2>{ range };
}

template <typename type_>
inline constexpr auto reverse(type_ & range) ->
  decltype(select_reverse_iteration_proxy(range, 0)) {
  return select_reverse_iteration_proxy(range, 0);
}