#pragma once

#include <iterator>
#include <utility>

namespace psemek::util
{

	namespace detail
	{

		template <typename Container>
		auto begin_helper(Container & x)
		{
			using std::begin;
			return begin(x);
		}

		template <typename Container>
		auto end_helper(Container & x)
		{
			using std::end;
			return end(x);
		}

	}

	template <typename Container>
	auto xbegin(Container & x)
	{
		return detail::begin_helper(x);
	}

	template <typename Container>
	auto xend(Container & x)
	{
		return detail::end_helper(x);
	}

	template <typename T>
	struct range_traits
	{
		using iterator = decltype(begin(std::declval<T>()));
		using iterator_traits = std::iterator_traits<iterator>;

		using iterator_category = typename iterator_traits::iterator_category;
		using value_type        = typename iterator_traits::value_type       ;
		using difference        = typename iterator_traits::difference       ;
		using pointer           = typename iterator_traits::pointer          ;
		using reference         = typename iterator_traits::reference        ;
	};

	template <typename Iterator>
	struct range
	{
		Iterator it_begin;
		Iterator it_end;

		Iterator begin() const
		{
			return it_begin;
		}

		Iterator end() const
		{
			return it_end;
		}

		auto size() const
		{
			return end() - begin();
		}

		auto empty() const
		{
			return begin() == end();
		}
	};

	template <typename Iterator>
	range(Iterator, Iterator) -> range<Iterator>;

	template <typename Range>
	auto reversed(Range const & r)
	{
		auto it1 = xbegin(r);
		auto it2 = xend(r);

		using ReverseIterator = std::reverse_iterator<decltype(it1)>;
		return range<ReverseIterator>{std::make_reverse_iterator(it2), std::make_reverse_iterator(it1)};
	}

}
