#ifndef COMPONENT_THREAD_H
#define COMPONENT_THREAD_H

#include <atomic>
#include <thread>
#include <unordered_map>
#include <boost/asio.hpp>
#include <stdexcept>
#include <queue>
#include <functional>
#include <pthread.h>
#include <sched.h>
#include <unistd.h>
#include <memory>

namespace smo {

class Mind; // Forward declaration
class MarionetteThread;
class MindThread;

class ComponentThread
{
public:
	enum ThreadId
	{
		MRNTT = 0,
		DIRECTOR,
		SIMULATOR,
		SUBCONSCIOUS,
		BODY,
		WORLD,
		N_ITEMS
	};

protected:
	ComponentThread(ThreadId _id)
    :   id(_id), name(getThreadName(_id)),
        work(io_service)
	{}

public:
	virtual ~ComponentThread() = default;

	void cleanup(void);

	boost::asio::io_service& getIoService(void) { return io_service; }

	static const std::shared_ptr<ComponentThread> getSelf(void);
	static std::shared_ptr<MarionetteThread> getMrntt();

	typedef void (mainFn)(ComponentThread &self);

	// CPU management methods
	static int getAvailableCpuCount();

	typedef std::function<void()> mindShutdownIndOpCbFn;
	// Intentionally doesn't take a callback.
	void exceptionInd(const std::shared_ptr<ComponentThread> &faultyThread);
	// Intentionally doesn't take a callback.
	void userShutdownInd();

public:
	ThreadId id;
	std::string name;
	boost::asio::io_service io_service;
	boost::asio::io_service::work work;
	std::atomic<bool> keepLooping;

	static const std::string getThreadName(ThreadId id)
	{
		if (id < 0 || id >= ComponentThread::N_ITEMS)
		{
			throw std::runtime_error(std::string(__func__)
				+ ": Invalid thread ID");
		}

		// Use function-local static to ensure proper initialization order
		static const std::string threadNames[N_ITEMS] = {
			"mrntt",
			"director",
			"simulator",
			"subconscious",
			"body",
			"world"
		};

		return threadNames[id];
	}
};

class MarionetteThread
:	public std::enable_shared_from_this<MarionetteThread>,
	public ComponentThread
{
public:
	MarionetteThread()
	:	ComponentThread(MRNTT),
	thread(main, std::ref(*this))
	{
	}

	static void main(MarionetteThread& self);
	void initializeTls(void);

public:
	std::thread thread;
};

class MindThread
:	public std::enable_shared_from_this<MindThread>, public ComponentThread
{
public:
	enum class ThreadOp
	{
		START,
		PAUSE,
		RESUME,
		EXIT,
		JOLT,
		N_ITEMS
	};

	MindThread(ThreadId _id, Mind& parent)
	:   ComponentThread(_id),
	pinnedCpuId(-1),
	pause_work(pause_io_service),
	parent(parent),
	thread(main, std::ref(*this))
	{
	}

	static void main(MindThread& self);
	void initializeTls(void);

	Mind& getParent() const { return parent; }

	// Thread management methods
	typedef std::function<void()> threadLifetimeMgmtOpCbFn;
	void startThreadReq(threadLifetimeMgmtOpCbFn callback);
	void exitThreadReq(threadLifetimeMgmtOpCbFn callback);
	void pauseThreadReq(threadLifetimeMgmtOpCbFn callback);
	void resumeThreadReq(threadLifetimeMgmtOpCbFn callback);

	/**
	 * JOLTs this thread to begin processing after global initialization.
	 *
	 * JOLTing is the mechanism that allows threads to enter their main
	 * event loops and set up TLS vars after all global constructors have
	 * completed. This prevents race conditions during system startup.
	 */
	void joltThreadReq(threadLifetimeMgmtOpCbFn callback);

	// CPU management methods
	void pinToCpu(int cpuId);

public:
	int pinnedCpuId;
	boost::asio::io_service pause_io_service;
	boost::asio::io_service::work pause_work;
	Mind& parent;
	std::thread thread;

public:
	class ThreadLifetimeMgmtOp;
	class MindShutdownIndOp;
};

namespace mrntt {
extern std::shared_ptr<MarionetteThread> thread;
} // namespace mrntt

} // namespace smo

#endif // COMPONENT_THREAD_H
