/*
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

#ifdef _MSC_VER
#pragma warning (disable: 4786)
#endif

#define STRICT
#include <Windows.h>
#include <Windowsx.h>
#include <string>
#include <map>
#include <list>
#include <vector>
#include <boost/shared_ptr.hpp>
#include <boost/thread/thread.hpp>
#include <boost/thread/mutex.hpp>
#include "WinOnly.h"
#include "WinBe.h"
#include "NativeThread.h"


namespace winBe {


// Static variables
NativeThreadLookup NativeThread::threadLookup; 
boost::mutex NativeThread::lookupMutex;


// Global variables
NativeThread mainThread;


// default constructor
NativeThread::NativeThread () :
exitValue (0)
{
	initializer.entryFunction = 0;
	initializer.entryArg = 0;
	initializer.owner = this;

	boostThread = new boost::thread ();
	threadId = ::GetCurrentThreadId ();
}


// constructor
NativeThread::NativeThread (thread_func function_name, const std::string& thread_name, int32 priority, void* arg) :
name (thread_name),
boostThread (0),
exitValue (0),
threadId (0)
{
	initializer.entryFunction = function_name;
	initializer.entryArg = arg;
	initializer.owner = this;

	winBe::debugMessage ("locking threadLookup");

	boost::mutex::scoped_lock scoped_lock (lookupMutex);
	threadLookup.insert (std::make_pair (name, this));

	winBe::debugMessage ("unlocking threadLookup");
}


// destructor
NativeThread::~NativeThread ()
{
	//debugMessage ("destructing nativethread");
	if (boostThread) {
		boost::mutex::scoped_lock scoped_lock (lookupMutex);
		NativeThreadLookup::iterator i = threadLookup.find (name);
		if (i != threadLookup.end ())
			threadLookup.erase (i);
		delete boostThread;
	}
}


// getThreadId
long NativeThread::getThreadId ()
{
	return threadId;
}


// resume
bool NativeThread::resume ()
{
	bool resumed = true;

	try {
		boostThread = new boost::thread (initializer);
	} catch (boost::thread_resource_error error) {
		resumed = false;
	}

	return resumed;
}


// wait_for_termination
int NativeThread::wait_for_termination ()
{
	boostThread->join ();
	return exitValue;
}


// find
NativeThread* NativeThread::find (std::string name)
{
	winBe::debugMessage ("locking threadLookup");

	boost::mutex::scoped_lock scoped_lock (lookupMutex);

	NativeThreadLookup::iterator result = threadLookup.find (name);

	winBe::debugMessage ("unlocking threadLookup");

	return result == threadLookup.end () ? 0 : (*result).second;
}


// findCurrentThread
NativeThread* NativeThread::findCurrentThread ()
{
	NativeThread* currentThread = 0;
	long threadId = ::GetCurrentThreadId ();

	if (threadId == mainThread.getThreadId ())
		currentThread = &mainThread;
	else
		for (NativeThreadLookup::iterator i = threadLookup.begin (); i != threadLookup.end (); ++i)
			if ((*i).second->getThreadId () == threadId) {
				currentThread = (*i).second;
				break;
			}

	return currentThread;
}


// postMessage
bool NativeThread::postMessage (long message)
{
	bool posted = false;

	for (int i = 0; i < 10; i++) {
		posted = ::PostThreadMessage (threadId, WM_BMESSAGE, message, 0) ? true : false;
		if (posted)
			break;
		::Sleep (10);
	}

	return posted;
}


// postQuitMessage
void NativeThread::postQuitMessage ()
{
	for (int i = 0; i < 10; i++) {
		bool posted = ::PostThreadMessage (threadId, WM_QUIT, 0, 0) ? true : false;
		if (posted)
			break;
		::Sleep (10);
	}
}


// operator ()
void NativeThread::NativeThreadInitializer::operator () ()
{
	debugMessage ("thread started");
	owner->threadId = ::GetCurrentThreadId ();
	owner->exitValue = (*entryFunction)(entryArg);
	//delete owner;
}


} // namespace winBe
