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

#include <ctime>

#include <ox/logconn/logconn.hpp>
#include <ox/logconn/def.hpp>
#include <ox/std/trace.hpp>
#include <ox/std/uuid.hpp>
#include <keel/media.hpp>
#include <turbine/turbine.hpp>

#include <studio/context.hpp>
#include <studioapp/studioapp.hpp>
#include "studioapp.hpp"

namespace studio {

class StudioUIDrawer: public turbine::gl::Drawer {
	private:
		StudioUI &m_ui;
	public:
		explicit StudioUIDrawer(StudioUI &ui) noexcept: m_ui(ui) {
		}
	protected:
		void draw(turbine::Context&) noexcept final {
			m_ui.draw();
		}
};

static int updateHandler(turbine::Context &ctx) noexcept {
	auto sctx = turbine::applicationData<studio::StudioContext>(ctx);
	auto ui = dynamic_cast<StudioUI*>(sctx->ui);
	ui->update();
	return 16;
}

static void keyEventHandler(turbine::Context &ctx, turbine::Key key, bool down) noexcept {
	auto sctx = turbine::applicationData<studio::StudioContext>(ctx);
	auto ui = dynamic_cast<StudioUI*>(sctx->ui);
	ui->handleKeyEvent(key, down);
}

static ox::Error runApp(
		ox::CRStringView appName,
		ox::CRStringView projectDataDir,
		ox::UniquePtr<ox::FileSystem> fs) noexcept {
	oxRequireM(ctx, turbine::init(std::move(fs), appName));
	turbine::setWindowTitle(*ctx, keelCtx(*ctx).appName);
	turbine::setUpdateHandler(*ctx, updateHandler);
	turbine::setKeyEventHandler(*ctx, keyEventHandler);
	turbine::setConstantRefresh(*ctx, false);
	studio::StudioContext studioCtx;
	turbine::setApplicationData(*ctx, &studioCtx);
	StudioUI ui(ctx.get(), projectDataDir);
	studioCtx.ui = &ui;
	StudioUIDrawer drawer(ui);
	turbine::gl::addDrawer(*ctx, &drawer);
	const auto err = turbine::run(*ctx);
	turbine::gl::removeDrawer(*ctx, &drawer);
	return err;
}

ox::Error run(
		ox::CRStringView appName,
		ox::CRStringView projectDataDir,
		int,
		const char**) {
	// seed UUID generator
	const auto time = std::time(nullptr);
	ox::UUID::seedGenerator({
		static_cast<uint64_t>(time),
		static_cast<uint64_t>(time << 1)
	});
	// run app
	const auto err = runApp(appName, projectDataDir, ox::UniquePtr<ox::FileSystem>(nullptr));
	oxAssert(err, "Something went wrong...");
	return err;
}

}

namespace olympic {

ox::Error run(
		ox::StringView project,
		ox::StringView appName,
		ox::StringView projectDataDir,
		int argc,
		const char **argv) noexcept {
	return studio::run(ox::sfmt("{} {}", project, appName), projectDataDir, argc, argv);
}

}
