/*
 * Copyright 2015 - 2025 gary@drinkingtea.net
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at https://mozilla.org/MPL/2.0/.
 */

#pragma once

#if __has_include(<array>)
#include <array>
#endif

#include "array.hpp"
#include "bit.hpp"
#include "def.hpp"
#include "iterator.hpp"
#include "vector.hpp"

OX_ALLOW_UNSAFE_BUFFERS_BEGIN

namespace ox {


template<typename T>
class Span {

	private:
		T *m_items{};
		std::size_t m_size{};

	public:
		using value_type = T;
		using size_type = std::size_t;

		template<typename RefType = T&, typename PtrType = T*, bool reverse = false>
		using iterator = SpanIterator<T, RefType, PtrType, reverse>;

		constexpr Span() noexcept = default;

#if __has_include(<array>)
		template<std::size_t sz>
		constexpr Span(std::array<T, sz> &a) noexcept:
			m_items(a.data()),
			m_size(a.size()) {
		}

		template<std::size_t sz>
		constexpr Span(std::array<ox::remove_const_t<T>, sz> const &a) noexcept:
			m_items(a.data()),
			m_size(a.size()) {
		}
#endif

		template<std::size_t sz>
		constexpr Span(ox::Array<T, sz> &a) noexcept:
			m_items(a.data()),
			m_size(a.size()) {
		}

		template<std::size_t sz>
		constexpr Span(ox::Array<ox::remove_const_t<T>, sz> const &a) noexcept:
			m_items(a.data()),
			m_size(a.size()) {
		}

		template<std::size_t sz, typename Allocator>
		constexpr Span(ox::Vector<T, sz, Allocator> &v) noexcept:
			m_items(v.data()),
			m_size(v.size()) {
		}

		template<std::size_t sz, typename Allocator>
		constexpr Span(ox::Vector<ox::remove_const_t<T>, sz, Allocator> const &v) noexcept:
			m_items(v.data()),
			m_size(v.size()) {
		}

		template<std::size_t sz>
		constexpr Span(T (&a)[sz]) noexcept:
			m_items(a),
			m_size(sz) {
		}

		constexpr Span(T *a, std::size_t sz) noexcept:
			m_items(a),
			m_size(sz) {
		}

		constexpr iterator<> begin() noexcept {
			return iterator<>(m_items, 0, m_size);
		}

		constexpr iterator<> end() noexcept {
			return iterator<>(m_items, m_size, m_size);
		}

		[[nodiscard]]
		constexpr iterator<const T&, const T*> begin() const noexcept {
			return iterator<const T&, const T*>(m_items, 0, m_size);
		}

		[[nodiscard]]
		constexpr iterator<const T&, const T*> end() const noexcept {
			return iterator<const T&, const T*>(m_items, m_size, m_size);
		}

		[[nodiscard]]
		constexpr iterator<const T&, const T*> cbegin() const noexcept {
			return iterator<const T&, const T*>(m_items, 0, m_size);
		}

		[[nodiscard]]
		constexpr iterator<const T&, const T*> cend() const noexcept {
			return iterator<const T&, const T*>(m_items, m_size, m_size);
		}

		[[nodiscard]]
		constexpr iterator<T&, T*, true> rbegin() noexcept {
			return iterator<T&, T*, true>(m_items, m_size - 1, m_size);
		}

		[[nodiscard]]
		constexpr iterator<T&, T*, true> rend() noexcept {
			return iterator<T&, T*, true>(m_items, MaxValue<size_type>, m_size);
		}

		[[nodiscard]]
		constexpr iterator<const T&, const T*, true> crbegin() const noexcept {
			return iterator<const T&, const T*, true>(m_items, m_size - 1, m_size);
		}

		[[nodiscard]]
		constexpr iterator<const T&, const T*, true> crend() const noexcept {
			return iterator<const T&, const T*, true>(m_items, MaxValue<size_type>, m_size);
		}

		[[nodiscard]]
		constexpr iterator<const T&, const T*, true> rbegin() const noexcept {
			return iterator<const T&, const T*, true>(m_items, m_size - 1, m_size);
		}

		[[nodiscard]]
		constexpr iterator<const T&, const T*, true> rend() const noexcept {
			return iterator<const T&, const T*, true>(m_items, MaxValue<size_type>, m_size);
		}

		constexpr T &operator[](std::size_t i) const noexcept {
			boundsCheck(i, size(), "Span access overflow");
			return m_items[i];
		}

		constexpr Span operator+(size_t i) const noexcept {
			boundsCheck(i, size(), "Span access overflow");
			return {m_items + i, m_size - i};
		}

		constexpr Span operator+=(size_t i) noexcept {
			boundsCheck(i, size(), "Span access overflow");
			m_items += i;
			m_size -= i;
			return *this;
		}

		constexpr Span operator++(int) noexcept {
			++m_items;
			--m_size;
			if (!m_size) [[unlikely]] {
				m_items = nullptr;
			}
			return *this;
		}

		constexpr Span operator++() noexcept {
			++m_items;
			--m_size;
			if (!m_size) [[unlikely]] {
				m_items = nullptr;
			}
			return *this;
		}

		[[nodiscard]]
		constexpr auto data() const noexcept {
			return m_items;
		}

		[[nodiscard]]
		constexpr std::size_t size() const noexcept {
			return m_size;
		}

		[[nodiscard]]
		constexpr bool empty() const noexcept {
			return m_size == 0;
		}

};

template<typename T>
using SpanView = Span<T const>;

template<typename T>
constexpr void spancpy(ox::Span<T> const dst, ox::SpanView<T> const src) noexcept {
	auto const sz = ox::min(dst.size(), src.size());
	if (std::is_constant_evaluated() || std::is_trivially_copyable_v<T>) {
		for (size_t i{}; i < sz; ++i) {
			dst.data()[i] = src.data()[i];
		}
	} else {
		memcpy(dst.data(), src.data(), sz * sizeof(T));
	}
}

}

OX_ALLOW_UNSAFE_BUFFERS_END
