#include <psemek/app/application.hpp>
#include <psemek/gfx/init.hpp>
#include <psemek/sdl2/init.hpp>
#include <psemek/sdl2/window.hpp>
#include <psemek/sdl2/events.hpp>
#include <psemek/util/clock.hpp>
#include <psemek/util/pretty_print.hpp>
#include <psemek/log/log.hpp>
#include <psemek/log/colored_stream.hpp>
#include <psemek/io/synchronized.hpp>
#include <psemek/util/exception.hpp>
#include <psemek/util/terminal_color.hpp>

#undef main

int main(int argc, char ** argv) try
{
	using namespace psemek;

	util::clock<std::chrono::milliseconds, std::chrono::high_resolution_clock> clock;

#ifdef PSEMEK_PACKAGE_MODE
	log::level const stdio_log_level = log::level::info;
#else
	log::level const stdio_log_level = log::level::debug;
#endif

	std::vector<std::unique_ptr<io::ostream>> synchronized_stdout_stderr;
	synchronized_stdout_stderr.push_back(io::std_out());
	synchronized_stdout_stderr.push_back(io::std_err());
	synchronized_stdout_stderr = io::synchronized(std::move(synchronized_stdout_stderr));

	if (util::terminal_color::supported())
		synchronized_stdout_stderr[1] = log::colored_stream(std::move(synchronized_stdout_stderr[1]), std::string{util::terminal_color::red()}, std::string{util::terminal_color::normal()});

	log::add_sink(log::default_sink(std::move(synchronized_stdout_stderr[0]), stdio_log_level, log::level::info));
	log::add_sink(log::default_sink(std::move(synchronized_stdout_stderr[1]), log::level::warning, log::level::error));
	log::register_thread("main");

	auto const factory = app::make_application_factory();
	auto const options = factory->options();

	sdl2::window window(options);

	app::application::context context;
	for (int i = 0; i < argc; ++i)
		context.args.push_back(argv[i]);
	context.show_cursor = [&](bool show){ window.show_cursor(show); };
	context.relative_mouse_mode = [&](bool mode){ window.relative_mouse_mode(mode); };
	context.windowed = [&](bool on){ window.windowed(on); };
	context.text_input = [](bool on){ if (on) SDL_StartTextInput(); else SDL_StopTextInput(); };

#if defined(PSEMEK_GRAPHICS_API_OPENGL)
	context.vsync = [&](bool on){ window.vsync(on); };
#endif

#if defined(PSEMEK_GRAPHICS_API_WEBGPU)
	context.instance = window.wgpu_instance();
	context.adapter = window.wgpu_adapter();
	context.surface = window.wgpu_surface();
	context.device = window.wgpu_device();
#endif

	auto application = factory->create(options, context);

	if (!application)
		return EXIT_FAILURE;

	application->on_event(app::resize_event{window.size()});

	window.show();

	log::info() << "Started in " << util::pretty(clock.duration(), std::chrono::milliseconds{1});
	log::info() << "Running";

	SDL_StopTextInput();

	while (application->running())
	{
		if (sdl2::poll_events(*application))
			application->stop();
		if (!application->running()) break;
		application->update();
		application->present();
		window.swap();
	}

	log::info() << "Quitting";
	return EXIT_SUCCESS;
}
catch (psemek::util::exception const & e)
{
	psemek::log::error() << e;
	return EXIT_FAILURE;
}
catch (std::exception const & e)
{
	psemek::log::error() << e.what();
	return EXIT_FAILURE;
}
catch (...)
{
	psemek::log::error() << "Unknown exception";
	throw;
}
