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

#include <ox/std/buffer.hpp>

#include "read.hpp"

namespace ox {

Result<ClawHeader> readClawHeader(const char *buff, std::size_t buffLen) noexcept {
	const auto s1End = ox::strchr(buff, ';', buffLen);
	if (!s1End) {
		return OxError(1, "Could not read Claw header");
	}
	const auto s1Size = static_cast<std::size_t>(s1End - buff);
	const String fmt(buff, s1Size);
	buff += s1Size + 1;
	buffLen -= s1Size + 1;

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

	const auto s3End = ox::strchr(buff, ';', buffLen);
	if (!s3End) {
		return OxError(3, "Could not read Claw header");
	}
	const auto s3Size = static_cast<std::size_t>(s3End - buff);
	const String versionStr(buff, s3Size);
	buff += s3Size + 1;
	buffLen -= s3Size + 1;

	ClawHeader hdr;
	if (fmt == "M2") {
		hdr.fmt = ClawFormat::Metal;
	} else if (fmt == "O1") {
		hdr.fmt = ClawFormat::Organic;
	} else {
		return OxError(4, "Claw format does not match any supported format/version combo");
	}
	hdr.typeName = typeName;
	if (auto r = ox::atoi(versionStr.c_str()); r.error == 0) {
		hdr.typeVersion = r.value;
	}
	hdr.data = buff;
	hdr.dataSize = buffLen;
	return hdr;
}

Result<ClawHeader> readClawHeader(const ox::Buffer &buff) noexcept {
	return readClawHeader(buff.data(), buff.size());
}

Result<Buffer> stripClawHeader(const char *buff, std::size_t buffLen) noexcept {
	oxRequire(header, readClawHeader(buff, buffLen));
	Buffer out(header.dataSize);
	ox::listcpy(out.data(), header.data, out.size());
	return out;
}

Result<Buffer> stripClawHeader(const ox::Buffer &buff) noexcept {
	return stripClawHeader(buff.data(), buff.size());
}

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

Result<ModelObject> readClaw(TypeStore &ts, const Buffer &buff) noexcept {
	return readClaw(ts, buff.data(), buff.size());
}

}
