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

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

#include <nostalgia/core/core.hpp>
#include <nostalgia/scene/scene.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<core::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.len(); ++i) {
		auto const c = static_cast<unsigned>(sprites[i] - ('a' - 1));
		core::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 key, bool 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) {
	constexpr ox::FileAddress TileSheetAddr = ox::StringLiteral("/TileSheets/Charset.ng");
	constexpr ox::FileAddress PaletteAddr = ox::StringLiteral("/Palettes/Chester.npal");
	oxRequireM(cctx, core::init(tctx));
	turbine::setApplicationData(tctx, cctx.get());
	oxRequire(tsStat, turbine::rom(tctx)->stat(PaletteAddr));
	oxReturnError(core::loadSpriteTileSheet(*cctx, TileSheetAddr));
	oxReturnError(core::loadSpritePalette(*cctx, PaletteAddr));
	oxReturnError(core::initConsole(*cctx));
	core::puts(*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::FileAddress PaletteAddr = ox::StringLiteral("/Palettes/Charset.npal");
	oxRequireM(cctx, core::init(tctx));
	turbine::setApplicationData(tctx, cctx.get());
	oxRequire(tsStat, turbine::rom(tctx)->stat(PaletteAddr));
	core::TileSheetSet const set{
		.bpp = 4,
		.entries = {
			{ .tilesheet = ox::StringLiteral("/TileSheets/Chester.ng"), .sections{{.begin = 0, .tiles = 1}} },
			{ .tilesheet = ox::StringLiteral("/TileSheets/AB.ng"),      .sections{{.begin = 0, .tiles = 2}} },
			{ .tilesheet = ox::StringLiteral("/TileSheets/CD.ng"),      .sections{{.begin = 0, .tiles = 2}} },
			{ .tilesheet = ox::StringLiteral("/TileSheets/AB.ng"),      .sections{{.begin = 1, .tiles = 1}} },
		},
	};
	constexpr auto bgPalBank = 1;
	oxReturnError(core::loadBgTileSheet(*cctx, 0, set));
	oxReturnError(core::loadSpriteTileSheet(*cctx, set));
	oxReturnError(core::loadBgPalette(*cctx, bgPalBank, PaletteAddr));
	oxReturnError(core::loadBgPalette(*cctx, 0, ox::StringLiteral("/Palettes/Chester.npal")));
	oxReturnError(core::loadSpritePalette(*cctx, PaletteAddr));
	core::setBgStatus(*cctx, 0, true);
	core::setBgTile(*cctx, 0, 10, 9, { .tileIdx = 1, .palBank = bgPalBank, .flipX = 0, .flipY = 1 });
	core::setBgTile(*cctx, 0, 11, 9, { .tileIdx = 2, .palBank = bgPalBank, .flipX = 1, .flipY = 0 });
	core::setBgTile(*cctx, 0, 13, 9, { .tileIdx = 4, .palBank = bgPalBank, .flipX = 0, .flipY = 0 });
	core::setSprite(*cctx, 16, {
		.enabled = true,
		.x = 12 * 8,
		.y = 9 * 8,
		.tileIdx = 3,
		.bpp = static_cast<unsigned>(set.bpp),
	});
	core::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);
}


static int sceneUpdateHandler(turbine::Context&) noexcept {
	constexpr auto sleepTime = 16;
	if (s_paused) {
		return sleepTime;
	}
	// do stuff
	return sleepTime;
}

static void sceneKeyEventHandler(turbine::Context &tctx, turbine::Key key, bool 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 runScene(turbine::Context &tctx) {
	constexpr ox::FileAddress SceneAddr = ox::StringLiteral("/Scenes/Chester.nscn");
	oxRequireM(cctx, core::init(tctx));
	oxRequire(scn, keel::readObj<scene::SceneStatic>(keelCtx(tctx), SceneAddr));
	turbine::setUpdateHandler(tctx, sceneUpdateHandler);
	turbine::setKeyEventHandler(tctx, sceneKeyEventHandler);
	scene::Scene const scene(*scn);
	oxReturnError(scene.setupDisplay(*cctx));
	return turbine::run(tctx);
}

ox::Error run(ox::UniquePtr<ox::FileSystem> &&fs) noexcept {
	oxRequireM(tctx, turbine::init(std::move(fs), "Nostalgia"));
	return runTileSheetSetTest(*tctx);
}
