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

#include <ox/claw/read.hpp>

namespace ox {

Result<StringView> readClawTypeId(BufferView const buff) noexcept {
	auto buffRaw = buff.data();
	auto buffLen = buff.size();
	size_t outSz{};
	const auto s1End = ox::strchr(buffRaw, ';', buffLen);
	if (!s1End) {
		return ox::Error(1, "Could not read Claw header");
	}
	auto const fmtSz = static_cast<std::size_t>(s1End - buffRaw) + 1;
	buffRaw += fmtSz;
	buffLen -= fmtSz;
	outSz += fmtSz;
	auto const s2End = ox::strchr(buffRaw, ';', buffLen);
	if (!s2End) {
		return ox::Error(2, "Could not read Claw header");
	}
	auto const s2Size = static_cast<std::size_t>(s2End - buffRaw) + 1;
	buffRaw += s2Size;
	buffLen -= s2Size;
	outSz += s2Size;
	auto const s3End = ox::strchr(buffRaw, ';', buffLen) + 1;
	if (!s3End) {
		return ox::Error(3, "Could not read Claw header");
	}
	auto const s3Size = static_cast<std::size_t>(s3End - buffRaw);
	buffRaw += s3Size;
	buffLen -= s3Size;
	outSz += s3Size;
	return {{buff.data() + fmtSz, outSz - fmtSz - 1}};
}

Result<ClawHeader> readClawHeader(BufferView const buff) noexcept {
	auto buffRaw = buff.data();
	auto buffLen = buff.size();
	const auto s1End = ox::strchr(buffRaw, ';', buffLen);
	if (!s1End) {
		return ox::Error(1, "Could not read Claw header");
	}
	auto const s1Size = static_cast<std::size_t>(s1End - buffRaw);
	StringView const fmt(buffRaw, s1Size);
	buffRaw += s1Size + 1;
	buffLen -= s1Size + 1;

	auto const s2End = ox::strchr(buffRaw, ';', buffLen);
	if (!s2End) {
		return ox::Error(2, "Could not read Claw header");
	}
	auto const s2Size = static_cast<std::size_t>(s2End - buffRaw);
	StringView const typeName(buffRaw, s2Size);
	buffRaw += s2Size + 1;
	buffLen -= s2Size + 1;

	auto const s3End = ox::strchr(buffRaw, ';', buffLen);
	if (!s3End) {
		return ox::Error(3, "Could not read Claw header");
	}
	auto const s3Size = static_cast<std::size_t>(s3End - buffRaw);
	StringView const versionStr(buffRaw, s3Size);
	buffRaw += s3Size + 1;
	buffLen -= s3Size + 1;
	ClawHeader hdr;
	if (fmt == "M2") {
		hdr.fmt = ClawFormat::Metal;
	} else if (fmt == "O1") {
		hdr.fmt = ClawFormat::Organic;
	} else {
		return ox::Error(4, "Claw format does not match any supported format/version combo");
	}
	hdr.typeName = typeName;
	std::ignore = ox::strToInt(versionStr).copyTo(hdr.typeVersion);
	hdr.data = buffRaw;
	hdr.dataSize = buffLen;
	return hdr;
}

Result<BufferView> stripClawHeader(BufferView const buff) noexcept {
	OX_REQUIRE(header, readClawHeader(buff));
	return {{header.data, header.dataSize}};
}

Result<ModelObject> readClaw(TypeStore &ts, BufferView buff) noexcept {
	OX_REQUIRE(header, readClawHeader(buff));
	auto const [t, tdErr] = ts.getLoad(
			header.typeName, header.typeVersion, header.typeParams);
	if (tdErr) {
		return ox::Error(3, "Could not load type descriptor");
	}
	ModelObject obj;
	OX_RETURN_ERROR(obj.setType(t));
	switch (header.fmt) {
		case ClawFormat::Metal:
		{
			ox::BufferReader br({header.data, header.dataSize});
			MetalClawReader reader(br);
			OX_RETURN_ERROR(model(reader.interface(), &obj));
			return obj;
		}
		case ClawFormat::Organic:
		{
#ifdef OX_USE_STDLIB
			OrganicClawReader reader({header.data, header.dataSize});
			ModelHandlerInterface handler(reader);
			OX_RETURN_ERROR(model(&handler, &obj));
			return obj;
#else
			break;
#endif
		}
		case ClawFormat::None:
			return ox::Error(1);
	}
	return ox::Error(1);
}

}
