#include <iostream>
#include <asynchronousContinuation.h>
#include <asynchronousLoop.h>
#include <component.h>
#include <componentThread.h>
#include <mindManager/mindManager.h>
#include <marionette/marionette.h>

namespace smo {
namespace mrntt {

class MarionetteComponent::MrnttLifetimeMgmtOp
:	public AsynchronousContinuation<mrnttLifetimeMgmtOpCbFn>,
	public ContinuationTarget
{
public:
	MrnttLifetimeMgmtOp(
		MarionetteComponent &parent, const std::shared_ptr<ComponentThread> &caller,
		mrnttLifetimeMgmtOpCbFn callback)
	:	AsynchronousContinuation<mrnttLifetimeMgmtOpCbFn>(callback),
		ContinuationTarget(caller),
		parent(parent)
	{}

	void callOriginalCbFn(bool success)
	{
		if (originalCbFn)
		{
			caller->getIoService().post(
				std::bind(originalCbFn, success));
		}
	}

private:
	MarionetteComponent &parent;

public:
	void initializeReq1_posted(
		[[maybe_unused]] std::shared_ptr<MrnttLifetimeMgmtOp> context
		)
	{
		auto self = ComponentThread::getSelf();
		if (self->id != ComponentThread::MRNTT)
		{
			throw std::runtime_error(std::string(__func__)
				+ ": Must be executed on Marionette thread");
		}

		smo::mind::globalMind = std::make_shared<Mind>();
		smo::mind::globalMind->initializeReq(
			std::bind(
				&MrnttLifetimeMgmtOp::initializeReq2,
				this, context, std::placeholders::_1));
	}

	void initializeReq2(
		std::shared_ptr<MrnttLifetimeMgmtOp> context,
		bool success
		)
	{
		if (!success)
		{
			std::cerr << __func__ << ": Failed to initialize globalMind"
				<< std::endl;
			context->callOriginalCbFn(false);
			return;
		}

		context->callOriginalCbFn(success);
	}

	void finalizeReq1_posted(
		[[maybe_unused]] std::shared_ptr<MrnttLifetimeMgmtOp> context
		)
	{
		auto self = ComponentThread::getSelf();
		if (self->id != ComponentThread::MRNTT)
		{
			throw std::runtime_error(std::string(__func__)
				+ ": Must be executed on Marionette thread");
		}

		smo::mind::globalMind->finalizeReq(
			std::bind(
				&MrnttLifetimeMgmtOp::finalizeReq2,
				this, context, std::placeholders::_1));
	}

	void finalizeReq2(
		std::shared_ptr<MrnttLifetimeMgmtOp> context,
		bool success
		)
	{
		if (!success)
		{
			std::cerr << __func__ << ": globalMind finalization failed"
				<< std::endl;
			context->callOriginalCbFn(false);
			return;
		}

		context->callOriginalCbFn(success);
	}
};

void MarionetteComponent::initializeReq(mrnttLifetimeMgmtOpCbFn callback)
{
	auto mrntt = ComponentThread::getSelf();

	if (mrntt->id != ComponentThread::MRNTT)
	{
		throw std::runtime_error(std::string(__func__)
			+ ": Must be executed on Marionette thread");
	}

	auto request = std::make_shared<MrnttLifetimeMgmtOp>(
		*this, mrntt, callback);

	mrntt->getIoService().post(
		std::bind(
			&MrnttLifetimeMgmtOp::initializeReq1_posted,
			request.get(), request));
}

void MarionetteComponent::finalizeReq(mrnttLifetimeMgmtOpCbFn callback)
{
	auto mrntt = ComponentThread::getSelf();

	if (mrntt->id != ComponentThread::MRNTT)
	{
		throw std::runtime_error(std::string(__func__)
			+ ": Must be executed on Marionette thread");
	}

	auto request = std::make_shared<MrnttLifetimeMgmtOp>(
		*this, mrntt, callback);

	mrntt->getIoService().post(
		std::bind(
			&MrnttLifetimeMgmtOp::finalizeReq1_posted,
			request.get(), request));
}

} // namespace mrntt
} // namespace smo
