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

#include <ox/std/bit.hpp>
#include <utility>

#include <ox/oc/read.hpp>

namespace ox {

OrganicClawReader::OrganicClawReader(const uint8_t *buff, size_t const buffSize) {
	auto json = reinterpret_cast<const char*>(buff);
	auto jsonLen = ox::strnlen_s(json, buffSize);
	Json::CharReaderBuilder parserBuilder;
	auto parser = std::unique_ptr<Json::CharReader>(parserBuilder.newCharReader());
	if (!parser->parse(json, json + jsonLen, &m_json, nullptr)) {
		throw Exception(1, "Could not parse JSON");
	}
}

OrganicClawReader::OrganicClawReader(CString const json, size_t const jsonLen) {
	Json::CharReaderBuilder parserBuilder;
	auto parser = std::unique_ptr<Json::CharReader>(parserBuilder.newCharReader());
	if (!parser->parse(json, json + jsonLen, &m_json, nullptr)) {
		throw Exception(1, "Could not parse JSON");
	}
}

OrganicClawReader::OrganicClawReader(Json::Value json, int const unionIdx) noexcept:
	m_json(std::move(json)),
	m_unionIdx(unionIdx) {
}

Error OrganicClawReader::field(CString const key, bool *val) noexcept {
	Error err{};
	if (targetValid()) {
		const auto &jv = value(key);
		if (jv.empty()) {
			*val = false;
		} else if (jv.isBool()) {
			*val = jv.asBool();
		} else {
			err = Error(1, "Type mismatch");
		}
	}
	++m_fieldIt;
	return err;
}

Error OrganicClawReader::fieldCString(CString const key, char *val, size_t const buffLen) noexcept {
	Error err{};
	CString begin = nullptr, end = nullptr;
	const auto &jv = value(key);
	if (targetValid()) {
		if (jv.empty()) {
			auto data = val;
			if (data) {
				data[0] = 0;
			}
		} else if (jv.isString()) {
			jv.getString(&begin, &end);
			const auto strSize = static_cast<size_t>(end - begin);
			auto data = val;
			if (strSize >= buffLen) {
				err = Error(2, "String size exceeds capacity of destination");
			} else {
				memcpy(data, begin, static_cast<size_t>(strSize));
				data[strSize] = 0;
			}
		} else {
			err = Error(1, "Type mismatch");
		}
	}
	++m_fieldIt;
	return err;
}

Error OrganicClawReader::fieldCString(CString const key, char **val) noexcept {
	Error err{};
	CString begin = nullptr, end = nullptr;
	const auto &jv = value(key);
	auto &data = *val;
	if (targetValid()) {
		if (jv.empty()) {
			if (data) {
				data[0] = 0;
			}
		} else if (jv.isString()) {
			jv.getString(&begin, &end);
			const auto strSize = static_cast<size_t>(end - begin);
			safeDelete(*val);
			*val = new char[strSize + 1];
			memcpy(data, begin, static_cast<size_t>(strSize));
			data[strSize] = 0;
		} else {
			err = Error(1, "Type mismatch");
		}
	}
	++m_fieldIt;
	return err;
}

Error OrganicClawReader::fieldCString(CString const key, char **val, size_t const buffLen) noexcept {
	Error err{};
	CString begin = nullptr, end = nullptr;
	const auto &jv = value(key);
	if (targetValid()) {
		if (jv.empty()) {
			auto data = val;
			if (data) {
				data[0] = nullptr;
			}
		} else if (jv.isString()) {
			jv.getString(&begin, &end);
			const auto strSize = static_cast<size_t>(end - begin);
			auto data = val;
			if (strSize >= buffLen) {
				safeDelete(*val);
				*val = new char[strSize + 1];
			}
			memcpy(data, begin, static_cast<size_t>(strSize));
			data[strSize] = nullptr;
		} else {
			err = Error(1, "Type mismatch");
		}
	}
	++m_fieldIt;
	return err;
}

Error OrganicClawReader::field(CString const key, UUID *val) noexcept {
	UUIDStr str;
	OX_RETURN_ERROR(field(key, &str));
	return UUID::fromString(str).moveTo(*val);
}

Result<size_t> OrganicClawReader::arrayLength(CString const key, bool) noexcept {
	const auto &jv = value(key);
	if (jv.empty()) {
		return 0;
	}
	if (jv.isArray()) {
		return jv.size();
	}
	return Error(1, "Type mismatch");
}

[[nodiscard]]
size_t OrganicClawReader::stringLength(CString const key) noexcept {
	CString begin = nullptr, end = nullptr;
	const auto &jv = value(key);
	if (jv.empty()) {
		return 0;
	}
	if (jv.isString()) {
		jv.getString(&begin, &end);
		return static_cast<size_t>(end - begin);
	}
	return Error(1, "Type mismatch");
}

OrganicClawReader OrganicClawReader::child(CString const key, int const unionIdx) noexcept {
	return OrganicClawReader(value(key), unionIdx);
}

bool OrganicClawReader::fieldPresent(CString const key) noexcept {
	return !m_json[key].empty();
}

int OrganicClawReader::whichFieldPresent(CString const name, ModelUnion const &u) const noexcept {
	const auto &obj = m_json[name];
	if (!obj.isObject()) {
		return -1;
	}
	const auto &keys = obj.getMemberNames();
	if (keys.size() != 1) {
		return -1;
	}
	return u.getKeyIdx(keys.front().c_str());
}

Json::Value &OrganicClawReader::value(CString const key) noexcept {
	if (m_json.isArray()) {
		return m_json[m_fieldIt];
	} else {
		return m_json[key];
	}
}

bool OrganicClawReader::targetValid() const noexcept {
	return static_cast<int>(m_fieldIt) == m_unionIdx || m_unionIdx == -1;
}

}
