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

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

#include <nostalgia/gfx/gfx.hpp>

using namespace nostalgia;

static int spriteX{};
static int spriteY{};
static bool s_paused = false;

[[maybe_unused]]
static int testUpdateHandler(turbine::Context &tctx) noexcept {
	auto &cctx = *turbine::applicationData<gfx::Context>(tctx);
	constexpr auto sleepTime = 16;
	if (s_paused) {
		return sleepTime;
	}
	int xmod = 0;
	int ymod = 0;
	if (buttonDown(tctx, turbine::Alpha_D) || buttonDown(tctx, turbine::GamePad_Right)) {
		xmod = 2;
	} else if (buttonDown(tctx, turbine::Alpha_A) || buttonDown(tctx, turbine::GamePad_Left)) {
		xmod = -2;
	}
	if (buttonDown(tctx, turbine::Alpha_S) || buttonDown(tctx, turbine::GamePad_Down)) {
		ymod = 2;
	} else if (buttonDown(tctx, turbine::Alpha_W) || buttonDown(tctx, turbine::GamePad_Up)) {
		ymod = -2;
	}
	if (!xmod && !ymod) {
		spriteX += 1;
	}
	spriteX += xmod;
	spriteY += ymod;
	constexpr ox::StringView sprites = "nostalgia";
	for (unsigned i = 0; i < sprites.size(); ++i) {
		auto const c = static_cast<unsigned>(sprites[i] - ('a' - 1));
		gfx::setSprite(cctx, i, {
			.enabled = true,
			.x = spriteX + 8 * (static_cast<int>(i) + 1),
			.y = spriteY,
			.tileIdx = c,
			.priority = 1,
		});
	}
	return sleepTime;
}

[[maybe_unused]]
static void testKeyEventHandler(turbine::Context &tctx, turbine::Key const key, bool const down) noexcept {
	if (down) {
		if (key == turbine::Key::Alpha_Q) {
			turbine::requestShutdown(tctx);
		} else if (key == turbine::Key::Alpha_P) {
			s_paused = !s_paused;
		}
	}
}

[[maybe_unused]]
static ox::Error runTest(turbine::Context &tctx) {
	OX_REQUIRE_M(cctx, gfx::init(tctx));
	turbine::setApplicationData(tctx, cctx.get());
	OX_RETURN_ERROR(gfx::loadSpriteTileSheet(*cctx, "/TileSheets/Charset.nts"));
	OX_RETURN_ERROR(gfx::loadSpritePalette(*cctx, "/Palettes/Chester.npal"));
	OX_RETURN_ERROR(gfx::initConsole(*cctx));
	gfx::consoleWrite(*cctx, 10, 9, "DOPENESS!!!");
	turbine::setUpdateHandler(tctx, testUpdateHandler);
	turbine::setKeyEventHandler(tctx, testKeyEventHandler);
	return turbine::run(tctx);
}


[[maybe_unused]]
static ox::Error runTileSheetSetTest(turbine::Context &tctx) {
	// this should make the screen display 'ABCDB', with the A being upside down
	// and the first B being backwards
	constexpr ox::StringView PaletteAddr{"/Palettes/Charset.npal"};
	OX_REQUIRE_M(cctx, gfx::init(tctx));
	turbine::setApplicationData(tctx, cctx.get());
	gfx::TileSheetSet const set{
		.bpp = 4,
		.entries = {
			{ .tilesheet = ox::StringLiteral{"/TileSheets/Chester.nts"}, .sections{{.begin = 0, .tiles = 1}} },
			{ .tilesheet = ox::StringLiteral{"/TileSheets/AB.nts"},      .sections{{.begin = 0, .tiles = 2}} },
			{ .tilesheet = ox::StringLiteral{"/TileSheets/CD.nts"},      .sections{{.begin = 0, .tiles = 2}} },
			{ .tilesheet = ox::StringLiteral{"/TileSheets/AB.nts"},      .sections{{.begin = 1, .tiles = 1}} },
		},
	};
	constexpr auto bgPalBank = 1;
	OX_RETURN_ERROR(gfx::loadBgTileSheet(*cctx, 0, set));
	OX_RETURN_ERROR(gfx::loadSpriteTileSheet(*cctx, set));
	OX_RETURN_ERROR(gfx::loadBgPalette(*cctx, bgPalBank, PaletteAddr));
	OX_RETURN_ERROR(gfx::loadBgPalette(*cctx, 0, PaletteAddr));
	OX_RETURN_ERROR(gfx::loadSpritePalette(*cctx, PaletteAddr));
	gfx::setBgStatus(*cctx, 0, true);
	gfx::setBgTile(*cctx, 0, 10, 9, { .tileIdx = 1, .palBank = bgPalBank, .flipX = 0, .flipY = 1 });
	gfx::setBgTile(*cctx, 0, 11, 9, { .tileIdx = 2, .palBank = bgPalBank, .flipX = 1, .flipY = 0 });
	gfx::setBgTile(*cctx, 0, 13, 9, { .tileIdx = 4, .palBank = bgPalBank, .flipX = 0, .flipY = 0 });
	gfx::setSprite(*cctx, 16, {
		.enabled = true,
		.x = 12 * 8,
		.y = 9 * 8,
		.tileIdx = 3,
		.bpp = static_cast<unsigned>(set.bpp),
	});
	gfx::setSprite(*cctx, 17, {
		.enabled = true,
		.x = 14 * 8,
		.y = 9 * 8,
		.tileIdx = 5,
		.bpp = static_cast<unsigned>(set.bpp),
	});
	turbine::setKeyEventHandler(tctx, testKeyEventHandler);
	return turbine::run(tctx);
}


ox::Error run(
		[[maybe_unused]] ox::StringView project,
		[[maybe_unused]] ox::StringView appName,
		[[maybe_unused]] ox::StringView projectDataDir,
		ox::SpanView<ox::CString> args) noexcept {
	if (args.size() < 2) {
		return ox::Error{1, "Please provide path to project directory or OxFS file."};
	}
	auto const path = args[1];
	OX_REQUIRE_M(tctx, turbine::init(path, project));
	return runTileSheetSetTest(*tctx);
}
