/*
 * thread.c: A simple thread base class
 *
 * See the main source file 'vdr.c' for copyright information and
 * how to reach the author.
 *
 * $Id: thread.c 1.45 2005/08/14 11:15:42 kls Exp $
 */

#include "..\stdafx.h"

#include "thread.h"
#include <malloc.h>
#include <stdarg.h>
#include "..\asprintf.h"
#include <sys/timeb.h>

#define DELTA_EPOCH_IN_USEC  11644473600000000Ui64

static unsigned __int64 filetime_to_unix_epoch (const FILETIME *ft)
{
	unsigned __int64 res = (unsigned __int64) ft->dwHighDateTime << 32;

	res |= ft->dwLowDateTime;
	res /= 10;                   /* from 100 nano-sec periods to usec */
	res -= DELTA_EPOCH_IN_USEC;  /* from Win epoch to Unix epoch */
	return (res);
}

int gettimeofday (struct timeval *tv, void *tz)
{ 
	FILETIME  ft;
	unsigned __int64 tim;

	if (!tv) {
		errno = EINVAL;
		return (-1);
	}
	GetSystemTimeAsFileTime (&ft);
	tim = filetime_to_unix_epoch (&ft);
	tv->tv_sec  = (long) (tim / 1000000L);
	tv->tv_usec = (long) (tim % 1000000L);
	return (0); 
}

//static bool GetAbsTime(struct timespec *Abstime, int MillisecondsFromNow)
//{
//	struct timeval now;
//	if (gettimeofday(&now, NULL) == 0) {			// get current time
//		now.tv_usec += MillisecondsFromNow * 1000;  // add the timeout
//		while (now.tv_usec >= 1000000) {            // take care of an overflow
//			now.tv_sec++;
//			now.tv_usec -= 1000000;
//		}
//		Abstime->tv_sec = now.tv_sec;          // seconds
//		Abstime->tv_nsec = now.tv_usec * 1000; // nano seconds
//		return true;
//	}
//	return false;
//}

static bool GetAbsTime(struct timespec *Abstime, int MillisecondsFromNow)
{
	struct _timeb now;
	const DWORD NANOSEC_PER_MILLISEC = 1000000;

	_ftime(&now);
	now.millitm += MillisecondsFromNow;
	while (now.millitm >= 1000) {
		now.time++;
		now.millitm -= 1000;
	}
	Abstime->tv_sec = now.time;								// seconds
	Abstime->tv_nsec = now.millitm * NANOSEC_PER_MILLISEC;	// nano seconds*/

	return true;
}

// --- cCondWait -------------------------------------------------------------

cCondWait::cCondWait(void)
{
	signaled = false;
	pthread_mutex_init(&mutex, NULL);
	pthread_cond_init(&cond, NULL);
}

cCondWait::~cCondWait()
{
	pthread_cond_broadcast(&cond); // wake up any sleepers
	pthread_cond_destroy(&cond);
	pthread_mutex_destroy(&mutex);
}

void cCondWait::SleepMs(int TimeoutMs)
{
	cCondWait w;
	w.Wait(max(TimeoutMs, 3)); // making sure the time is >2ms to avoid a possible busy wait
}

bool cCondWait::Wait(int TimeoutMs)
{
	pthread_mutex_lock(&mutex);
	if (!signaled) {
		if (TimeoutMs) {
			struct timespec abstime;
			if (GetAbsTime(&abstime, TimeoutMs)) {
				while (!signaled) {
					if (pthread_cond_timedwait(&cond, &mutex, &abstime) == ETIMEDOUT)
						break;
				}
			}
		}
		else
			pthread_cond_wait(&cond, &mutex);
	}
	bool r = signaled;
	signaled = false;
	pthread_mutex_unlock(&mutex);
	return r;
}

// --- cCondVar --------------------------------------------------------------

cCondVar::cCondVar(void)
{
	pthread_cond_init(&cond, 0);
}

cCondVar::~cCondVar()
{
	pthread_cond_broadcast(&cond); // wake up any sleepers
	pthread_cond_destroy(&cond);
}

void cCondVar::Wait(cMutex &Mutex)
{
	if (Mutex.locked) {
		int locked = Mutex.locked;
		Mutex.locked = 0;	// have to clear the locked count here, as pthread_cond_wait
							// does an implicit unlock of the mutex
		pthread_cond_wait(&cond, &Mutex.mutex);
		Mutex.locked = locked;
	}
}

bool cCondVar::TimedWait(cMutex &Mutex, int TimeoutMs)
{
	bool r = true; // true = condition signaled, false = timeout

	if (Mutex.locked) {
		struct timespec abstime;
		if (GetAbsTime(&abstime, TimeoutMs)) {
			int locked = Mutex.locked;
			Mutex.locked = 0; // have to clear the locked count here, as pthread_cond_timedwait
							  // does an implicit unlock of the mutex.
			if (pthread_cond_timedwait(&cond, &Mutex.mutex, &abstime) == ETIMEDOUT)
				r = false;
			Mutex.locked = locked;
		}
	}
	return r;
}

void cCondVar::Broadcast(void)
{
	pthread_cond_broadcast(&cond);
}

// --- cMutex ----------------------------------------------------------------

cMutex::cMutex(void)
{
	locked = 0;
	pthread_mutexattr_t attr;
	pthread_mutexattr_init(&attr);
	pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ERRORCHECK_NP);
	pthread_mutex_init(&mutex, &attr);
}

cMutex::~cMutex()
{
	pthread_mutex_destroy(&mutex);
}

void cMutex::Lock(void)
{
	pthread_mutex_lock(&mutex);
	locked++;
}

void cMutex::Unlock(void)
{
	if (!--locked)
		pthread_mutex_unlock(&mutex);
}

// --- cThread ---------------------------------------------------------------

cThread::cThread(const char *Description)
{
	active = running = false;
	childTid.p = 0;
	childTid.x = 0;
	childThreadId = 0;
	description = NULL;
	if (Description)
		SetDescription("%s",Description);
}

cThread::~cThread()
{
	Cancel(); // just in case the derived class didn't call it
	free(description);
}

void cThread::SetDescription(const char *Description, ...)
{
	free(description);
	description = NULL;
	if (Description) {
		va_list ap;
		va_start(ap, Description);
		vasprintf(&description, Description, ap);
		va_end(ap);
	}
}

void *cThread::StartThread(cThread *Thread)
{
	if (Thread->description)
		printf("%s thread started (tid=%ld)\n", Thread->description, pthread_self());
	Thread->Action();
	if (Thread->description)
		printf("%s thread ended (tid=%ld)\n", Thread->description, pthread_self());
	Thread->running = false;
	Thread->active = false;
	return NULL;
}

bool cThread::Start(void)
{
	if (!active) {
		active = running = true;
		if (pthread_create(&childTid, NULL, (void *(*) (void *))&StartThread, (void *)this) == 0) {
			pthread_detach(childTid); // auto-reap
			pthread_setschedparam(childTid, SCHED_RR, 0);
		}
		else {
			active = running = false;
			return false;
		}
	}
	return true;
}

bool cThread::Active(void)
{
	if (active) {
		//
		// Single UNIX Spec v2 says:
		//
		// The pthread_kill() function is used to request
		// that a signal be delivered to the specified thread.
		//
		// As in kill(), if sig is zero, error checking is
		// performed but no signal is actually sent.
		//
		int err;
		if ((err = pthread_kill(childTid, 0)) != 0) {
			if (err != ESRCH)
				printf("ERROR (%s,%d)", __FILE__, __LINE__);
			childTid.p = 0;
			childTid.x = 0;
			active = running = false;
		}
		else
			return true;
	}
	return false;
}

void cThread::Cancel(int WaitSeconds)
{
	running = false;
	if (active) {
		if (WaitSeconds > 0) {
			for (time_t t0 = time(NULL) + WaitSeconds; time(NULL) < t0; ) {
				if (!Active())
					return;
				cCondWait::SleepMs(10);
			}
			printf("ERROR: %s thread %d won't end (waited %d seconds) - canceling it...", description ? description : "", childThreadId, WaitSeconds);
		}
		pthread_cancel(childTid);
		childTid.p = 0;
		childTid.x = 0;
		active = false;
	}
}

// --- cMutexLock ------------------------------------------------------------

cMutexLock::cMutexLock(cMutex *Mutex)
{
	mutex = NULL;
	locked = false;
	Lock(Mutex);
}

cMutexLock::~cMutexLock()
{
	if (mutex && locked)
		mutex->Unlock();
}

bool cMutexLock::Lock(cMutex *Mutex)
{
	if (Mutex && !mutex) {
		mutex = Mutex;
		Mutex->Lock();
		locked = true;
		return true;
	}
	return false;
}

// --- cThreadLock -----------------------------------------------------------

cThreadLock::cThreadLock(cThread *Thread)
{
	thread = NULL;
	locked = false;
	Lock(Thread);
}

cThreadLock::~cThreadLock()
{
	if (thread && locked)
		thread->Unlock();
}

bool cThreadLock::Lock(cThread *Thread)
{
	if (Thread && !thread) {
		thread = Thread;
		Thread->Lock();
		locked = true;
		return true;
	}
	return false;
}

// --- cPipe -----------------------------------------------------------------

// cPipe::Open() and cPipe::Close() are based on code originally received from
// Andreas Vitting <Andreas@huji.de>

cPipe::cPipe(void)
{
	pid = -1;
	f = NULL;
}

cPipe::~cPipe()
{
	Close();
}

bool cPipe::Open(const char *Command, const char *Mode)
{
	int fd[2];

	return false;
}

int cPipe::Close(void)
{
	int ret = -1;

	return ret;
}
