// DWBS ------------------------------------------------------------------ DWBS
//
// PLATFORM		: ALL
// PRODUCT		: BIT_DEMON
// VISIBILITY	: PRIVATE
//
// DWBS ------------------------------------------------------------------ DWBS

#include <bdCore/bdThread/bdThread.h>
#include <bdPlatform/bdPlatformThread/bdPlatformThread.h>
#include <bdCore/bdLog/bdLog.h>


bdThread::bdThread(bdRunnable *runnable, bdInt priority, bdUInt stackSize)
: m_runnable(BD_NULL),
  m_isRunning(false)
#if (defined BD_PLATFORM_PS2 || defined BD_PLATFORM_PSP)
 ,m_stackPointer(BD_NULL)
 ,m_joiningThread(-1)
#endif
{
	m_runnable = runnable;
	m_threadArgs.m_runnable = m_runnable;
	m_threadArgs.m_thread = this;
#if (defined BD_PLATFORM_PS2 || defined BD_PLATFORM_PSP)
	if (!m_stackPointer)
	{
		m_stackPointer = bdMemory::alignedAllocate(stackSize, 16);
	}
	BD_ASSERT(bdPlatformThread::createThread((bdThreadProc)threadProc, &m_threadArgs, m_handle, priority, stackSize, m_stackPointer), "thread creation failed");
#else
	BD_ASSERT(bdPlatformThread::createThread((bdThreadProc)threadProc, &m_threadArgs, m_handle, priority, stackSize), "thread creation failed");
#endif // BD_PLATFORM_PS2
}

bdThread::~bdThread()
{
	if(m_threadArgs.m_args != BD_NULL)
	{
		bdMemory::deallocate(m_threadArgs.m_args);
	}

#if (defined BD_PLATFORM_PS2 || defined BD_PLATFORM_PSP)
	if (m_stackPointer)
	{
		bdMemory::alignedDeallocate(m_stackPointer);
	}
#endif
	bdPlatformThread::deleteThread(m_handle);
	m_runnable = BD_NULL;
}

bdBool bdThread::start(const void *args, bdUInt size)
{
	if (m_runnable == BD_NULL || m_isRunning)
	{
		return false;
	}
	if(args != NULL)
	{
		// copy user data (stack allocated data may 
		// be de-allocated while in use by the thread)
		m_threadArgs.m_args = bdMemory::allocate(size);	
		bdMemcpy(m_threadArgs.m_args, args, size); 
	} 
	else 
	{
		m_threadArgs.m_args = BD_NULL;
	}

	m_isRunning = true;
	m_runnable->start();
#if defined BD_PLATFORM_PS3
	m_threadArgs.m_startSignal.signal();
	return true;
#else
	return bdPlatformThread::startThread(m_handle, &m_threadArgs, sizeof(m_threadArgs));
#endif
}

void bdThread::stop()
{
	m_runnable->stop();
}

void bdThread::join()
{
#if (defined BD_PLATFORM_PS2 )
	if (m_isRunning)
	{
		m_joiningThread = bdPlatformThread::getThreadHandle();
		SleepThread();

	}
#else
	bdPlatformThread::joinThread(m_handle);
#endif
	if (m_threadArgs.m_args)
	{
		bdMemory::deallocate(m_threadArgs.m_args);
		m_threadArgs.m_args = BD_NULL;
	}
}

void bdThread::cleanup()
{
	if (m_threadArgs.m_args)
	{
		bdMemory::deallocate(m_threadArgs.m_args);
		m_threadArgs.m_args = BD_NULL;
	}
	delete this;
}

void bdThread::setPriority(bdInt priority)
{
	bdPlatformThread::setPriority(m_handle, priority);
}

bdInt bdThread::getPriority()
{
	return bdPlatformThread::getPriority(m_handle);
}

bdBool bdThread::isRunning() const
{
	return m_isRunning;
}

bdRunnable *bdThread::getRunnable()
{
	return m_runnable;
}

bdThreadHandle bdThread::getThreadHandle()
{
	return m_handle;
}

void bdThread::wakeupJoiningThread()
{
#if (defined BD_PLATFORM_PS2 )
	if (m_joiningThread != -1)
	{
		WakeupThread(m_joiningThread);
	}
#endif
	m_isRunning = false;
}


#if !defined BD_PLATFORM_PSP

bdInt BD_CALL bdThread::threadProc(bdThreadArgs *args)
{
	bdUInt ret ;
	bdRunnable *runnable = args->m_runnable;
#if defined BD_PLATFORM_PS3
	// Wait for startThread signal
	args->m_startSignal.wait();
#endif
	ret = runnable->run(args->m_args);
	args->m_thread->wakeupJoiningThread();
#if defined BD_PLATFORM_PS3
	sys_ppu_thread_exit(ret);
#endif
	return ret;
}

#else

bdInt BD_CALL bdThread::threadProc(bdUInt argSize, bdThreadArgs *args)
{
	bdUInt ret ;
	//bdThreadArgs *pargs = *args;
	bdRunnable *runnable = args->m_runnable;
	ret = runnable->run(args->m_args);
	args->m_thread->wakeupJoiningThread();
	return ret;
}

#endif 
