#pragma once

#include <gsl/pointers>

template <typename Element>
class MatrixRowIterator
{
public:
	using ColumnPtr = gsl::owner<Element *>;

	explicit MatrixRowIterator(ColumnPtr column_ptr) noexcept;

	auto operator++() noexcept -> MatrixRowIterator &;
	auto operator++(int) noexcept -> MatrixRowIterator;

	auto operator*() const noexcept -> Element &;

	auto operator==(const MatrixRowIterator &rhs) const noexcept -> bool;
	auto operator!=(const MatrixRowIterator &rhs) const noexcept -> bool;

private:
	ColumnPtr _column_ptr;
};

template <typename Element>
class MatrixRow
{
public:
	using RowPtr = gsl::owner<Element *>;

	explicit MatrixRow(RowPtr row_ptr, const uint32_t &column_cnt) noexcept;

	[[nodiscard]] auto begin() const noexcept -> MatrixRowIterator<Element>;

	[[nodiscard]] auto end() const noexcept -> MatrixRowIterator<Element>;

private:
	RowPtr _row_ptr;

	const uint32_t &_column_cnt;
};

template <typename Element>
class MatrixIterator
{
public:
	using RowPtr = gsl::owner<Element **>;

	explicit MatrixIterator(RowPtr row_ptr, const uint32_t &column_cnt) noexcept;

	auto operator++() noexcept -> MatrixIterator &;
	auto operator++(int) noexcept -> MatrixIterator;

	auto operator*() const noexcept -> MatrixRow<Element>;

	auto operator==(const MatrixIterator &rhs) const noexcept -> bool;
	auto operator!=(const MatrixIterator &rhs) const noexcept -> bool;

private:
	RowPtr _row_ptr;

	const uint32_t &_column_cnt;
};

#include "matrix_iterator_impl.hpp"