/*
 * 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

#include "bit.hpp"
#include "def.hpp"
#include "cstrops.hpp"
#include "iterator.hpp"

OX_CLANG_NOWARN_BEGIN(-Wunsafe-buffer-usage)

namespace ox::detail {

class BaseStringView {
	public:
		template<typename RefType = char&, typename PtrType = char*, bool reverse = false>
		struct iterator: public Iterator<std::bidirectional_iterator_tag, char> {
			private:
				PtrType m_t = nullptr;
				std::size_t m_offset = 0;
				std::size_t m_max = 0;

			public:
				constexpr iterator() noexcept = default;

				constexpr iterator(PtrType t, std::size_t offset, std::size_t max) noexcept {
					m_t = t;
					m_offset = offset;
					m_max = max;
				}

				[[nodiscard]]
				constexpr auto offset() const noexcept {
					return m_offset;
				}

				constexpr iterator operator+(std::size_t s) const noexcept {
					if constexpr(reverse) {
						return iterator(m_t, max<std::size_t>(m_offset - s, 0), m_max);
					} else {
						return iterator(m_t, min<std::size_t>(m_offset + s, m_max), m_max);
					}
				}

				constexpr auto operator-(const iterator &other) const noexcept {
					if constexpr(reverse) {
						return m_offset + other.m_offset;
					} else {
						return m_offset - other.m_offset;
					}
				}

				constexpr iterator operator-(std::size_t s) const noexcept {
					if constexpr(reverse) {
						return iterator(m_t, min<std::size_t>(m_offset + s, m_max), m_max);
					} else {
						return iterator(m_t, max<std::size_t>(m_offset - s, 0), m_max);
					}
				}

				constexpr iterator &operator+=(std::size_t s) noexcept {
					if constexpr(reverse) {
						m_offset = max<std::size_t>(m_offset - s, 0);
					} else {
						m_offset = min(m_offset + s, m_max);
					}
					return *this;
				}

				constexpr iterator &operator-=(std::size_t s) noexcept {
					if constexpr(reverse) {
						m_offset = min(m_offset + s, m_max);
					} else {
						m_offset = max<std::size_t>(m_offset - s, 0);
					}
					return *this;
				}

				constexpr iterator &operator++() noexcept {
					return operator+=(1);
				}

				constexpr iterator &operator--() noexcept {
					return operator-=(1);
				}

				constexpr RefType operator*() const noexcept {
					return m_t[m_offset];
				}

				constexpr RefType operator[](std::size_t s) const noexcept {
					return m_t[s];
				}

				constexpr bool operator<(const iterator &other) const noexcept {
					return m_offset < other.m_offset;
				}

				constexpr bool operator>(const iterator &other) const noexcept {
					return m_offset > other.m_offset;
				}

				constexpr bool operator<=(const iterator &other) const noexcept {
					return m_offset <= other.m_offset;
				}

				constexpr bool operator>=(const iterator &other) const noexcept {
					return m_offset >= other.m_offset;
				}

				constexpr bool operator==(const iterator &other) const noexcept {
					return m_t == other.m_t && m_offset == other.m_offset && m_max == other.m_max;
				}

				constexpr bool operator!=(const iterator &other) const noexcept {
					return m_t != other.m_t || m_offset != other.m_offset || m_max != other.m_max;
				}

		};

	private:
		const char *m_str = nullptr;
		std::size_t m_len = 0;
	protected:
		constexpr BaseStringView() noexcept = default;

		constexpr BaseStringView(BaseStringView const&sv) noexcept = default;

		constexpr explicit BaseStringView(std::nullptr_t) noexcept {}

		constexpr explicit BaseStringView(const char *str) noexcept: m_str(str), m_len(str ? ox::strlen(str) : 0) {}

		constexpr explicit BaseStringView(const char *str, std::size_t len) noexcept: m_str(str), m_len(len) {}

	public:
		[[nodiscard]]
		constexpr iterator<const char&, const char*> begin() const noexcept {
			return {m_str, 0, m_len};
		}

		[[nodiscard]]
		constexpr iterator<const char&, const char*> end() const noexcept {
			return {m_str, m_len, m_len};
		}

		[[nodiscard]]
		constexpr iterator<const char&, const char*> cbegin() const noexcept {
			return {m_str, 0, m_len};
		}

		[[nodiscard]]
		constexpr iterator<const char&, const char*> cend() const noexcept {
			return {m_str, m_len, m_len};
		}

		[[nodiscard]]
		constexpr iterator<const char&, const char*, true> crbegin() const noexcept {
			return {m_str, m_len - 1, m_len};
		}

		[[nodiscard]]
		constexpr iterator<const char&, const char*, true> crend() const noexcept {
			return {m_str, MaxValue<std::size_t>, m_len};
		}

		[[nodiscard]]
		constexpr iterator<const char&, const char*, true> rbegin() const noexcept {
			return {m_str, m_len - 1, m_len};
		}

		[[nodiscard]]
		constexpr iterator<const char&, const char*, true> rend() const noexcept {
			return {m_str, MaxValue<std::size_t>, m_len};
		}

		[[nodiscard]]
		constexpr auto bytes() const noexcept {
			return m_len;
		}

		[[nodiscard]]
		constexpr auto size() const noexcept {
			return m_len;
		}

		[[nodiscard]]
		constexpr auto *data() const noexcept {
			return &m_str[0];
		}

		[[nodiscard]]
		constexpr auto &front() const noexcept {
			return m_str[0];
		}

		[[nodiscard]]
		constexpr auto &back() const noexcept {
			return m_str[m_len - 1];
		}

		[[nodiscard]]
		constexpr auto &operator[](std::size_t i) const noexcept {
			return m_str[i];
		}

	protected:
		constexpr void set(const char *str, std::size_t len) noexcept {
			m_str = str;
			m_len = len;
		}

};

}

OX_CLANG_NOWARN_END
