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

#include <lodepng.h>

#include <ox/clargs/clargs.hpp>
#include <ox/std/trace.hpp>

#include <studio/context.hpp>
#include <studio/project.hpp>

#include <nostalgia/gfx/palette.hpp>
#include <nostalgia/gfx/tilesheet.hpp>

#include "export-tilesheet.hpp"

namespace nostalgia::gfx {

static ox::Vector<uint32_t> normalizePixelSizes(
		ox::Vector<uint8_t> const &inPixels) noexcept {
	ox::Vector<uint32_t> outPixels;
	outPixels.reserve(inPixels.size());
	outPixels.resize(inPixels.size());
	for (size_t i{}; i < inPixels.size(); ++i) {
		outPixels[i] = inPixels[i];
	}
	return outPixels;
}

static ox::Vector<uint32_t> normalizePixelArrangement(
		ox::Vector<uint32_t> const &inPixels,
		int const cols,
		int const scale) {
	auto const scalePt = ox::Point{scale, scale};
	auto const width = cols * TileWidth;
	auto const height = static_cast<int>(inPixels.size()) / width;
	auto const dstWidth = width * scale;
	ox::Vector<uint32_t> outPixels(static_cast<size_t>((width * scale) * (height * scale)));
	for (size_t dstIdx{}; dstIdx < outPixels.size(); ++dstIdx) {
		auto const dstPt = ox::Point{
			static_cast<int>(dstIdx) % dstWidth,
			static_cast<int>(dstIdx) / dstWidth};
		auto const srcPt = dstPt / scalePt;
		auto const srcIdx = ptToIdx(srcPt, cols);
		outPixels[dstIdx] = inPixels[srcIdx];
	}
	return outPixels;
}

static ox::Error toPngFile(
		ox::CStringViewCR path,
		ox::Vector<uint32_t> &&pixels,
		Palette const &pal,
		size_t const page,
		unsigned const width,
		unsigned const height) noexcept {
	for (auto &c : pixels) {
		c = color32(color(pal, page, c)) | static_cast<Color32>(0XFF << 24);
	}
	constexpr auto fmt = LCT_RGBA;
	return ox::Error(static_cast<ox::ErrorCode>(
			lodepng_encode_file(
					path.c_str(),
					reinterpret_cast<uint8_t const*>(pixels.data()),
					width,
					height,
					fmt,
					8)));
}

ox::Error exportSubsheetToPng(
	TileSheet::SubSheet const &s,
	Palette const &pal,
	size_t const palPage,
	ox::CStringViewCR dstPath,
	int const scale) noexcept {
	// subsheet to png
	auto const width = s.columns * TileWidth;
	auto const height = s.rows * TileHeight;
	auto const err = toPngFile(
			dstPath,
			normalizePixelArrangement(
				normalizePixelSizes(s.pixels),
				s.columns,
				scale),
			pal,
			palPage,
			static_cast<unsigned>(width * scale),
			static_cast<unsigned>(height * scale));
	if (err) {
		oxErrorf("TileSheet export failed: {}", toStr(err));
		return ox::Error{7, "TileSheet export failed"};
	}
	return {};
}

ox::Error exportSubsheetToPng(
	TileSheet const &src,
	Palette const &pal,
	ox::StringViewCR subsheetPath,
	size_t const palPage,
	ox::CStringViewCR dstPath,
	int const scale) noexcept {
	// subsheet to png
	auto const [s, ssErr] = getSubSheet(
		ox::split(subsheetPath, '.'), src);
	if (ssErr) {
		return ox::Error{6, "failed to find SubSheet"};
	}
	return exportSubsheetToPng(
		*s,
		pal,
		palPage,
		dstPath,
		scale);
}

ox::Error cmdExportTilesheet(studio::Project &project, ox::SpanView<ox::CString> const args) noexcept {
	// parse args
	ox::ClArgs const clargs{args};
	bool showUsage = true;
	OX_DEFER [&showUsage] {
		if (showUsage) {
			oxErr("usage: export-tilesheet "
				"-src-path <path> "
				"-dst-path <path> "
				"[-pal-path <path>] "
				"[-subsheet-path <path>] "
				"[-scale <int>]\n");
		}
	};
	if (args.empty()) {
		return {};
	}
	OX_REQUIRE(srcPath, clargs.getString("src-path").transformError(1, "no src path specified"));
	OX_REQUIRE(dstPath, clargs.getString("dst-path").transformError(2, "no dst path specified"));
	auto const palPath = clargs.getString("pal-path", "");
	auto const subsheetPath = clargs.getString("subsheet-path").or_value(ox::String{"Root"});
	auto const palPage = static_cast<size_t>(clargs.getInt("pal-page", 0));
	auto const scale = clargs.getInt("scale", 1);
	showUsage = false;
	// load objects
	auto &kctx = project.kctx();
	OX_REQUIRE(ts, keel::readObj<TileSheet>(kctx, srcPath).transformError(4, "could not load TileSheet"));
	OX_REQUIRE(pal, keel::readObj<Palette>(kctx, palPath.size() ? palPath : ts->defaultPalette)
		.transformError(5, "could not load Palette"));
	// export to the destination file
	return exportSubsheetToPng(
		*ts,
		*pal,
		subsheetPath,
		palPage,
		dstPath,
		scale);
}

}
