/*
 * 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(<string>)
#include <string>
#endif

#include "algorithm.hpp"
#include "def.hpp"
#include "ignore.hpp"
#include "memops.hpp"
#include "serialize.hpp"
#include "strconv.hpp"
#include "stringliteral.hpp"
#include "stringview.hpp"
#include "strops.hpp"
#include "vector.hpp"

OX_CLANG_NOWARN_BEGIN(-Wunsafe-buffer-usage)

namespace ox {

template<typename Integer>
constexpr ox::IString<21> intToStr(Integer v) noexcept;

template<std::size_t SmallStringSize_v>
class BasicString {
	private:
		Vector<char, SmallStringSize_v> m_buff;

	public:
		static constexpr auto SmallStringSize = SmallStringSize_v;
		constexpr BasicString() noexcept;

		constexpr explicit BasicString(std::size_t cap) noexcept;

		constexpr explicit BasicString(const char *str) noexcept;

		constexpr explicit BasicString(const char8_t *str) noexcept;

		constexpr BasicString(const char *str, std::size_t size) noexcept;

		constexpr explicit BasicString(StringLiteral const&str) noexcept;

		constexpr explicit BasicString(StringViewCR str) noexcept;

		constexpr explicit BasicString(BasicString const&) noexcept;

		constexpr BasicString(BasicString&&) noexcept;

		[[nodiscard]]
		constexpr auto begin() noexcept {
			return m_buff.begin();
		}

		[[nodiscard]]
		constexpr auto end() noexcept {
			return m_buff.end();
		}

		[[nodiscard]]
		constexpr auto begin() const noexcept {
			return m_buff.begin();
		}

		[[nodiscard]]
		constexpr auto end() const noexcept {
			return m_buff.end();
		}

		[[nodiscard]]
		constexpr auto cbegin() const noexcept {
			return m_buff.begin();
		}

		[[nodiscard]]
		constexpr auto cend() const noexcept {
			return m_buff.end();
		}

		[[nodiscard]]
		constexpr auto rbegin() noexcept {
			return m_buff.rbegin();
		}

		[[nodiscard]]
		constexpr auto rend() noexcept {
			return m_buff.rend();
		}

		[[nodiscard]]
		constexpr auto rbegin() const noexcept {
			return m_buff.rbegin();
		}

		[[nodiscard]]
		constexpr auto rend() const noexcept {
			return m_buff.rend();
		}

		[[nodiscard]]
		constexpr auto crbegin() const noexcept {
			return m_buff.rbegin();
		}

		[[nodiscard]]
		constexpr auto crend() const noexcept {
			return m_buff.rend();
		}

		constexpr BasicString &operator=(const char *str) noexcept;

		constexpr BasicString &operator=(char c) noexcept;

		constexpr BasicString &operator=(int i) noexcept;

		constexpr BasicString &operator=(int64_t i) noexcept;

		constexpr BasicString &operator=(uint64_t i) noexcept;

		constexpr BasicString &operator=(const BasicString &src) noexcept;

		constexpr BasicString &operator=(BasicString &&src) noexcept;

		constexpr BasicString &operator=(StringViewCR src) noexcept;

		constexpr BasicString &operator+=(const char *str) noexcept;

		constexpr BasicString &operator+=(char *str) noexcept;

		constexpr BasicString &operator+=(char c) noexcept;

		constexpr BasicString &operator+=(Integer_c auto i) noexcept;

		constexpr BasicString &operator+=(StringViewCR src) noexcept;

		constexpr BasicString &operator+=(BasicString const&src) noexcept;

		constexpr BasicString operator+(const char *str) const noexcept;

		constexpr BasicString operator+(char *str) const noexcept;

		constexpr BasicString operator+(char c) const noexcept;

		constexpr BasicString operator+(Integer_c auto i) const noexcept;

		constexpr BasicString operator+(StringViewCR src) const noexcept;

		constexpr BasicString operator+(BasicString const&src) const noexcept;

		constexpr bool operator==(const char *other) const noexcept;

		constexpr bool operator==(OxString_c auto const&other) const noexcept;

		constexpr bool operator!=(const char *other) const noexcept;

		constexpr bool operator!=(OxString_c auto const&other) const noexcept;

		constexpr bool operator<(BasicString const&other) const noexcept;

		constexpr bool operator>(BasicString const&other) const noexcept;

		constexpr bool operator<=(BasicString const&other) const noexcept;

		constexpr bool operator>=(BasicString const&other) const noexcept;

		constexpr char const&operator[](std::size_t i) const noexcept;

		constexpr char &operator[](std::size_t i) noexcept;

		constexpr Error append(const char *str, std::size_t strLen) noexcept {
			auto currentLen = size();
			m_buff.resize(m_buff.size() + strLen);
			ox::listcpy(&m_buff[currentLen], str, strLen);
			// make sure last element is a null terminator
			m_buff[currentLen + strLen] = 0;
			// this can't fail, but it returns an Error to match BString::append
			return {};
		}

		constexpr Error append(StringViewCR sv) noexcept {
			return append(sv.data(), sv.size());
		}

		[[nodiscard]]
		constexpr BasicString substr(std::size_t pos) const noexcept;

		[[nodiscard]]
		constexpr BasicString substr(std::size_t begin, std::size_t end) const noexcept;

		constexpr void resize(size_t sz) noexcept {
			++sz;
			m_buff.resize(sz);
			m_buff[sz - 1] = 0;
		}

		[[nodiscard]]
		constexpr const char *data() const noexcept {
			return m_buff.data();
		}

		[[nodiscard]]
		constexpr char *data() noexcept {
			return m_buff.data();
		}

		[[nodiscard]]
		constexpr const char *c_str() const noexcept {
			return m_buff.data();
		}

		[[nodiscard]]
		constexpr explicit operator const char*() const {
			return c_str();
		}

#if __has_include(<string>)
		[[nodiscard]]
		std::string toStdString() const {
			return c_str();
		}

		[[nodiscard]]
		explicit operator std::string() const {
			return c_str();
		}
#endif

		/**
		 * Returns the number of characters in this string.
		 */
		[[nodiscard]]
		constexpr std::size_t size() const noexcept;

		/**
		 * Returns the number of bytes used for this string.
		 */
		[[nodiscard]]
		constexpr std::size_t bytes() const noexcept;

	private:
		constexpr void set(StringViewCR str) noexcept;

		constexpr void set(const char8_t *str) noexcept;
};

template<std::size_t SmallStringSize_v>
constexpr BasicString<SmallStringSize_v>::BasicString() noexcept {
	m_buff.resize(1);
}

template<std::size_t SmallStringSize_v>
constexpr BasicString<SmallStringSize_v>::BasicString(std::size_t cap) noexcept: m_buff(cap + 1) {}

template<std::size_t SmallStringSize_v>
constexpr BasicString<SmallStringSize_v>::BasicString(const char *str) noexcept {
	set(str);
}

template<std::size_t SmallStringSize_v>
constexpr BasicString<SmallStringSize_v>::BasicString(const char8_t *str) noexcept {
	set(str);
}

template<std::size_t SmallStringSize_v>
constexpr BasicString<SmallStringSize_v>::BasicString(const char *str, std::size_t size) noexcept {
	m_buff.resize(size + 1);
	ox::listcpy(m_buff.data(), str, size);
}

template<std::size_t SmallStringSize_v>
constexpr BasicString<SmallStringSize_v>::BasicString(StringLiteral const&str) noexcept:
	BasicString(StringView{str.data(), str.size()}) {
}

template<std::size_t SmallStringSize_v>
constexpr BasicString<SmallStringSize_v>::BasicString(StringViewCR str) noexcept {
	set(str);
}

template<std::size_t SmallStringSize_v>
constexpr BasicString<SmallStringSize_v>::BasicString(const BasicString &other) noexcept {
	m_buff = other.m_buff;
}

template<std::size_t SmallStringSize_v>
constexpr BasicString<SmallStringSize_v>::BasicString(BasicString &&other) noexcept: m_buff(std::move(other.m_buff)) {
	other.m_buff.resize(1);
	other.m_buff[0] = 0;
}

template<std::size_t SmallStringSize_v>
constexpr BasicString<SmallStringSize_v> &BasicString<SmallStringSize_v>::operator=(const char *str) noexcept {
	set(str);
	return *this;
}

template<std::size_t SmallStringSize_v>
constexpr BasicString<SmallStringSize_v> &BasicString<SmallStringSize_v>::operator=(char c) noexcept {
	ox::Array<char, 2> str{c, 0};
	set(str.data());
	return *this;
}

template<std::size_t SmallStringSize_v>
constexpr BasicString<SmallStringSize_v> &BasicString<SmallStringSize_v>::operator=(int i) noexcept {
	this->operator=(static_cast<int64_t>(i));
	return *this;
}

template<std::size_t SmallStringSize_v>
constexpr BasicString<SmallStringSize_v> &BasicString<SmallStringSize_v>::operator=(int64_t i) noexcept {
	set(ox::intToStr(i));
	return *this;
}

template<std::size_t SmallStringSize_v>
constexpr BasicString<SmallStringSize_v> &BasicString<SmallStringSize_v>::operator=(uint64_t i) noexcept {
	set(ox::intToStr(i));
	return *this;
}

template<std::size_t SmallStringSize_v>
constexpr BasicString<SmallStringSize_v> &BasicString<SmallStringSize_v>::operator=(const BasicString &src) noexcept {
	if (this != &src) {
		set(src);
	}
	return *this;
}

template<std::size_t SmallStringSize_v>
constexpr BasicString<SmallStringSize_v> &BasicString<SmallStringSize_v>::operator=(BasicString &&src) noexcept {
	if (this != &src) {
		m_buff = std::move(src.m_buff);
		src.m_buff.resize(1);
		src.m_buff[0] = 0;
	}
	return *this;
}

template<std::size_t SmallStringSize_v>
constexpr BasicString<SmallStringSize_v> &BasicString<SmallStringSize_v>::operator=(StringViewCR src) noexcept {
	set(src);
	return *this;
}

template<std::size_t SmallStringSize_v>
constexpr BasicString<SmallStringSize_v> &BasicString<SmallStringSize_v>::operator+=(const char *str) noexcept {
	std::size_t strLen = ox::strlen(str);
	std::ignore = append(str, strLen);
	return *this;
}

template<std::size_t SmallStringSize_v>
constexpr BasicString<SmallStringSize_v> &BasicString<SmallStringSize_v>::operator+=(char *str) noexcept {
	return *this += static_cast<const char*>(str);
}

template<std::size_t SmallStringSize_v>
constexpr BasicString<SmallStringSize_v> &BasicString<SmallStringSize_v>::operator+=(char c) noexcept {
	const char str[] = {c, 0};
	return this->operator+=(str);
}

template<std::size_t SmallStringSize_v>
constexpr BasicString<SmallStringSize_v> &BasicString<SmallStringSize_v>::operator+=(Integer_c auto i) noexcept {
	auto const str = ox::intToStr(i);
	return this->operator+=(str.c_str());
}

template<std::size_t SmallStringSize_v>
constexpr BasicString<SmallStringSize_v> &BasicString<SmallStringSize_v>::operator+=(StringViewCR s) noexcept {
	std::size_t strLen = s.bytes();
	std::ignore = append(s.data(), strLen);
	return *this;
}

template<std::size_t SmallStringSize_v>
constexpr BasicString<SmallStringSize_v> &BasicString<SmallStringSize_v>::operator+=(BasicString const&src) noexcept {
	std::ignore = append(src.c_str(), src.size());
	return *this;
}

template<std::size_t SmallStringSize_v>
constexpr BasicString<SmallStringSize_v> BasicString<SmallStringSize_v>::operator+(const char *str) const noexcept {
	const std::size_t strLen = ox::strlen(str);
	const auto currentLen = size();
	BasicString<SmallStringSize_v> cpy;
	cpy.m_buff.resize(m_buff.size() + strLen);
	ox::listcpy(&cpy.m_buff[0], m_buff.data(), currentLen);
	ox::listcpy(&cpy.m_buff[currentLen], str, strLen);
	// make sure last element is a null terminator
	cpy.m_buff[currentLen + strLen] = 0;
	return cpy;
}

template<std::size_t SmallStringSize_v>
constexpr BasicString<SmallStringSize_v> BasicString<SmallStringSize_v>::operator+(char *str) const noexcept {
	return *this + static_cast<const char*>(str);
}

template<std::size_t SmallStringSize_v>
constexpr BasicString<SmallStringSize_v> BasicString<SmallStringSize_v>::operator+(char c) const noexcept {
	const char str[] = {c, 0};
	return *this + str;
}

template<std::size_t SmallStringSize_v>
constexpr BasicString<SmallStringSize_v> BasicString<SmallStringSize_v>::operator+(Integer_c auto i) const noexcept {
	auto const str = ox::intToStr(i);
	return *this + str;
}

template<std::size_t SmallStringSize_v>
constexpr BasicString<SmallStringSize_v> BasicString<SmallStringSize_v>::operator+(StringViewCR src) const noexcept {
	const std::size_t strLen = src.size();
	const auto currentLen = size();
	BasicString<SmallStringSize_v> cpy(currentLen + strLen);
	cpy.m_buff.resize(m_buff.size() + strLen);
	ox::listcpy(&cpy.m_buff[0], m_buff.data(), currentLen);
	ox::listcpy(&cpy.m_buff[currentLen], src.data(), strLen);
	cpy.m_buff[cpy.m_buff.size() - 1] = 0;
	return cpy;
}

template<std::size_t SmallStringSize_v>
constexpr BasicString<SmallStringSize_v> BasicString<SmallStringSize_v>::operator+(BasicString const&src) const noexcept {
	const std::size_t strLen = src.size();
	const auto currentLen = size();
	BasicString<SmallStringSize_v> cpy(currentLen + strLen);
	cpy.m_buff.resize(m_buff.size() + strLen);
	ox::listcpy(&cpy.m_buff[0], m_buff.data(), currentLen);
	ox::listcpy(&cpy.m_buff[currentLen], src.data(), strLen);
	cpy.m_buff[cpy.m_buff.size() - 1] = 0;
	return cpy;
}

template<std::size_t SmallStringSize_v>
constexpr bool BasicString<SmallStringSize_v>::operator==(const char *other) const noexcept {
	bool retval = true;
	for (auto i = 0u; i < m_buff.size() && (m_buff[i] || other[i]); ++i) {
		if (m_buff[i] != other[i]) {
			retval = false;
			break;
		}
	}
	return retval;
}

template<std::size_t SmallStringSize_v>
constexpr bool BasicString<SmallStringSize_v>::operator==(OxString_c auto const&other) const noexcept {
	return StringView(*this) == StringView(other);
}

template<std::size_t SmallStringSize_v>
constexpr bool BasicString<SmallStringSize_v>::operator!=(const char *other) const noexcept {
	return !operator==(other);
}

template<std::size_t SmallStringSize_v>
constexpr bool BasicString<SmallStringSize_v>::operator!=(OxString_c auto const&other) const noexcept {
	return !operator==(other);
}

template<std::size_t SmallStringSize_v>
constexpr bool BasicString<SmallStringSize_v>::operator<(BasicString const&other) const noexcept {
	return ox::strcmp(c_str(), other.c_str()) < 0;
}

template<std::size_t SmallStringSize_v>
constexpr bool BasicString<SmallStringSize_v>::operator>(BasicString const&other) const noexcept {
	return ox::strcmp(c_str(), other.c_str()) > 0;
}

template<std::size_t SmallStringSize_v>
constexpr bool BasicString<SmallStringSize_v>::operator<=(BasicString const&other) const noexcept {
	return ox::strcmp(c_str(), other.c_str()) < 1;
}

template<std::size_t SmallStringSize_v>
constexpr bool BasicString<SmallStringSize_v>::operator>=(BasicString const&other) const noexcept {
	return ox::strcmp(c_str(), other.c_str()) > -1;
}

template<std::size_t SmallStringSize_v>
constexpr char const&BasicString<SmallStringSize_v>::operator[](std::size_t i) const noexcept {
	return m_buff[i];
}

template<std::size_t SmallStringSize_v>
constexpr char &BasicString<SmallStringSize_v>::operator[](std::size_t i) noexcept {
	return m_buff[i];
}

template<std::size_t SmallStringSize_v>
constexpr BasicString<SmallStringSize_v> BasicString<SmallStringSize_v>::substr(std::size_t pos) const noexcept {
	return BasicString(&m_buff[pos], m_buff.size() - pos - 1);
}

template<std::size_t SmallStringSize_v>
constexpr BasicString<SmallStringSize_v> BasicString<SmallStringSize_v>::substr(std::size_t begin, std::size_t end) const noexcept {
	const auto src = &m_buff[begin];
	const auto size = end - begin;
	BasicString<SmallStringSize_v> out(size);
	const auto buff = out.data();
	ox::listcpy(buff, src, size);
	buff[size] = 0;
	return out;
}

template<std::size_t SmallStringSize_v>
constexpr std::size_t BasicString<SmallStringSize_v>::bytes() const noexcept {
	return m_buff.size();
}

template<std::size_t SmallStringSize_v>
constexpr std::size_t BasicString<SmallStringSize_v>::size() const noexcept {
	return m_buff.size() - 1;
}

template<std::size_t SmallStringSize_v>
constexpr void BasicString<SmallStringSize_v>::set(StringViewCR str) noexcept {
	std::size_t const strBytes = str.bytes();
	m_buff.resize(strBytes + 1);
	copy_n(str.data(), strBytes, m_buff.data());
	*m_buff.back().value = 0;
}

template<std::size_t SmallStringSize_v>
constexpr void BasicString<SmallStringSize_v>::set(const char8_t *str) noexcept {
	std::size_t strBytes = ox::strlen(str) + 1;
	m_buff.resize(strBytes);
	ox::listcpy(m_buff.data(), str, strBytes);
	*m_buff.back().value = 0;
}

extern template class BasicString<8>;

using String = BasicString<8>;
using StringCR = String const&;


[[nodiscard]]
constexpr String toString(StringViewCR sv) noexcept {
	return ox::String(sv);
}


template<typename PlatSpec, std::size_t SmallStringSize_v>
[[nodiscard]]
constexpr auto sizeOf(BasicString<SmallStringSize_v> const*) noexcept {
	VectorMemMap<PlatSpec> v{.smallVecSize = SmallStringSize_v};
	return sizeOf<PlatSpec>(&v);
}

template<typename PlatSpec, std::size_t SmallStringSize_v>
[[nodiscard]]
constexpr auto alignOf(BasicString<SmallStringSize_v> const&) noexcept {
	VectorMemMap<PlatSpec> v{.smallVecSize = SmallStringSize_v};
	return alignOf<PlatSpec>(&v);
}

template<size_t sz>
struct MaybeView<BasicString<sz>> {
	using type = StringView;
};

}

OX_CLANG_NOWARN_END
