#pragma once

#include <psemek/util/range.hpp>

#include <iterator>

namespace psemek::util
{

	template <typename Iterator>
	struct cyclic_iterator
	{
		using traits = std::iterator_traits<Iterator>;
		using value_type = typename traits::value_type;
		using reference = typename traits::reference;
		using pointer = typename traits::pointer;
		using difference_type = typename traits::difference_type;
		using iterator_category = std::bidirectional_iterator_tag;

		Iterator begin, end;
		Iterator iter;

		cyclic_iterator() = default;
		cyclic_iterator(cyclic_iterator const &) = default;

		cyclic_iterator(Iterator begin, Iterator end, Iterator iter)
			: begin(begin)
			, end(end)
			, iter(iter)
		{}

		cyclic_iterator(Iterator begin, Iterator end)
			: begin(begin)
			, end(end)
			, iter(begin)
		{}

		cyclic_iterator & operator = (cyclic_iterator const &) = default;

		decltype(auto) operator *()
		{
			return *iter;
		}

		cyclic_iterator & operator++()
		{
			if (++iter == end)
				iter = begin;
			return *this;
		}

		cyclic_iterator operator++(int)
		{
			auto copy = *this;
			this->operator++();
			return copy;
		}

		cyclic_iterator & operator--()
		{
			if (iter == begin)
				iter = end;
			--iter;
			return *this;
		}

		cyclic_iterator operator--(int)
		{
			auto copy = *this;
			this->operator--();
			return copy;
		}

		friend bool operator == (cyclic_iterator const & it1, cyclic_iterator const & it2)
		{
			return it1.iter == it2.iter;
		}

		friend bool operator != (cyclic_iterator const & it1, cyclic_iterator const & it2)
		{
			return it1.iter != it2.iter;
		}
	};

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

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

	template <typename Container>
	auto make_cyclic_iterator(Container & container)
	{
		return cyclic_iterator{util::xbegin(container), util::xend(container)};
	}

}
