// Copyright 2003-2004 The Apache Software Foundation.
// 
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// 
//        http://www.apache.org/licenses/LICENSE-2.0
// 
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef __THREAD_H__
#define __THREAD_H__

class CThread
{
public:
		/*
		 *	Info: Default Constructor
		 */
		CThread()
		{ 
			m_pThreadFunc = CThread::EntryPoint; // Can call Detach() also.
		}

		/*
		 *	Info: Plug Constructor
		 *
		 *	Use this to migrate/port existing worker threads to objects immediately
		 *  Although you lose the benefits of ThreadCTOR and ThreadDTOR.
		 */
		CThread(LPTHREAD_START_ROUTINE lpExternalRoutine)
		{
			Attach(lpExternalRoutine);
		}

		/*
		 *	Info: Default Destructor
		 *
		 *	I think it is wise to destroy the thread even if it is running,
		 *  when the main thread reaches here.
		 */
		~CThread()
		{
			if ( m_ThreadCtx.m_hThread )
				Stop(true);
		}
		/*
		 *	Info: 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();
		}

		/*
		 *	Info: Stops the thread.
		 *	
		 *	This function stops the current thread. 
		 *	We can force kill a thread which results in a TerminateThread.
		 */
		DWORD Stop ( bool bForceKill = false )
		{
			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;
		}

		/*
		 *	Info: Starts the thread.
		 *	
		 *	This function starts the thread pointed by m_pThreadFunc with default attributes
		 */
		DWORD GetExitCode() const 
		{ 
			if ( m_ThreadCtx.m_hThread )
				GetExitCodeThread(m_ThreadCtx.m_hThread, (LPDWORD)&m_ThreadCtx.m_dwExitCode);
			return m_ThreadCtx.m_dwExitCode;
		}

		/*
		 *	Info: 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;
		}

		/*
		 *	Info: Detaches the Attached Thread Function
		 *	
		 *	Detaches the Attached Thread Function, If any.
		 *	by resetting the thread function pointer to EntryPoint1
		 */
		void  Detach( void ){
			m_pThreadFunc = CThread::EntryPoint; 
		}

protected:

		/*
		 *	Info: DONT override this method.
		 *	
		 *	This function is like a standard template. 
		 *	Override if you are sure of what you are doing.
		 */
		static DWORD WINAPI EntryPoint( LPVOID pArg)
		{
			CThread *pParent = reinterpret_cast<CThread*>(pArg);

			pParent->ThreadCtor();

			pParent->Run( pParent->m_ThreadCtx.m_pUserData );

			pParent->ThreadDtor();

			return STILL_ACTIVE;
		}
		
		/*
		 *	Info: Override this method.
		 *	
		 *	This function should contain the body/code of your thread.
		 *	Notice the signature is similar to that of any worker thread function
		 *  except for the calling convention.
		 */
		virtual DWORD Run( LPVOID /* arg */ )
		{ return m_ThreadCtx.m_dwExitCode; }

		/*
		 *	Info: Constructor-like function. 
		 *	
		 *	Will be called by EntryPoint before executing the thread body.
		 *  Override this function to provide your extra initialization.
		 *
		 *  NOTE: do not confuse it with the classes constructor
		 */
		virtual void ThreadCtor(){	}

		/*
		 *	Info: Destructor-like function. 
		 *	
		 *	Will be called by EntryPoint after executing the thread body.
		 *  Override this function to provide your extra destruction.
		 *
		 *  NOTE: do not confuse it with the classes constructor
		 */
		virtual void ThreadDtor(){	}
		
private:
		/*
		 *	Info: Thread Context Inner Class
		 *	
		 *	Every thread object needs to be associated with a set of values.
		 *	like UserData Pointer, Handle, Thread ID etc.
		 *  
		 *  NOTE: This class can be enhanced to varying functionalities
		 *		  eg.,
		 *				* Members to hold StackSize
		 *				* SECURITY_ATTRIBUTES member.
		 */
		class CThreadContext
		{
		public:
			CThreadContext(){
				memset(this, 0, sizeof(this));
			}

			/*
			 *	Attributes Section
			 */
		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
		};

		/*
		 *	Attributes Section
		 */
protected:
		/*
		 *	Info: Members of CThread
		 */
		CThreadContext			m_ThreadCtx;	//	The Thread Context member
		LPTHREAD_START_ROUTINE	m_pThreadFunc;	//	The Worker Thread Function Pointer
};

#endif //__THREAD_H__
