#ifndef __THREAD_H__
#define __THREAD_H__

#include <vector>
#include <process.h>

#pragma once

class CMutex
{
public:
	CMutex()
	{
		InitializeCriticalSection(&m_critSection);
	}
	~CMutex()
	{
		DeleteCriticalSection(&m_critSection);
	}

	class CLock
	{
	public:
		CLock( CMutex& mtx ) : m_pCritSection(&mtx.m_critSection)
		{
			EnterCriticalSection(m_pCritSection);
		}
		~CLock()
		{
			LeaveCriticalSection(m_pCritSection);
		}

	private:
		CLock( const CLock& );
		CLock& operator=( const CLock& );

		CRITICAL_SECTION * m_pCritSection;
	};

private:
	CMutex( const CMutex& );
	CMutex& operator=( const CMutex& );

	CRITICAL_SECTION m_critSection;
};

class CThread
{
public:
	CThread( void (*func)(void *), void * p )
	{
		m_handle = (HANDLE) _beginthread(func, 0, p);
	}

	~CThread()
	{
		WaitForSingleObject( m_handle, INFINITE );
	}

	static void SetName( const char * name )
	{
		typedef struct tagTHREADNAME_INFO
		{
			DWORD dwType; // must be 0x1000
			LPCSTR szName; // pointer to name (in user addr space)
			DWORD dwThreadID; // thread ID (-1=caller thread)
			DWORD dwFlags; // reserved for future use, must be zero
		} THREADNAME_INFO;

		THREADNAME_INFO info;
		{
			info.dwType = 0x1000;
			info.szName = name;
			info.dwThreadID = DWORD(-1);
			info.dwFlags = 0;
		}
		__try
		{
			RaiseException( 0x406D1388, 0, sizeof(info)/sizeof(DWORD), (ULONG_PTR*)&info );
		}
		__except (EXCEPTION_CONTINUE_EXECUTION)
		{
		}
	}

private:
	CThread( const CThread& );
	CThread& operator=( const CThread& );

	HANDLE m_handle;
};

// the "big global lock" for the network system
extern CMutex g_mutex;
extern int g_mutexLocks;

#define SCOPED_GLOBAL_LOCK CMutex::CLock globalLock(g_mutex); g_mutexLocks++

class CMultiWait
{
public:
	CMultiWait() {}
	typedef void (*Callback)(void *);

	void WaitOn( HANDLE hdl, Callback callback, void * pUser )
	{
		CallbackInfo ci;
		ci.callback = callback;
		ci.pUser = pUser;
		m_callbacks.push_back(ci);
		m_waits.push_back(hdl);
	}

	bool WaitFor( float tm, bool clearWaits )
	{
		bool slept = true;
		DWORD millis(DWORD(tm*1000.0f));
		if (millis)
		{
			if (m_waits.size())
			{
				DWORD i = WaitForMultipleObjects( DWORD(m_waits.size()), &m_waits[0], FALSE, millis );
				if (i >= WAIT_OBJECT_0 && i < WAIT_OBJECT_0+m_waits.size())
				{
					SCOPED_GLOBAL_LOCK;
					CallbackInfo ci = m_callbacks[i - WAIT_OBJECT_0];
					ci.callback( ci.pUser );
				}
				else if (i == WAIT_FAILED)
				{
					DWORD code = GetLastError();
					//NetWarning("Wait failed: %.8x", code);
				}
				slept = (i == WAIT_TIMEOUT);
				if (clearWaits)
				{
					m_waits.resize(0);
					m_callbacks.resize(0);
				}
			}
			else
			{
				Sleep( millis );
			}
		}
		return slept;
	}

private:
	struct CallbackInfo
	{
		Callback callback;
		void * pUser;
	};

	std::vector<HANDLE> m_waits;
	std::vector<CallbackInfo> m_callbacks;

	CMultiWait( const CMultiWait& );
	CMultiWait& operator=( const CMultiWait& );
};

#endif
