#pragma once

#include <iterator>
#include <utility>

namespace NStlIterator {
    template <class T>
    class TProxy {
    public:
        TProxy() = default;
        TProxy(T&& value)
            : Value_(std::move(value))
        {
        }

        const T* operator->() const noexcept {
            return &Value_;
        }

        const T& operator*() const noexcept {
            return Value_;
        }

        bool operator==(const TProxy& rhs) const {
            return Value_ == rhs.Value_;
        }

    private:
        T Value_;
    };
} // namespace NStlIterator

/**
 * Range adaptor that turns a derived class with a Java-style iteration
 * interface into an STL range.
 *
 * Derived class is expected to define:
 * \code
 * TSomething* Next();
 * \endcode
 *
 * `Next()` returning `nullptr` signals end of range. Note that you can also use
 * pointer-like types instead of actual pointers (e.g. `TAtomicSharedPtr`).
 *
 * Since iteration state is stored inside the derived class, the resulting range
 * is an input range (works for single pass algorithms only). Technically speaking,
 * if you're returning a non-const pointer from `Next`, it can also work as an output range.
 *
 * Example usage:
 * \code
 * class TSquaresGenerator: public TInputRangeAdaptor<TSquaresGenerator> {
 * public:
 *     const double* Next() {
 *         Current_ = State_ * State_;
 *         State_ += 1.0;
 *         // Never return nullptr => we have infinite range!
 *         return &Current_;
 *     }
 *
 * private:
 *     double State_ = 0.0;
 *     double Current_ = 0.0;
 * }
 * \endcode
 */
template <class TSlave>
class TInputRangeAdaptor {
public: // TODO: private
    class TIterator {
    public:
        static constexpr bool IsNoexceptNext = noexcept(std::declval<TSlave>().Next());

        using difference_type = std::ptrdiff_t;
        using pointer = decltype(std::declval<TSlave>().Next());
        using reference = decltype(*std::declval<TSlave>().Next());
        using value_type = std::remove_cv_t<std::remove_reference_t<reference>>;
        using iterator_category = std::input_iterator_tag;

        inline TIterator() noexcept
            : Slave_(nullptr)
            , Cur_()
        {
        }

        inline TIterator(TSlave* slave) noexcept(IsNoexceptNext)
            : Slave_(slave)
            , Cur_(Slave_->Next())
        {
        }

        inline bool operator==(const TIterator& it) const noexcept {
            return Cur_ == it.Cur_;
        }

        inline bool operator!=(const TIterator& it) const noexcept {
            return !(*this == it);
        }

        inline pointer operator->() const noexcept {
            return Cur_;
        }

        inline reference operator*() const noexcept {
            return *Cur_;
        }

        inline TIterator& operator++() noexcept(IsNoexceptNext) {
            Cur_ = Slave_->Next();

            return *this;
        }

    private:
        TSlave* Slave_;
        pointer Cur_;
    };

public:
    using const_iterator = TIterator;
    using iterator = const_iterator;

    inline iterator begin() const noexcept(TIterator::IsNoexceptNext) {
        return TIterator(const_cast<TSlave*>(static_cast<const TSlave*>(this)));
    }

    inline iterator end() const noexcept {
        return TIterator();
    }
};

/**
 * Transform given reverse iterator into forward iterator pointing to the same element.
 *
 * @see http://stackoverflow.com/a/1830240
 */
template <class TIterator>
auto ToForwardIterator(TIterator iter) {
    return std::next(iter).base();
}