/*
 * Copyright 2016 - 2024 Gary Talent (gary@drinkingtea.net). All rights reserved.
 */

#include <ox/claw/read.hpp>

#include <keel/media.hpp>
#include <keel/typeconv.hpp>

namespace keel {

[[nodiscard]]
static ox::Result<const BaseConverter*> findConverter(
		ox::Vector<const BaseConverter*> const&converters,
		ox::CRStringView srcTypeName,
		int srcTypeVersion,
		ox::CRStringView dstTypeName,
		int dstTypeVersion) noexcept {
	for (auto &c : converters) {
		if (c->matches(srcTypeName, srcTypeVersion, dstTypeName, dstTypeVersion)) {
			return c;
		}
	}
	return OxError(1, "Could not find converter");
};

static ox::Result<ox::UniquePtr<Wrap>> convert(
		[[maybe_unused]] keel::Context &ctx,
		ox::Vector<const BaseConverter*> const&converters,
		[[maybe_unused]] ox::Buffer const&srcBuffer,
		[[maybe_unused]] ox::CRStringView srcTypeName,
		[[maybe_unused]] int srcTypeVersion,
		[[maybe_unused]] ox::CRStringView dstTypeName,
		[[maybe_unused]] int dstTypeVersion) noexcept {
	// look for direct converter
	auto [c, err] = findConverter(converters, srcTypeName, srcTypeVersion, dstTypeName, dstTypeVersion);
	if (!err) {
		return c->convertBuffToPtr(ctx, srcBuffer);
	}
	// try to chain multiple converters
	for (const auto &subConverter : converters) {
		if (!subConverter->dstMatches(dstTypeName, dstTypeVersion)) {
			continue;
		}
		const auto [intermediate, chainErr] =
			convert(ctx, converters, srcBuffer, srcTypeName, srcTypeVersion,
			        subConverter->srcTypeName(), subConverter->srcTypeVersion());
		if (!chainErr) {
			return subConverter->convertPtrToPtr(ctx, *intermediate);
		}
	}
	return OxError(1, "Could not convert between types");
}

ox::Result<ox::UniquePtr<Wrap>> convert(
		[[maybe_unused]] keel::Context &ctx,
		[[maybe_unused]] ox::Buffer const&srcBuffer,
		[[maybe_unused]] ox::CRStringView dstTypeName,
		[[maybe_unused]] int dstTypeVersion) noexcept {
#ifndef OX_BARE_METAL
	oxRequire(hdr, readAssetHeader(srcBuffer));
	return convert(
			ctx,
			ctx.converters,
			srcBuffer,
			hdr.clawHdr.typeName,
			hdr.clawHdr.typeVersion,
			dstTypeName,
			dstTypeVersion);
#else
	return OxError(1, "Operation not supported on this platform");
#endif
}

}
