/*
 * Copyright 2015 - 2023 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

#ifdef OX_USE_STDLIB
#include <string_view>
#endif

#include "basestringview.hpp"
#include "cstrops.hpp"
#include "writer.hpp"

namespace ox {

template<std::size_t buffLen>
class BString;

template<std::size_t buffLen>
class BasicString;

class StringView: public detail::BaseStringView {
	public:
		constexpr StringView() noexcept = default;

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

#ifdef OX_USE_STDLIB
		constexpr StringView(const std::string_view &sv) noexcept: BaseStringView(sv.data(), sv.size()) {}
#endif

		constexpr StringView(BaseStringView const&str) noexcept: BaseStringView(str.data(), str.bytes()) {}

		template<std::size_t SmallStrSz>
		constexpr StringView(const BasicString<SmallStrSz> &str) noexcept: BaseStringView(str.data(), str.len()) {}

		template<std::size_t SmallStrSz>
		constexpr StringView(const BString<SmallStrSz> &str) noexcept: BaseStringView(str.data(), str.len()) {}

		constexpr StringView(std::nullptr_t) noexcept {}

		constexpr StringView(const char *str) noexcept: BaseStringView(str) {}

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

		constexpr auto &operator=(StringView const&other) noexcept {
			if (&other != this) {
				set(other.data(), other.len());
			}
			return *this;
		}

};

using CRStringView = const StringView&;

constexpr auto operator==(CRStringView s1, CRStringView s2) noexcept {
	if (s2.len() != s1.len()) {
		return false;
	}
	return ox_strncmp(s1.data(), s2.data(), s1.len()) == 0;
}

constexpr auto operator<=>(CRStringView s1, CRStringView s2) noexcept {
	const auto maxLen = ox::min(s1.len(), s2.len());
	const auto a = &s1.front();
	const auto b = &s2.front();
	for (std::size_t i = 0; i < maxLen && (a[i] || b[i]); ++i) {
		if (a[i] < b[i]) {
			return -1;
		} else if (a[i] > b[i]) {
			return 1;
		}
	}
	if (s1.len() > s2.len()) {
		return 1;
	} else if (s1.len() < s2.len()) {
		return -1;
	} else {
		return 0;
	}
}

constexpr auto write(Writer_c auto &writer, ox::CRStringView sv) noexcept {
	return writer.write(sv.data(), sv.bytes());
}

#ifdef OX_USE_STDLIB
constexpr auto toStdStringView(CRStringView sv) noexcept {
	return std::string_view(sv.data(), sv.bytes());
}
#endif


// Maybe StringView. If T is a string type, MaybeType::type/MaybeSV_t is a
// StringView. This avoids creating unnecessary Strings when taking a
// StringView or C string as a function argument.
template<typename T, bool isStr = isOxString_v<T>>
struct MaybeSV {
	using type = T;
};
template<typename T>
struct MaybeSV<T, true> {
	using type = ox::StringView;
};
template<typename T>
using MaybeSV_t = typename MaybeSV<T>::type;


}

constexpr ox::Result<int> ox_atoi(ox::CRStringView str) noexcept {
	int total = 0;
	int multiplier = 1;
	for (auto i = static_cast<int64_t>(str.len()) - 1; i != -1; --i) {
		auto s = static_cast<std::size_t>(i);
		if (str[s] >= '0' && str[s] <= '9') {
			total += (str[s] - '0') * multiplier;
			multiplier *= 10;
		} else {
			return OxError(1);
		}
	}
	return total;
}

