/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

/* An iterator that acts like another iterator, but iterating in
 * the negative direction. (Note that not all iterators can iterate
 * in the negative direction.) */

#ifndef mozilla_ReverseIterator_h
#define mozilla_ReverseIterator_h

#include "mozilla/Attributes.h"
#include "mozilla/TypeTraits.h"

namespace mozilla {

template <typename IteratorT>
class ReverseIterator {
 public:
  template <typename Iterator>
  explicit ReverseIterator(Iterator aIter) : mCurrent(aIter) {}

  template <typename Iterator>
  MOZ_IMPLICIT ReverseIterator(const ReverseIterator<Iterator>& aOther)
      : mCurrent(aOther.mCurrent) {}

  decltype(*DeclVal<IteratorT>()) operator*() const {
    IteratorT tmp = mCurrent;
    return *--tmp;
  }

  /* Increments and decrements operators */

  ReverseIterator& operator++() {
    --mCurrent;
    return *this;
  }
  ReverseIterator& operator--() {
    ++mCurrent;
    return *this;
  }
  ReverseIterator operator++(int) {
    auto ret = *this;
    mCurrent--;
    return ret;
  }
  ReverseIterator operator--(int) {
    auto ret = *this;
    mCurrent++;
    return ret;
  }

  /* Comparison operators */

  template <typename Iterator1, typename Iterator2>
  friend bool operator==(const ReverseIterator<Iterator1>& aIter1,
                         const ReverseIterator<Iterator2>& aIter2);
  template <typename Iterator1, typename Iterator2>
  friend bool operator!=(const ReverseIterator<Iterator1>& aIter1,
                         const ReverseIterator<Iterator2>& aIter2);
  template <typename Iterator1, typename Iterator2>
  friend bool operator<(const ReverseIterator<Iterator1>& aIter1,
                        const ReverseIterator<Iterator2>& aIter2);
  template <typename Iterator1, typename Iterator2>
  friend bool operator<=(const ReverseIterator<Iterator1>& aIter1,
                         const ReverseIterator<Iterator2>& aIter2);
  template <typename Iterator1, typename Iterator2>
  friend bool operator>(const ReverseIterator<Iterator1>& aIter1,
                        const ReverseIterator<Iterator2>& aIter2);
  template <typename Iterator1, typename Iterator2>
  friend bool operator>=(const ReverseIterator<Iterator1>& aIter1,
                         const ReverseIterator<Iterator2>& aIter2);

 private:
  IteratorT mCurrent;
};

template <typename Iterator1, typename Iterator2>
bool operator==(const ReverseIterator<Iterator1>& aIter1,
                const ReverseIterator<Iterator2>& aIter2) {
  return aIter1.mCurrent == aIter2.mCurrent;
}

template <typename Iterator1, typename Iterator2>
bool operator!=(const ReverseIterator<Iterator1>& aIter1,
                const ReverseIterator<Iterator2>& aIter2) {
  return aIter1.mCurrent != aIter2.mCurrent;
}

template <typename Iterator1, typename Iterator2>
bool operator<(const ReverseIterator<Iterator1>& aIter1,
               const ReverseIterator<Iterator2>& aIter2) {
  return aIter1.mCurrent > aIter2.mCurrent;
}

template <typename Iterator1, typename Iterator2>
bool operator<=(const ReverseIterator<Iterator1>& aIter1,
                const ReverseIterator<Iterator2>& aIter2) {
  return aIter1.mCurrent >= aIter2.mCurrent;
}

template <typename Iterator1, typename Iterator2>
bool operator>(const ReverseIterator<Iterator1>& aIter1,
               const ReverseIterator<Iterator2>& aIter2) {
  return aIter1.mCurrent < aIter2.mCurrent;
}

template <typename Iterator1, typename Iterator2>
bool operator>=(const ReverseIterator<Iterator1>& aIter1,
                const ReverseIterator<Iterator2>& aIter2) {
  return aIter1.mCurrent <= aIter2.mCurrent;
}

namespace detail {

template <typename IteratorT>
class IteratorRange {
 public:
  typedef IteratorT iterator;
  typedef IteratorT const_iterator;
  typedef ReverseIterator<IteratorT> reverse_iterator;
  typedef ReverseIterator<IteratorT> const_reverse_iterator;

  template <typename Iterator1, typename Iterator2>
  MOZ_IMPLICIT IteratorRange(Iterator1 aIterBegin, Iterator2 aIterEnd)
      : mIterBegin(aIterBegin), mIterEnd(aIterEnd) {}

  template <typename Iterator>
  MOZ_IMPLICIT IteratorRange(const IteratorRange<Iterator>& aOther)
      : mIterBegin(aOther.mIterBegin), mIterEnd(aOther.mIterEnd) {}

  iterator begin() const { return mIterBegin; }
  const_iterator cbegin() const { return begin(); }
  iterator end() const { return mIterEnd; }
  const_iterator cend() const { return end(); }
  reverse_iterator rbegin() const { return reverse_iterator(mIterEnd); }
  const_reverse_iterator crbegin() const { return rbegin(); }
  reverse_iterator rend() const { return reverse_iterator(mIterBegin); }
  const_reverse_iterator crend() const { return rend(); }

 private:
  IteratorT mIterBegin;
  IteratorT mIterEnd;
};

}  // namespace detail

template <typename Range>
detail::IteratorRange<typename Range::reverse_iterator> Reversed(
    Range& aRange) {
  return {aRange.rbegin(), aRange.rend()};
}

template <typename Range>
detail::IteratorRange<typename Range::const_reverse_iterator> Reversed(
    const Range& aRange) {
  return {aRange.rbegin(), aRange.rend()};
}

}  // namespace mozilla

#endif  // mozilla_ReverseIterator_h
