#include "RuntimeControlBridge.h"

#include "ControlServices.h"
#include "ControlServer.h"
#include "OpenGLComposite.h"
#include "OscServer.h"
#include "RuntimeStore.h"

bool StartControlServicesBoundary(
	OpenGLComposite& composite,
	RuntimeStore& runtimeStore,
	ControlServices& controlServices,
	ControlServer& controlServer,
	OscServer& oscServer,
	std::string& error)
{
	ControlServer::Callbacks callbacks;
	callbacks.getStateJson = [&composite]() { return composite.GetRuntimeStateJson(); };
	callbacks.addLayer = [&composite](const std::string& shaderId, std::string& actionError) { return composite.AddLayer(shaderId, actionError); };
	callbacks.removeLayer = [&composite](const std::string& layerId, std::string& actionError) { return composite.RemoveLayer(layerId, actionError); };
	callbacks.moveLayer = [&composite](const std::string& layerId, int direction, std::string& actionError) { return composite.MoveLayer(layerId, direction, actionError); };
	callbacks.moveLayerToIndex = [&composite](const std::string& layerId, std::size_t targetIndex, std::string& actionError) { return composite.MoveLayerToIndex(layerId, targetIndex, actionError); };
	callbacks.setLayerBypass = [&composite](const std::string& layerId, bool bypassed, std::string& actionError) { return composite.SetLayerBypass(layerId, bypassed, actionError); };
	callbacks.setLayerShader = [&composite](const std::string& layerId, const std::string& shaderId, std::string& actionError) { return composite.SetLayerShader(layerId, shaderId, actionError); };
	callbacks.updateLayerParameter = [&composite](const std::string& layerId, const std::string& parameterId, const std::string& valueJson, std::string& actionError) {
		return composite.UpdateLayerParameterJson(layerId, parameterId, valueJson, actionError);
	};
	callbacks.resetLayerParameters = [&composite](const std::string& layerId, std::string& actionError) { return composite.ResetLayerParameters(layerId, actionError); };
	callbacks.saveStackPreset = [&composite](const std::string& presetName, std::string& actionError) { return composite.SaveStackPreset(presetName, actionError); };
	callbacks.loadStackPreset = [&composite](const std::string& presetName, std::string& actionError) { return composite.LoadStackPreset(presetName, actionError); };
	callbacks.requestScreenshot = [&composite](std::string& actionError) { return composite.RequestScreenshot(actionError); };
	callbacks.reloadShader = [&composite](std::string& actionError) {
		if (!composite.ReloadShader())
		{
			actionError = "Shader reload failed. See native app status for details.";
			return false;
		}
		return true;
	};

	if (!controlServer.Start(runtimeStore.GetRuntimeUiRoot(), runtimeStore.GetRuntimeDocsRoot(), runtimeStore.GetConfiguredControlServerPort(), callbacks, error))
		return false;
	runtimeStore.SetBoundControlServerPort(controlServer.GetPort());

	OscServer::Callbacks oscCallbacks;
	oscCallbacks.updateParameter = [&controlServices](const std::string& layerKey, const std::string& parameterKey, const std::string& valueJson, std::string& actionError) {
		return controlServices.QueueOscUpdate(layerKey, parameterKey, valueJson, actionError);
	};
	if (runtimeStore.GetConfiguredOscPort() > 0 &&
		!oscServer.Start(runtimeStore.GetConfiguredOscBindAddress(), runtimeStore.GetConfiguredOscPort(), oscCallbacks, error))
		return false;

	return true;
}
