#pragma once

#include "BeefySysLib/Common.h"
#include "BeefySysLib/util/CritSect.h"
#include "BfObjects.h"
#include "ThreadLocalStorage.h"

#ifdef BF_HAS_TLS_DECLSPEC
#define BF_THREAD_TLS
#endif

#pragma push_macro("MemoryBarrier")
#undef MemoryBarrier

class BfDbgInternalThread;

namespace bf
{
	namespace System
	{
		namespace Threading
		{			
			struct ThreadHandle
			{

			};

			class Thread : public Object
			{
			private:
				Thread();

			public:
				BF_DECLARE_CLASS(Thread, Object);

#ifdef BF_THREAD_TLS
				static BF_TLS_DECLSPEC Thread* sCurrentThread;
#endif
			private:
				BfInternalThread* SetupInternalThread();

				BFRT_EXPORT void ManualThreadInit();								
				BFRT_EXPORT int GetPriorityNative();
				BFRT_EXPORT void SetPriorityNative(int priority);
				BFRT_EXPORT void SetJoinOnDelete(bool joinOnDelete);
				BFRT_EXPORT bool GetIsAlive();
				BFRT_EXPORT bool GetIsThreadPoolThread();
				BFRT_EXPORT bool JoinInternal(int millisecondsTimeout);
				BFRT_EXPORT static void SleepInternal(int millisecondsTimeout);
				BFRT_EXPORT static void SpinWaitInternal(int iterations);
				BFRT_EXPORT static bool YieldInternal();
				BFRT_EXPORT static Thread* GetCurrentThreadNative();
				BFRT_EXPORT unsigned long GetProcessDefaultStackSize();								
				BFRT_EXPORT void StartInternal();				
				BFRT_EXPORT void ThreadStarted();
				BFRT_EXPORT void SetStackStart(void* ptr);
				BFRT_EXPORT void InternalFinalize();				
				BFRT_EXPORT bool IsBackgroundNative();
				BFRT_EXPORT void SetBackgroundNative(bool isBackground);
				BFRT_EXPORT int GetThreadStateNative();
				BFRT_EXPORT void InformThreadNameChange(String* name);				
				BFRT_EXPORT intptr GetThreadId();

				BFRT_EXPORT void Dbg_CreateInternal();

			public:
				BFRT_EXPORT void Suspend();
				BFRT_EXPORT void Resume();
				BFRT_EXPORT void Abort();

				BFRT_EXPORT static void RequestExitNotify();
				BFRT_EXPORT static void MemoryBarrier();

				static Thread* Alloc()
				{
					return BFRTCALLBACKS.Thread_Alloc();
				}

				BfInternalThread* GetInternalThread()
				{
					return BFRTCALLBACKS.Thread_GetInternalThread(this);
				}

				BfDbgInternalThread* Dbg_GetInternalThread()
				{
					return (BfDbgInternalThread*)BFRTCALLBACKS.Thread_GetInternalThread(this);
				}

				void SetInternalThread(BfInternalThread* internalThread)
				{
					BFRTCALLBACKS.Thread_SetInternalThread(this, internalThread);
				}

				int GetMaxStackSize()
				{
					return BFRTCALLBACKS.Thread_GetMaxStackSize(this);
				}
			};
		}
	} // Namespace System
}

class BfInternalThread
{
public:
	bool mIsSuspended;
	intptr mStackStart;	
	bf::System::Threading::Thread* mThread;	
	bool mRunning;
	bool mDone;
	bool mStarted;
	bool mJoinOnDelete;
	bool mIsManualInit;
	bool mIsBackground;
	BfpThread* mThreadHandle;
	intptr mThreadId;	
	Beefy::CritSect mCritSect;
	Beefy::SyncEvent mStartedEvent;

	BfInternalThread()
	{
		mThread = NULL;
		mThreadHandle = 0;
		mStarted = false;
		mRunning = false;
		mDone = false;
		mIsSuspended = false;
		mJoinOnDelete = true;
		mIsManualInit = false;
		mIsBackground = false;
		mStackStart = 0;
		mThreadId = 0;
	}

	virtual ~BfInternalThread()
	{
		if (mThreadHandle != 0)
		{
			BfpThread_Release(mThreadHandle);
		}
	}

	virtual void ManualThreadInit(bf::System::Threading::Thread* thread)
	{
		bf::System::Threading::Thread* newThread = thread;

		mIsManualInit = true;
		mStarted = true;
		mStartedEvent.Set(true);
		mThread = newThread;
		newThread->SetInternalThread(this);
		mThreadId = BfpThread_GetCurrentId();
		mThreadHandle = BfpThread_GetCurrent();
		mStackStart = ((intptr)&newThread + 0xFFF) & ~(intptr)0xFFF;
		ThreadStarted();
	}

	virtual void ThreadStarted()
	{		
		mRunning = true;
	}

	virtual void ThreadStopped()
	{
        //printf("TheadStopped\n");
		mRunning = false;
	}

	static void WaitForAllDone();
};

#pragma pop_macro("MemoryBarrier")
