#pragma once
#include "UThreadData.h"

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

	/**
	 * This is a handle to a specific UThread. Currently, not many operations
	 * is supported on another UThread than the current. Therefore, the backing
	 * UThread is not kept after the UThread has terminated.
	 */
	class UThread {
	public:

		/**
		 * Description of how to handle the result from a function call on another
		 * thread. When the function completes, 'done' is called with its result.
		 * If an exception is thrown, 'error' is called with the exception instead.
		 * 'data' may be used to provide custom data to the callbacks.
		 */
		template <class R, class Param>
		struct Result {
			Param *data;
			void (*done)(Param *, R);
			void (*error)(Param *, const Exception &);
		};

		// Specific for void.
		template <class Param>
		struct Result<void, Param> {
			Param *data;
			void (*done)(Param *);
			void (*error)(Param *, const Exception &);
		};

		// Copy.
		UThread(const UThread &o) : data(o.data) {
			if (data)
				data->addRef();
		}

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

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

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

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

		// Same UThread?
		inline bool operator ==(const UThread &o) const { return data == o.data; }
		inline bool operator !=(const UThread &o) const { return data != o.data; }

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

		// Yield to another UThread. Returns sometime in the future. Returns 'true' if other
		// threads were run between the call and the return.
		static bool leave();

		// See if there are any UThreads that are currently sleeping, and may wake in the future.
		// This can be used together with "leave" to detect if we need to keep "polling" for
		// sleeping threads from custom thread-wait logic in certain cases (e.g. when it cannot use
		// the standard "wait" function).
		static bool anySleeping();

		// Yield for a specific amount of time.
		static void sleep(nat ms);

		// Any more UThreads to run here?
		static bool any();

		// Get the currently running UThread.
		static UThread current();

		// Get the thread data. Mainly for internal use.
		inline UThreadData *threadData() { return data; }

		// Value representing no thread.
		static const UThread invalid;


		/**
		 * Low-level spawn functions. See 'spawn' below for explanations.
		 */

		// Spawn a 'void' function.
		static UThread spawnRaw(const void *fn, bool memberFn, void *firstParam,
								const FnCallRaw &call, const Thread *on = null);

		// Spawn a function, capturing the result in a future.
		static UThread spawnRaw(const void *fn, bool memberFn, void *firstParam,
								const FnCallRaw &call, FutureBase &result,
								void *target, const Thread *on = null);


		/**
		 * Spawn a new UThread on the currently running thread, or another thread. There are a few
		 * variants of spawn here, all of them take some kind of function pointer, optionally with
		 * parameters to run, followed by a reference to the OS thread (Thread *) to run on. If this
		 * is left out, or set to null, the thread of the caller is used.
		 *
		 * All of these return as soon as the new UThread is set up, they do not pre-empt the
		 * currently running thread. Note, however, that any parameters are copied before the call
		 * returns, so there is no need to worry about variables used as parameters going out of
		 * scope.
		 */

		// Spawn using a Fn<void, void>.
		static UThread spawn(const util::Fn<void, void> &fn, const Thread *on = null);

		// Spawn using a FnCall object.
		template <int P>
		static UThread spawn(const void *fn, bool memberFn, const FnCall<void, P> &call, const Thread *on = null) {
			return spawnRaw(fn, memberFn, null, call, on);
		}

		// Spawn using a FnCall object, providing the result in a Future<T>.
		template <class R, int P>
		static UThread spawn(const void *fn, bool memberFn, const FnCall<R, P> &call,
							Future<R> &result, const Thread *on = null) {
			return spawnRaw(fn, memberFn, null, call, result.impl(), result.data(), on);
		}

		// Execute a detour function on this uthread. Assumes that this thread is currently not
		// running and belongs to the same thread as the caller of this function. The detour will be
		// executed synchronously, ie. the current thread will be suspended until the detour is
		// completed.
		// This function is intended to provide stack traces for suspended threads. As such, the
		// stack during the detour will look like the suspended thread called the function in the
		// detour.
		// Returns 'false' if unable to execute the detour, usually since the thread is currently
		// being executed.
		bool detour(const util::Fn<void, void> &fn);

	private:
		friend class UThreadState;

		// Create
		UThread(UThreadData *data);

		// Referenced data.
		UThreadData *data;

		// Insert an UThread.
		static UThread insert(UThreadData *data, ThreadData *on);
	};

}


// Don't ask...
#include "Sync.h"
