/*
 * Copyright 2015 - 2022 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 "cstrops.hpp"
#include "error.hpp"
#include "math.hpp"
#include "stringview.hpp"
#include "types.hpp"
#include "vector.hpp"
#include "writer.hpp"

namespace ox {

template<OxString_c Str>
[[nodiscard]]
constexpr ox::StringView substr(Str const&str, std::size_t pos) noexcept {
	if (str.len() >= pos) {
		return Str(str.data() + pos, str.len() - pos);
	}
	return Str();
}

template<OxString_c Str>
[[nodiscard]]
constexpr ox::StringView substr(Str const&str, std::size_t start, std::size_t end) noexcept {
	if (str.len() >= start && end >= start) {
		return Str(str.data() + start, end - start);
	}
	return Str();
}

template<typename Integer>
constexpr ox::Error itoa(Integer v, ox::Writer_c auto &writer) noexcept {
	if (v) {
		ox::ResizedInt_t<Integer, 64> mod = 1000000000000000000;
		ox::ResizedInt_t<Integer, 64> val = v;
		constexpr auto base = 10;
		auto it = 0;
		if (val < 0) {
			oxReturnError(writer.put('-'));
			++it;
		}
		while (mod) {
			auto digit = val / mod;
			val %= mod;
			mod /= base;
			if (it || digit) {
				ox::ResizedInt_t<Integer, 64> start = '0';
				if (digit >= 10) {
					start = 'a';
					digit -= 10;
				}
				oxReturnError(writer.put(static_cast<char>(start + digit)));
				++it;
			}
		}
	} else {
		// 0 is a special case
		oxReturnError(writer.put('0'));
	}
	return {};
}

[[nodiscard]]
constexpr bool beginsWith(CRStringView base, CRStringView beginning) noexcept {
	const auto beginningLen = ox::min(beginning.len(), base.len());
	return base.len() >= beginning.len() && ox_strncmp(base.data(), beginning, beginningLen) == 0;
}

[[nodiscard]]
constexpr bool endsWith(CRStringView base, CRStringView ending) noexcept {
	const auto endingLen = ending.len();
	return base.len() >= endingLen && ox_strcmp(base.data() + (base.len() - endingLen), ending) == 0;
}

constexpr std::size_t find(CRStringView str, char search) noexcept {
	std::size_t i = 0;
	for (; i < str.len(); ++i) {
		if (str[i] == search) {
			break;
		}
	}
	return i;
}

constexpr std::size_t find(CRStringView str, CRStringView search) noexcept {
	std::size_t i = 0;
	for (; i < str.len(); ++i) {
		if (beginsWith(substr(str, i), search)) {
			break;
		}
	}
	return i;
}

template<std::size_t smallSz = 0>
constexpr ox::Vector<ox::StringView, smallSz> split(CRStringView str, char del) noexcept {
	ox::Vector<ox::StringView, smallSz> out;
	constexpr auto nextSeg = [](CRStringView current, char del) {
		return substr(current, find(current, del) + 1);
	};
	for (auto current = str; current.len(); current = nextSeg(current, del)) {
		const auto next = find(current, del);
		if (const auto s = substr(current, 0, next); s.len()) {
			out.emplace_back(s);
		}
		current = substr(current, next);
	}
	return out;
}

template<std::size_t smallSz = 0>
constexpr ox::Vector<ox::StringView, smallSz> split(CRStringView str, CRStringView del) noexcept {
	ox::Vector<ox::StringView, smallSz> out;
	constexpr auto nextSeg = [](CRStringView current, CRStringView del) {
		return substr(current, find(current, del) + del.len());
	};
	for (auto current = str; current.len(); current = nextSeg(current, del)) {
		const auto next = find(current, del);
		if (const auto s = substr(current, 0, next); s.len()) {
			out.emplace_back(s);
		}
		current = substr(current, next);
	}
	return out;
}

[[nodiscard]]
constexpr ox::Result<std::size_t> lastIndexOf(ox::CRStringView str, int character) noexcept {
	ox::Result<std::size_t> retval = OxError(1, "Character not found");
	for (auto i = static_cast<int>(str.bytes() - 1); i >= 0; --i) {
		if (str[static_cast<std::size_t>(i)] == character) {
			retval = static_cast<std::size_t>(i);
		}
	}
	return retval;
}

}
