#pragma once

#include "store_policy.h"
#include "typetraits.h"

namespace NPrivate {
    template <class Range>
    class TReverseRangeStorage {
    public:
        TReverseRangeStorage(Range&& range)
            : Base_(std::forward<Range>(range))
        {
        }

        decltype(auto) Base() const {
            return *Base_.Ptr();
        }

        decltype(auto) Base() {
            return *Base_.Ptr();
        }

    private:
        TAutoEmbedOrPtrPolicy<Range> Base_;
    };

    template <class Range>
    constexpr bool HasReverseIterators(i32, decltype(std::declval<Range>().rbegin())*) {
        return true;
    }

    template <class Range>
    constexpr bool HasReverseIterators(char, std::nullptr_t*) {
        return false;
    }

    template <class Range, bool hasReverseIterators = HasReverseIterators<Range>((i32)0, nullptr)>
    class TReverseRangeBase: public TReverseRangeStorage<Range> {
        using TBase = TReverseRangeStorage<Range>;

    public:
        using TBase::Base;
        using TBase::TBase;

        auto begin() const {
            return Base().rbegin();
        }

        auto end() const {
            return Base().rend();
        }

        auto begin() {
            return Base().rbegin();
        }

        auto end() {
            return Base().rend();
        }
    };

    template <class Range>
    class TReverseRangeBase<Range, false>: public TReverseRangeStorage<Range> {
        using TBase = TReverseRangeStorage<Range>;

    public:
        using TBase::Base;
        using TBase::TBase;

        auto begin() const {
            using std::end;
            return std::make_reverse_iterator(end(Base()));
        }

        auto end() const {
            using std::begin;
            return std::make_reverse_iterator(begin(Base()));
        }

        auto begin() {
            using std::end;
            return std::make_reverse_iterator(end(Base()));
        }

        auto end() {
            using std::begin;
            return std::make_reverse_iterator(begin(Base()));
        }
    };

    template <class Range>
    class TReverseRange: public TReverseRangeBase<Range> {
        using TBase = TReverseRangeBase<Range>;

    public:
        using TBase::Base;
        using TBase::TBase;

        TReverseRange(TReverseRange&&) = default;
        TReverseRange(const TReverseRange&) = default;

        auto rbegin() const {
            using std::begin;
            return begin(Base());
        }

        auto rend() const {
            using std::end;
            return end(Base());
        }

        auto rbegin() {
            using std::begin;
            return begin(Base());
        }

        auto rend() {
            using std::end;
            return end(Base());
        }
    };
}

/**
 * Provides a reverse view into the provided container.
 *
 * Example usage:
 * @code
 * for(auto&& value: Reversed(container)) {
 *     // use value here.
 * }
 * @endcode
 *
 * @param cont                          Container to provide a view into. Must be an lvalue.
 * @returns                             A reverse view into the provided container.
 */
template <class Range>
constexpr ::NPrivate::TReverseRange<Range> Reversed(Range&& range) {
    return ::NPrivate::TReverseRange<Range>(std::forward<Range>(range));
}