#pragma once

namespace Ooganizer
{	
	// Generic Thread Class!
	class Thread
	{
	public:
				 
		// 	Starts the thread
		//	This function starts the thread pointed by m_pThreadFunc with default attributes		 
		DWORD Start( void* arg = NULL )
		{
			m_ThreadCtx.m_pUserData = arg;
			m_ThreadCtx.m_hThread = CreateThread(NULL, 0, m_pThreadFunc, this, 0, &m_ThreadCtx.m_dwTID);
			m_ThreadCtx.m_dwExitCode = (DWORD)-1;
			return GetLastError();
		}
		
		// Stops the thread.		
		// This function stops the current thread		
		DWORD Stop ( bool bForceKill = false )
		{
			log(LOGGING_HIGH, L"Hidden Wnd Thread Stopped()");
			if ( m_ThreadCtx.m_hThread )
			{
				GetExitCodeThread(m_ThreadCtx.m_hThread, &m_ThreadCtx.m_dwExitCode);

				if ( m_ThreadCtx.m_dwExitCode == STILL_ACTIVE && bForceKill )
					TerminateThread(m_ThreadCtx.m_hThread, DWORD(-1));

				m_ThreadCtx.m_hThread = NULL;
			}
			return m_ThreadCtx.m_dwExitCode;
		}				
		
		// Returns Thread Exit Code
		DWORD GetExitCode() const 
		{ 
			if ( m_ThreadCtx.m_hThread )
				GetExitCodeThread(m_ThreadCtx.m_hThread, (LPDWORD)&m_ThreadCtx.m_dwExitCode);			
			return m_ThreadCtx.m_dwExitCode;
		}
		
		// Attaches a Thread Function		
		// Used primarily for porting but can serve in developing generic thread objects		
		void Attach( LPTHREAD_START_ROUTINE lpThreadFunc ){
			m_pThreadFunc = lpThreadFunc;
		}
		
		// Detaches the Attached Thread Function		
		// Detaches the Attached Thread Function, If any.		
		void  Detach( void ){
			m_pThreadFunc = Thread::EntryPoint; 
		}

		Thread(){ m_pThreadFunc = Thread::EntryPoint; /*Can call Detach Also*/ }
		//plug-in constructor
		Thread(PTHREAD_START_ROUTINE lpExternalRoutine){Attach(lpExternalRoutine);}
		~Thread(){ if(m_ThreadCtx.m_hThread) Stop(true);}

	protected:

		// Std Template: Override if you are sure of what you are doing
		static DWORD WINAPI EntryPoint( LPVOID pArg)
		{
			Thread *pParent = reinterpret_cast<Thread*>(pArg);
			pParent->ThreadCtor();
			pParent->Run( pParent->m_ThreadCtx.m_pUserData );
			pParent->ThreadDtor();
			return STILL_ACTIVE;
		}

		// Override this method with the body/code of your thread
		virtual DWORD Run( LPVOID /*arg */){return m_ThreadCtx.m_dwExitCode;}
		// override this function to provide your extra initialization
		virtual void ThreadCtor(){}
		// Override this function to provide your extra destruction
		virtual void ThreadDtor(){}
	
		////
		//	Thread Context Inner Class (UserData Pointer, Handle, Thread ID)
		////
		class CThreadContext
		{
		public:
			CThreadContext(){memset(this, 0, sizeof(this));}

		// Attributes
		public:
			HANDLE m_hThread;					//	The Thread Handle
			DWORD  m_dwTID;						//	The Thread ID
			LPVOID m_pUserData;					//	The user data pointer
			LPVOID m_pParent;					//	The this pointer of the parent CThread object
			DWORD  m_dwExitCode;				//	The Exit Code of the thread
		
		} m_ThreadCtx;							// The Thread Context Member
		LPTHREAD_START_ROUTINE	m_pThreadFunc;	// The Worker Thread Function Pointer
	};
}