
#include "MainLoop.h"
#include "Input.h"
#include <stdio.h>

// Live++ API
#include "LivePP/API/x64/LPP_API_x64_CPP.h"


// plugin handling
#if DEBUG_BUILD
static const char* const PluginFilename = "12_DynamicallyLoadedDLLsPlugin_Debug.dll";
#else
static const char* const PluginFilename = "12_DynamicallyLoadedDLLsPlugin_Release.dll";
#endif

static HMODULE g_plugin = nullptr;
static Input g_loadPluginInput(Input::Type::LoadPlugin);
static Input g_unloadPluginInput(Input::Type::UnloadPlugin);

// helper that loads the plugin in case it is not loaded already
static void LoadPlugin(void)
{
	if (!g_plugin)
	{
		printf("Loading plugin...\n");
		g_plugin = ::LoadLibraryA(PluginFilename);
	}
}

// helper that unloads the plugin in case it is loaded already
static void UnloadPlugin(void)
{
	if (g_plugin)
	{
		printf("Unloading plugin...\n");
		::FreeLibrary(g_plugin);
		g_plugin = nullptr;
	}
}


// main entry point
int main(void)
{
	// create a synchronized Live++ agent
	lpp::LppSynchronizedAgent lppAgent = lpp::LppCreateSynchronizedAgent(nullptr, L"../../../LivePP");
	if (!lpp::LppIsValidSynchronizedAgent(&lppAgent))
	{
		return 1;
	}

	lppAgent.EnableModule(lpp::LppGetCurrentModulePath(), lpp::LPP_MODULES_OPTION_NONE, nullptr, nullptr);

	// make Live++ handle dynamically loaded modules automatically, enabling them on load, disabling them on unload
	lppAgent.EnableAutomaticHandlingOfDynamicallyLoadedModules(nullptr, nullptr);

	// init the example
	MainLoop::Init("Example 12: Dynamically loaded DLLs");
	MainLoop::Announce(lppAgent, "../../readme/README_DynamicallyLoadedDLLs.txt");

	// load the plugin that converts the framebuffer to grayscale.
	// loading the DLL will automatically enable it in Live++ so its code can be hot-reloaded as well.
	// the plugin can be loaded and unloaded by pressing "L" and "U", respectively.
	LoadPlugin();

	while (MainLoop::PollInput(lppAgent))
	{
		// check input for loading and unloading the plugin
		{
			g_loadPluginInput.Poll();
			g_unloadPluginInput.Poll();

			if (g_loadPluginInput.WentDown())
			{
				LoadPlugin();
			}

			if (g_unloadPluginInput.WentDown())
			{
				UnloadPlugin();
			}

			g_loadPluginInput.Next();
			g_unloadPluginInput.Next();
		}

		// listen to hot-reload and hot-restart requests
		if (lppAgent.WantsReload(lpp::LPP_RELOAD_OPTION_SYNCHRONIZE_WITH_RELOAD))
		{
			// Live++: client code can do whatever it wants here, e.g. synchronize across several threads, the network, etc.
			lppAgent.Reload(lpp::LPP_RELOAD_BEHAVIOUR_WAIT_UNTIL_CHANGES_ARE_APPLIED);
		}

		if (lppAgent.WantsRestart())
		{
			// Live++: client code can do whatever it wants here, e.g. finish logging, abandon threads, etc.
			lppAgent.Restart(lpp::LPP_RESTART_BEHAVIOUR_INSTANT_TERMINATION, 0u, nullptr);
		}

		// run the main loop
		MainLoop::Update();
		MainLoop::Render();

		if (g_plugin)
		{
			MainLoop::RenderPlugin(g_plugin);
		}

		MainLoop::Present();
	}

	UnloadPlugin();

	MainLoop::Exit();

	lpp::LppDestroySynchronizedAgent(&lppAgent);

	return 0;
}
