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

#include <ox/claw/read.hpp>

#include "gfx.hpp"

#include "typeconv.hpp"

namespace nostalgia::core {

struct NostalgiaGraphicToTileSheetConverter: public Converter<NostalgiaGraphic, TileSheet> {
	ox::Error convert(NostalgiaGraphic *src, TileSheet *dst) noexcept final {
		dst->bpp              = src->bpp;
		dst->subsheet.name    = "Root";
		dst->subsheet.rows    = src->rows;
		dst->subsheet.columns = src->columns;
		dst->defaultPalette   = std::move(src->defaultPalette);
		dst->subsheet.pixels  = std::move(src->pixels);
		return OxError(0);
	}
};

struct NostalgiaPaletteToPaletteConverter: public Converter<NostalgiaPalette, Palette> {
	ox::Error convert(NostalgiaPalette *src, Palette *dst) noexcept final {
		dst->colors = std::move(src->colors);
		return OxError(0);
	}
};

struct TileSheetToCompactTileSheetConverter: public Converter<TileSheet, CompactTileSheet> {
	ox::Error convert(TileSheet *src, CompactTileSheet *dst) noexcept final {
		dst->bpp            = src->bpp;
		dst->defaultPalette = std::move(src->defaultPalette);
		dst->pixels         = src->pixels();
		return OxError(0);
	}
};

#ifndef OX_BARE_METAL

static const auto converters = [] {
	ox::Vector<ox::UniquePtr<BaseConverter>, 3> converters;
	converters.emplace_back(new NostalgiaGraphicToTileSheetConverter());
	converters.emplace_back(new NostalgiaPaletteToPaletteConverter());
	converters.emplace_back(new TileSheetToCompactTileSheetConverter());
	return converters;
}();

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

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

ox::Result<ox::UniquePtr<Wrap>> convert(const ox::Buffer &srcBuffer, const char *dstTypeName, int dstTypeVersion) noexcept {
	oxRequire(hdr, ox::readClawHeader(srcBuffer));
	return convert(srcBuffer, hdr.typeName.c_str(), hdr.typeVersion, dstTypeName, dstTypeVersion);
}

#endif

}
