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

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

#include "realstd.hpp"
#include "error.hpp"
#include "ignore.hpp"
#include "istring.hpp"
#include "string.hpp"
#include "strops.hpp"
#include "types.hpp"
#include "typetraits.hpp"

OX_CLANG_NOWARN_BEGIN(-Wunsafe-buffer-usage)

namespace ox {
namespace detail {

template<bool force = false>
constexpr StringView toStringView(StringViewCR s) noexcept {
	return s;
}

template<bool force = false>
constexpr StringView toStringView(CString const s) noexcept {
	return s;
}

template<bool force = false, std::size_t size>
constexpr StringView toStringView(IString<size> const &s) noexcept {
	return s;
}

template<bool force = false>
constexpr StringView toStringView(StringLiteral const &s) noexcept {
	return s;
}

template<bool force = false, std::size_t size>
constexpr StringView toStringView(BasicString<size> const &s) noexcept {
	return s;
}

#if __has_include(<string>)
template<bool force = false>
#ifdef OX_OS_Darwin
constexpr
#else
inline
#endif
StringView toStringView(std::string const &s) noexcept {
	return s.c_str();
}
#endif

#if __has_include(<QString>)
template<bool force = false>
inline StringView toStringView(QString const &s) noexcept {
	return s.toUtf8().data();
}
#endif

template<bool force = false>
constexpr StringView toStringView(auto const&) noexcept requires(force) {
	return "<unstringable>";
}

class FmtArg {

	private:
		static constexpr auto DataSz = 23;
		Array<char, DataSz> dataStr{};

		template<typename T>
		constexpr StringView sv(T const &v, Span<char> dataStr) noexcept {
			if constexpr(is_bool_v<T>) {
				return v ? "true" : "false";
			} else if constexpr(is_integer_v<T>) {
				ox::CharBuffWriter w{dataStr};
				std::ignore = ox::writeItoa(v, w);
				return dataStr.data();
			} else {
				return toStringView(v);
			}
		}

	public:
		StringView const out;

		template<typename T>
		constexpr FmtArg(T const &v) noexcept: out(sv(v, dataStr)) {
		}

};

[[nodiscard]]
constexpr uint64_t argCount(StringViewCR str) noexcept {
	uint64_t cnt = 0;
	auto const prev = [str](std::size_t i) -> char {
		if (i > 0) {
			return str[i - 1];
		} else {
			return '\0';
		}
	};
	auto const next = [str](std::size_t i) -> char {
		if (i < str.bytes() - 1) {
			return str[i + 1];
		} else {
			return '\0';
		}
	};
	for (std::size_t i = 0; i < str.bytes(); ++i) {
		if (str[i] == '{' && prev(i) != '\\' && next(i) == '}') {
			++cnt;
		}
	}
	return cnt;
}

struct FmtSegment {
	CString str{};
	unsigned length{};

	constexpr bool operator==(FmtSegment const &o) const noexcept {
		return length == o.length && ox::strncmp(str, o.str, length) == 0;
	}

	constexpr bool operator!=(FmtSegment const &o) const noexcept {
		return length != o.length || ox::strncmp(str, o.str, length) != 0;
	}
};

template<std::size_t sz>
struct Fmt {
	static constexpr std::size_t size = sz;
	Array<FmtSegment, sz> segments;

	constexpr bool operator==(Fmt const &o) const noexcept {
		for (std::size_t i = 0; i < sz; ++i) {
			if (segments[i] != o.segments[i]) {
				return false;
			}
		}
		return true;
	}
};

template<std::size_t segementCnt>
[[nodiscard]]
constexpr Fmt<segementCnt> fmtSegments(StringViewCR fmt) noexcept {
	Fmt<segementCnt> out;
	auto const prev = [fmt](std::size_t const i) -> char {
		if (i > 0) {
			return fmt[i - 1];
		} else {
			return '\0';
		}
	};
	auto const next = [fmt](std::size_t const i) -> char {
		if (i < fmt.bytes() - 1) {
			return fmt[i + 1];
		} else {
			return '\0';
		}
	};
	auto current = &out.segments[0];
	current->str = fmt.data();
	for (std::size_t i = 0; i < fmt.bytes(); ++i) {
		if (fmt[i] == '{' && prev(i) != '\\' && next(i) == '}') {
			++current;
			current->str = fmt.data() + i + 2;
			current->length = 0;
			i += 1;
		} else {
			++current->length;
		}
	}
	return out;
}

}

template<typename StringType = String, typename ...Args>
[[nodiscard]]
constexpr StringType sfmt(StringViewCR fmt, Args&&... args) noexcept {
	assert(ox::detail::argCount(fmt) == sizeof...(args));
	StringType out;
	auto const fmtSegments = detail::fmtSegments<sizeof...(args)+1>(fmt);
	auto const &firstSegment = fmtSegments.segments[0];
	std::ignore = out.append(firstSegment.str, firstSegment.length);
	detail::FmtArg const elements[sizeof...(args)] = {args...};
	for (size_t i = 0; i < fmtSegments.size - 1; ++i) {
		std::ignore = out.append(elements[i].out);
		auto const &s = fmtSegments.segments[i + 1];
		std::ignore = out.append(s.str, s.length);
	}
	return out;
}

template<typename T = String>
constexpr Result<T> join(auto const &d, auto const &list) {
	if (!list.size()) {
		return T("");
	}
	T out;
	out += list[0];
	for (auto i = 1ul; i < list.size(); ++i) {
		out += d;
		out += list[i];
	}
	return out;
}

}

OX_CLANG_NOWARN_END
