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

#include <cstddef>
#include <cstdint>
#include <substrate/internal/defs>

namespace substrate
{
	struct indexIterator_t final
	{
	private:
		std::size_t index;
		std::size_t step;

	public:
		constexpr indexIterator_t(const std::size_t value, const std::size_t stepValue) noexcept :
			index{value}, step{stepValue} { }
		SUBSTRATE_NO_DISCARD(constexpr std::size_t operator *() const noexcept) { return index; }

		SUBSTRATE_CXX14_CONSTEXPR indexIterator_t &operator ++() noexcept
		{
			index += step;
			return *this;
		}

		SUBSTRATE_CXX14_CONSTEXPR indexIterator_t &operator --() noexcept
		{
			index -= step;
			return *this;
		}
	};

	SUBSTRATE_NO_DISCARD(constexpr inline bool operator ==
			(const indexIterator_t &a, const indexIterator_t &b) noexcept)
		{ return *a == *b; }

	SUBSTRATE_NO_DISCARD(constexpr inline bool operator !=
			(const indexIterator_t &a, const indexIterator_t &b) noexcept)
		{ return *a != *b; }

	struct indexSequence_t final
	{
	private:
		std::size_t begin_{0};
		std::size_t end_;
		std::size_t step_{1};

	public:
		constexpr indexSequence_t(const std::size_t end) noexcept : end_{end} { }
		constexpr indexSequence_t(const std::size_t begin, const std::size_t end) noexcept :
			begin_{begin}, end_{end} { }

		SUBSTRATE_NO_DISCARD(constexpr indexIterator_t begin() const noexcept) { return {begin_, step_}; }
		SUBSTRATE_NO_DISCARD(constexpr indexIterator_t end() const noexcept) { return {end_, step_}; }

		SUBSTRATE_NO_DISCARD(SUBSTRATE_CXX14_CONSTEXPR indexSequence_t step(const size_t step) noexcept)
		{
			// If this has already been run on this sequence, or the new step size is 0, do nothing
			if (step_ != 1U || !step)
				return *this;
			step_ = step;
			// Adjust end_ so the problem always properly terminates
			const auto adjustment{step_ - ((end_ - begin_) % step_)};
			if (adjustment != step_)
				end_ += adjustment;
			return *this;
		}
	};
} // namespace substrate

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