// SPDX-License-Identifier: BSD-3-Clause
#ifndef SUBSTRATE_ITERATOR
#define SUBSTRATE_ITERATOR

#include <iterator>
#include <substrate/utility>

namespace substrate
{
	using std::iterator_traits;

	/**
	 * Defines an iterator adapter that is 'normal' in the sense that
	 * it does not change the semantics of any of the operators of its
	 * \c iterator_t parameter. Its primary purpose is to convert an
	 * iterator that is not a class (ie a pointer), into an iterator that is.
	 * \c container_t exists as a container-specific tag for instantiation
	 * differentiation purposes.
	 */
	template<typename iterator_t, typename container_t> struct normalIterator_t
	{
	protected:
		iterator_t current;

		using traits_t = iterator_traits<iterator_t>;

	public:
		using iterator_type = iterator_t;
		using iterator_category = typename traits_t::iterator_category;
		using value_type = typename traits_t::value_type;
		using difference_type = typename traits_t::difference_type;
		using reference = typename traits_t::reference;
		using pointer = typename traits_t::pointer;

		constexpr normalIterator_t() noexcept : current{iterator_t{}} { }
		explicit normalIterator_t(const iterator_t &iter) noexcept : current{iter} { }

		// We specifically allow iterator_t to const iterator_t conversion.
		template<typename iter_t> normalIterator_t(const normalIterator_t<iter_t,
			substrate::enable_if_t<std::is_same<iter_t, typename container_t::pointer>::value,
			container_t>> &iter) noexcept : current{iter.base()} { }

		reference operator *() const noexcept { return *current; }
		pointer operator ->() const noexcept { return current; }
		// NOLINTNEXTLINE(cert-dcl21-cpp)
		normalIterator_t operator++(int) noexcept { return {current++}; }
		// NOLINTNEXTLINE(cert-dcl21-cpp)
		normalIterator_t operator--(int) noexcept { return {current--}; }

		normalIterator_t &operator ++() noexcept
		{
			++current;
			return *this;
		}

		normalIterator_t &operator --() noexcept
		{
			--current;
			return *this;
		}

		reference operator [](const difference_type offset) const noexcept
			{ return current[offset]; }
		normalIterator_t operator +(const difference_type offset) const noexcept
			{ return normalIterator_t{current + offset}; }
		normalIterator_t operator -(const difference_type offset) const noexcept
			{ return normalIterator_t{current - offset}; }

		normalIterator_t operator +=(const difference_type offset) const noexcept
		{
			current += offset;
			return *this;
		}

		normalIterator_t operator -=(const difference_type offset) const noexcept
		{
			current -= offset;
			return *this;
		}

		iterator_t base() const noexcept { return current; }
	};

	template<typename iteratorL_t, typename iteratorR_t, typename container_t> inline bool operator ==(
		const normalIterator_t<iteratorL_t, container_t> &lhs,
		const normalIterator_t<iteratorR_t, container_t> &rhs
	) noexcept
		{ return lhs.base() == rhs.base(); }

	template<typename iterator_t, typename container_t> inline bool operator ==(
		const normalIterator_t<iterator_t, container_t> &lhs,
		const normalIterator_t<iterator_t, container_t> &rhs
	) noexcept
		{ return lhs.base() == rhs.base(); }

	template<typename iteratorL_t, typename iteratorR_t, typename container_t> inline bool operator !=(
		const normalIterator_t<iteratorL_t, container_t> &lhs,
		const normalIterator_t<iteratorR_t, container_t> &rhs
	) noexcept
		{ return lhs.base() != rhs.base(); }

	template<typename iterator_t, typename container_t> inline bool operator !=(
		const normalIterator_t<iterator_t, container_t> &lhs,
		const normalIterator_t<iterator_t, container_t> &rhs
	) noexcept
		{ return lhs.base() != rhs.base(); }

	template<typename iteratorL_t, typename iteratorR_t, typename container_t> inline bool operator <(
		const normalIterator_t<iteratorL_t, container_t> &lhs,
		const normalIterator_t<iteratorR_t, container_t> &rhs
	) noexcept
		{ return lhs.base() < rhs.base(); }

	template<typename iterator_t, typename container_t> inline bool operator <(
		const normalIterator_t<iterator_t, container_t> &lhs,
		const normalIterator_t<iterator_t, container_t> &rhs
	) noexcept
		{ return lhs.base() < rhs.base(); }

	template<typename iteratorL_t, typename iteratorR_t, typename container_t> inline bool operator >(
		const normalIterator_t<iteratorL_t, container_t> &lhs,
		const normalIterator_t<iteratorR_t, container_t> &rhs
	) noexcept
		{ return lhs.base() > rhs.base(); }

	template<typename iterator_t, typename container_t> inline bool operator >(
		const normalIterator_t<iterator_t, container_t> &lhs,
		const normalIterator_t<iterator_t, container_t> &rhs
	) noexcept
		{ return lhs.base() > rhs.base(); }

	template<typename iteratorL_t, typename iteratorR_t, typename container_t> inline bool operator <=(
		const normalIterator_t<iteratorL_t, container_t> &lhs,
		const normalIterator_t<iteratorR_t, container_t> &rhs
	) noexcept
		{ return lhs.base() <= rhs.base(); }

	template<typename iterator_t, typename container_t> inline bool operator <=(
		const normalIterator_t<iterator_t, container_t> &lhs,
		const normalIterator_t<iterator_t, container_t> &rhs
	) noexcept
		{ return lhs.base() <= rhs.base(); }

	template<typename iteratorL_t, typename iteratorR_t, typename container_t> inline bool operator >=(
		const normalIterator_t<iteratorL_t, container_t> &lhs,
		const normalIterator_t<iteratorR_t, container_t> &rhs
	) noexcept
		{ return lhs.base() >= rhs.base(); }

	template<typename iterator_t, typename container_t> inline bool operator >=(
		const normalIterator_t<iterator_t, container_t> &lhs,
		const normalIterator_t<iterator_t, container_t> &rhs
	) noexcept
		{ return lhs.base() >= rhs.base(); }

	template<typename iteratorL_t, typename iteratorR_t, typename container_t> inline auto operator -(
		const normalIterator_t<iteratorL_t, container_t> &lhs,
		const normalIterator_t<iteratorR_t, container_t> &rhs
	) noexcept -> decltype(lhs.base() - rhs.base())
		{ return lhs.base() - rhs.base(); }

	template<typename iterator_t, typename container_t> inline auto operator -(
		const normalIterator_t<iterator_t, container_t> &lhs,
		const normalIterator_t<iterator_t, container_t> &rhs
	) noexcept -> typename normalIterator_t<iterator_t, container_t>::difference_type
		{ return lhs.base() - rhs.base(); }

	template<typename iterator_t, typename container_t> inline auto operator +(
		const typename normalIterator_t<iterator_t, container_t>::difference_type n,
		const normalIterator_t<iterator_t, container_t> &iter
	) noexcept -> normalIterator_t<iterator_t, container_t>
		{ return normalIterator_t<iterator_t, container_t>{iter.base() + n}; }

}


#endif /* SUBSTRATE_ITERATOR */
/* vim: set ft=cpp ts=4 sw=4 noexpandtab: */
