/*
 * 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 "array.hpp"
#include "concepts.hpp"
#include "def.hpp"
#include "cstrops.hpp"
#include "memops.hpp"
#include "error.hpp"
#include "buffer.hpp"
#include "ignore.hpp"
#include "stringview.hpp"
#include "typetraits.hpp"
#include "strconv.hpp"

namespace ox {

// Inline String
template<std::size_t StrCap>
class IString {
	private:
		size_t m_size{};
		ox::Array<char, StrCap + 1> m_buff;

	public:
		constexpr IString() noexcept;

		constexpr IString(StringView str) noexcept;

		constexpr IString(const char *str) noexcept;

		constexpr IString &operator=(StringViewCR str) noexcept;

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

		constexpr IString &operator=(Integer_c auto i) noexcept;


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

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

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

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

		constexpr char 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;

		constexpr Error append(ox::StringView str) noexcept;

		[[nodiscard]]
		constexpr const char *data() const noexcept;

		[[nodiscard]]
		constexpr char *data() noexcept;

		[[nodiscard]]
		constexpr const char *c_str() const noexcept;

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

		constexpr ox::Error resize(size_t sz) noexcept;

		/**
		 * Resizes without clearing memory between current end and new end.
		 */
		constexpr ox::Error unsafeResize(size_t sz) noexcept;

		/**
		 * Returns the capacity of bytes for this string.
		 */
		[[nodiscard]]
		constexpr static std::size_t cap() noexcept {
			return StrCap;
		}
};

template<std::size_t size>
constexpr IString<size>::IString() noexcept: m_buff{{0}} {
}

template<std::size_t size>
constexpr IString<size>::IString(StringView str) noexcept: m_buff{{0}} {
	operator=(str);
}

template<std::size_t size>
constexpr IString<size>::IString(const char *str) noexcept: m_buff{{0}} {
	operator=(str);
}

template<std::size_t size>
constexpr IString<size> &IString<size>::operator=(Integer_c auto i) noexcept {
	ox::Array<char, 22> s;
	ox::CharBuffWriter w(s);
	std::ignore = ox::writeItoa(i, w);
	this->operator=({s.data(), w.tellp()});
	return *this;
}

template<std::size_t size>
constexpr IString<size> &IString<size>::operator=(ox::StringViewCR str) noexcept {
	std::size_t strLen = str.size();
	if (cap() < strLen) {
		strLen = cap();
	}
	m_size = strLen;
	ox::listcpy(m_buff.data(), str.data(), strLen);
	// make sure last element is a null terminator
	m_buff[strLen] = 0;
	return *this;
}

template<std::size_t size>
constexpr IString<size> &IString<size>::operator=(const char *str) noexcept {
	operator=(ox::StringView{str});
	return *this;
}

template<std::size_t StrCap>
constexpr bool IString<StrCap>::operator==(const char *other) const noexcept {
	return ox::StringView(*this) == other;
}

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

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

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

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

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

template<std::size_t StrCap>
constexpr Error IString<StrCap>::append(const char *str, std::size_t strLen) noexcept {
	Error err{};
	auto const currentLen = size();
	if (cap() < currentLen + strLen) {
		strLen = cap() - currentLen;
		err = ox::Error(1, "Insufficient space for full string");
	}
OX_CLANG_NOWARN_BEGIN(-Wunsafe-buffer-usage)
	ox::strncpy(m_buff.data() + currentLen, str, strLen);
OX_CLANG_NOWARN_END
	// make sure last element is a null terminator
	m_buff[currentLen + strLen] = 0;
	m_size += strLen;
	return err;
}

template<std::size_t StrCap>
constexpr Error IString<StrCap>::append(ox::StringView str) noexcept {
	return append(str.data(), str.size());
}

template<std::size_t StrCap>
constexpr const char *IString<StrCap>::data() const noexcept {
	return static_cast<const char*>(m_buff.data());
}

template<std::size_t StrCap>
constexpr char *IString<StrCap>::data() noexcept {
	return static_cast<char*>(m_buff.data());
}

template<std::size_t StrCap>
constexpr const char *IString<StrCap>::c_str() const noexcept {
	return static_cast<const char*>(m_buff.data());
}


template<std::size_t StrCap>
constexpr std::size_t IString<StrCap>::size() const noexcept {
	return m_size;
}

template<std::size_t StrCap>
constexpr std::size_t IString<StrCap>::bytes() const noexcept {
	return m_size + 1;
}

template<std::size_t StrCap>
constexpr ox::Error IString<StrCap>::resize(size_t sz) noexcept {
	if (sz > StrCap) [[unlikely]] {
		return ox::Error(1, "Trying to extend IString beyond its cap");
	}
	for (auto i = m_size; i < sz; ++i) {
		m_buff[i] = 0;
	}
	m_size = sz;
	return {};
}

template<std::size_t StrCap>
constexpr ox::Error IString<StrCap>::unsafeResize(size_t sz) noexcept {
	if (sz > StrCap) [[unlikely]] {
		return ox::Error(1, "Trying to extend IString beyond its cap");
	}
	m_size = sz;
	return {};
}

template<size_t sz>
struct MaybeView<ox::IString<sz>> {
	using type = ox::StringView;
};


template<Integer_c Integer>
[[nodiscard]]
constexpr auto intToStr(Integer v) noexcept {
	constexpr auto Cap = [] {
		auto out = 0;
		switch (sizeof(Integer)) {
			case 1:
				out = 3;
				break;
			case 2:
				out = 5;
				break;
			case 4:
				out = 10;
				break;
			case 8:
				out = 21;
				break;
		}
		return out + ox::is_signed_v<Integer>;
	}();
	ox::IString<Cap> out;
	std::ignore = out.resize(out.cap());
	ox::CharBuffWriter w{{out.data(), out.cap()}};
	std::ignore = writeItoa(v, w);
	std::ignore = out.resize(w.tellp());
	return out;
}

}
