#include <iostream>
#include <stdexcept>
#include <opts.h>
#include <body/body.h>
#include <body/bodyThread.h>
#include <componentThread.h>
#include <deviceManager/deviceManager.h>
#include <mind.h>
#include <stimBuffApis/stimBuffApiManager.h>
#include <comparatorLibs/comparatorApiManager.h>

namespace smo {
namespace body {

Body::Body(Mind &parent, const std::shared_ptr<sscl::PuppetThread> &thread)
:	MindComponent(static_cast<sscl::PuppetApplication&>(parent), thread)
{
}

BodyViralPostingInvoker<void> Body::initializeCReq()
{
	auto self = sscl::ComponentThread::getSelf();
	if (self->id != SmoThreadId::BODY)
	{
		throw std::runtime_error(std::string(__func__)
			+ ": Must be executed on Body thread");
	}

	/**	EXPLANATION:
	 * Comparator libs are loaded before stim buff API libs so comparator
	 * types are registered before stimbuff libs can resolve them via
	 * SmoCallbacks during getStimBuffApiDesc.
	 *
	 * SmoThreadingModelDesc is initialized in Mind::initializeCReq before
	 * body::initializeCReq is entered.
	 */
	// Upcast to Mind to access Mind-specific members
	Mind &mind = static_cast<Mind&>(parent);

	comparator_lib::ComparatorApiManager::getInstance()
		.loadAllComparatorApiLibsFromOptions();

	stim_buff::StimBuffApiManager::getInstance()
		.loadAllStimBuffApiLibsFromOptions();

	/**	EXPLANATION:
	 * Consider body::initializeCReq to have been called if even one of its
	 * operations was executed at all, whether successfully or
	 * unsuccessfully.
	 */
	mind.bodyComponentInitialized = true;

	const std::string comparatorLibs =
		comparator_lib::ComparatorApiManager::getInstance().stringifyLibs();
	const std::string stimBuffLibs =
		stim_buff::StimBuffApiManager::getInstance().stringifyLibs();

	if (!comparatorLibs.empty()) {
		std::cout << comparatorLibs;
	}
	if (!comparatorLibs.empty() && !stimBuffLibs.empty()) {
		std::cout << '\n';
	}
	if (!stimBuffLibs.empty()) {
		std::cout << stimBuffLibs;
	}
	std::cout << std::endl;

	if (OptionParser::getOptions().verbose)
	{
		std::cout << __func__ << ": About to initializeAllStimBuffApiLibs"
			<< '\n';
	}
	co_await stim_buff::StimBuffApiManager::getInstance()
		.initializeAllStimBuffApiLibsCReq();

	if (OptionParser::getOptions().verbose)
	{
		std::cout << __func__ << ": About to attachAllUnattachedDevicesFromCmdline"
			<< '\n';
	}

	co_await device::DeviceManager::getInstance()
		.attachAllUnattachedDevicesFromCmdlineCReq();

	co_return;
}

BodyViralPostingInvoker<void> Body::finalizeCReq()
{
	auto self = sscl::ComponentThread::getSelf();
	if (self->id != SmoThreadId::BODY)
	{
		throw std::runtime_error(std::string(__func__)
			+ ": Must be executed on Body thread");
	}

	// Upcast to Mind to access Mind-specific members
	Mind &mind = static_cast<Mind&>(parent);
	if (!mind.bodyComponentInitialized)
	{
		std::cout << "Mrntt: Body component not initialized. "
			<< "Skipping finalization." << "\n";
		co_return;
	}

	std::cout << "Mrntt: About to detach all sense devices." << "\n";
	co_await device::DeviceManager::getInstance()
		.detachAllAttachedDeviceRolesCReq();

	std::cout << "Mrntt: About to finalize all stim buff api libs." << "\n";
	co_await stim_buff::StimBuffApiManager::getInstance()
		.finalizeAllStimBuffApiLibsCReq();

	std::cout << "Mrntt: About to unload all stim buff api libs." << "\n";
	stim_buff::StimBuffApiManager::getInstance().unloadAllStimBuffApiLibs();

	std::cout << "Mrntt: About to unload all comparator libs." << "\n";
	comparator_lib::ComparatorApiManager::getInstance()
		.unloadAllComparatorLibs();

	co_return;
}

} // namespace body
} // namespace smo
