#pragma once
#include "ThreadData.h"

// Note: The implementation is in ThreadData.cpp.
namespace os {

	/**
	 * OS thread.
	 *
	 * The Thread object represents a live instance of an OS thread. You can
	 * use the == operator to see if two threads are the same. The underlying OS
	 * thread will be kept running (even though it is not doing anything useful)
	 * as long as there are any Thread objects referring the thread.
	 *
	 * For windows:
	 * CoInitializeEx will be called for every thread created from this class. For
	 * other threads, please call initThread and cleanThread when before using the
	 * thread.
	 */
	class Thread {
	public:
		// Copy.
		Thread(const Thread &o) : data(o.data) {
			if (data)
				data->addRef();
		}

		// Destroy.
		~Thread() {
			if (data)
				data->release();
		}

		// Copy.
		Thread &operator =(const Thread &o) {
			if (this != &o) {
				if (data)
					data->release();
				data = o.data;
				if (data)
					data->addRef();
			}
			return *this;
		}

#ifdef USE_MOVE
		// Move.
		Thread(Thread &&o) : data(o.data) {
			o.data = null;
		}

		Thread &operator =(Thread &&o) {
			std::swap(data, o.data);
			return *this;
		}
#endif

		// Compare.
		inline bool operator ==(const Thread &o) const { return data == o.data; }
		inline bool operator !=(const Thread &o) const { return !(*this == o); }

		// Get the thread data.
		inline ThreadData *threadData() const { return data; }

		// Get an unique identifier for this thread.
		inline uintptr_t id() const { return (uintptr_t)data; }

		// Attach a os handle to this thread.
		void attach(Handle h) const;

		// Detach an os handle from this thread. This should be done before the handle is closed.
		// Note: Currently only applicable on POSIX systems.
		void detach(Handle h) const;

		// Get a list of UThreads running on this thread. Note that access to this list is not thread safe.
		const InlineSet<Stack> &stacks() const;

		// Get all idle threads. In contrast to 'stacks', acquires the appropriate lock before accessing 'stacks'.
		vector<UThread> idleUThreads();

		// Start a thread.
		static Thread spawn(ThreadGroup &group);
		static Thread spawn(const util::Fn<void, void> &start, ThreadGroup &group);

		// Start a thread with a specific 'ThreadWait' behaviour. Will take ownership of 'wait'.
		static Thread spawn(ThreadWait *wait, ThreadGroup &group);

		// Get the current thread.
		static Thread current();

		// Set the stack base for the first thread.
		static void setStackBase(void *base);

		// Invalid thread.
		static const Thread invalid;

		// Initialize a thread with any OS specific resources.
		static void initThread();
		static void cleanThread();

		friend wostream &operator <<(wostream &to, const Thread &o);
	private:
		friend class ThreadGroupData;

		// Create.
		explicit Thread(ThreadData *data);

		// Thread data.
		ThreadData *data;
	};

	wostream &operator <<(wostream &to, const Thread &o);
}
