// Common, groups WinWrap, Thread
// Copyright Alexander Liss

#ifndef __THREAD_H__
#define __THREAD_H__

#include "kernel.h"
#include "event.h"

// for a thread we need two objects 
// one of type Thread, another of type ThreadControl
// it is best to allocate them in 
// the global space, the heap or the stack of main()

	// Thread

class ThreadControl;

	// thread driver

// base class; 
// derived class supplies procedures and data (!) for a thread;
// if the same derived class is used to create different threads,
// then threads share procedures (!), but do not share data
class Thread 
{
friend class ThreadControl;

public:

Thread():loop_sleep(0),destruct(false){}
virtual ~Thread(){}

// wait on thread_event
void sleep_in_loop(unsigned milliseconds){loop_sleep=milliseconds;}
void wake_up(){thread_event.set();} // one step of a loop
void selfdestruct();

protected:

// in addition to the constructor
virtual int startup(){return 0;}

virtual int loop(bool& continue_loop)
	{continue_loop=false; return 0;}

// cleanup what startup() had allocated or opened
virtual int cleanup(){return 0;}

	unsigned loop_sleep;
	bool destruct;
	Event thread_event;

};


	// AsyncStep

class AsyncStep:public Thread
{
public:
AsyncStep(){}
int step(){wake_up();return 0;}
protected:
virtual int respond()=0;
private:
int loop(bool& continue_loop);
};

	// ThreadControl

class ThreadControl: public Kernel 
{
ThreadControl(const ThreadControl&);
ThreadControl& operator=(const ThreadControl&);
public:
 
	// tread return values
	enum
	{
		destructed=0,
		failed_startup=1,
		bad_loop=2,
		failed_cleanup=3
	};
	
ThreadControl():thread_id(0),thread_object(0){}
~ThreadControl(){}

// launch the thread, use flags if want some features of the thread at its creation
int launch( Thread& cThread,
			unsigned uInitFlag = 0, // launch running
			LPSECURITY_ATTRIBUTES lpSecurity = NULL, 
			unsigned uStackSize = 0); // same as caller's
  

BOOL set_priority( int nPriority);
DWORD suspend();
DWORD resume();
BOOL terminate( DWORD dwExitCode=0);

BOOL get_exit_code( DWORD *pdwExitCode);
int get_priority();
DWORD get_thread_id()const;

// queue an asynchronous procedure call to this thread
//VOID (APIENTRY *PAPCFUNC)( DWORD dwData );
#if(_WIN32_WINNT >= 0x0400)
DWORD queue_APC(PAPCFUNC pfnAPC,DWORD dwData);
#endif

protected:

	Thread * thread_object;
    unsigned thread_id;

	static unsigned _stdcall thread_procedure(void *pThread);
};


#endif