/***************************************************************************
                          cthread.cpp  -  description
                             -------------------
    begin                : Sun Sep 30 2001
    copyright            : (C) 2001-2004 by Mathias Küster
    email                : mathen@users.berlios.de
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   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.                                   *
 *                                                                         *
 ***************************************************************************/

#include "cthread.h"

#include <stdio.h>
#include <time.h>
#include <errno.h>
#include <string.h>

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif

#include "ccallback.h"

#ifdef WIN32
#include <process.h>
#endif

CThread::CThread()
{
	_thread_callback_function = 0;

	iStop = 1;
	iRun  = 0;
	
#ifdef WIN32
	thread = 0;
#endif
}

CThread::~CThread()
{
	iStop = 1;

	if ( iRun == 1 )
	{
#ifdef WIN32
		WaitForSingleObject( thread, INFINITE );
#else
		pthread_join(thread,NULL);
#endif
	}

#ifdef WIN32
	if ( thread != 0 )
	{
		if ( CloseHandle(thread) )
		{
			thread = 0;
		}
		else
		{
			printf("CThread::~CThread: CloseHandle failed\n");
		}
	}
#endif

	delete _thread_callback_function;
	_thread_callback_function = 0;
}

/** */
int CThread::Start()
{
	if (iRun==1)
		return -1;

	if (iStop==0)
		return -1;

	iStop = 0;

#ifdef WIN32
	if ( thread != 0 )
	{
		if ( CloseHandle(thread) )
		{
			thread = 0;
		}
		else
		{
			return -1;
		}
	}
	
	/* HANDLE is void* and _beginthreadex() returns long unsigned int ... */
	thread = (HANDLE) _beginthreadex(
		NULL,		// default security
		0,		// default stacksize
		MainThread,	// function to call
		this,		// function parameter
		0,		// default creation flags - start running
		NULL		// there's both an ID and a handle apparently...
	);
	
	if ( thread == 0 )
	{
		return -1;
	}
	else
	{
		return 0;
	}
#else
	return pthread_create(&thread, 0, MainThread, this);
#endif
}

/** */
int CThread::Stop( bool bHard )
{
	if (iRun==0)
		return -1;

	if (iStop==1)
		return -1;

	iStop=1;

	if ( bHard )
	{
#ifdef WIN32
		WaitForSingleObject( thread, INFINITE );
		
		if ( CloseHandle(thread) )
		{
			thread = 0;
		}
		else
		{
			printf("CThread::MainThread: CloseHandle failed\n");
		}
#else
		pthread_join(thread,NULL);
#endif
	}

	return 0;
}

/** */
bool CThread::Join()
{
	if ( iRun == 0 )
	{
		return false;
	}
	else
	{
#ifdef WIN32
		return (WaitForSingleObject( thread, INFINITE ) != WAIT_FAILED);
#else
		return (pthread_join(thread,NULL) == 0);
#endif
	}
}

/** */
void CThread::SetThreadCallBackFunction( _CCallback0 * callback )
{
	Lock();

	delete _thread_callback_function;

	_thread_callback_function = callback;

	UnLock();
}

/** */
#ifdef WIN32
unsigned int __stdcall CThread::MainThread( void * object )
#else
void * CThread::MainThread( void * object )
#endif
{
#ifndef WIN32
	int oldState = 0;
#endif
	CThread * pThread = (CThread*)object;

	pThread->iRun = 1;

#ifndef WIN32
	pthread_setcancelstate(PTHREAD_CANCEL_DISABLE,&oldState);
#endif

	for(;;)
	{
		if ( pThread->iStop == 1 )
		{
			break;
		}
		else
		{
			if ( pThread->_thread_callback_function != 0 )
			{
				pThread->_thread_callback_function->notify();
			}
			else
			{
				pThread->Thread();
			}
		}
	}

#ifndef WIN32
	if ( (oldState = pthread_detach(pthread_self())) != 0 )
	{
		printf("CThread: pthread_detach with %d\n",oldState);
	}
#endif

	pThread->iRun = 0;

#ifdef WIN32
	/* it's supposed to be called automatically on return anyway */
	_endthreadex(0);
	
	return 0;
#else
	pthread_exit(0);
	
	/*
	 * pthread_exit() cannot return, maybe this is just to keep the compiler happy,
	 * return 0 probably works fine for non-WIN32.
	 */
	return (void*)1;
#endif
	
}

/**
  * Sleep     : milliseconds : 0,001
  * usleep    : microseconds : 0,000001
  * nanosleep : nanoseconds  : 0,000000001
  */
void CThread::NanoSleep( unsigned long millisec )
{
#ifndef WIN32
#ifdef HAVE_NANOSLEEP
	struct timespec req, rem;

	req.tv_sec  = 0;
	req.tv_nsec = millisec*1000*1000;
	rem.tv_sec  = 0;
	rem.tv_nsec = 0;

	while( (nanosleep(&req,&rem) == -1) )
	{
		if ( errno != EINTR )
			break;
		if ( rem.tv_nsec == 0 )
			break;
		req.tv_sec  = 0;
		req.tv_nsec = rem.tv_nsec;
		rem.tv_sec  = 0;
		rem.tv_nsec = 0;
	}
#elif HAVE_USLEEP
	usleep(millisec*1000);
#else
#error "nanosleep or usleep not supported"
#endif
#else
	Sleep(millisec);
#endif
}
