#include <errno.h>
#include <time.h>
#include <sys/select.h>
#include "threads.h"

Thread::Thread(void)
{
	m_started = false;
	m_detached = false;
}

Thread::~Thread(void)
{
	Stop();
}

bool Thread::Start(void* parameter)
{
	if (!m_started) {
		pthread_attr_t attributes;
		pthread_attr_init(&attributes);
		if (m_detached)
			pthread_attr_setdetachstate(&attributes, PTHREAD_CREATE_DETACHED);
		m_parameter = parameter;
		m_threadID = 0;
		if (pthread_create(&m_threadHandle, &attributes, ThreadFunction, this) == 0)
			m_started = true;
		pthread_attr_destroy(&attributes);
	}
	return m_started;
}

void Thread::Detach(void)
{
	if (m_started && !m_detached)
		pthread_detach(m_threadHandle);
	m_detached = true;
}

void* Thread::Wait(void)
{
	void* status = NULL;
	if (m_started && !m_detached) {
		pthread_join(m_threadHandle, &status);
		m_detached = true;
	}
	return status;
}

void Thread::Stop(void)
{
	if (m_started && !m_detached) {
		pthread_cancel(m_threadHandle);
		pthread_detach(m_threadHandle);
		m_detached = true;
	}
}

uint32_t Thread::GetThreadId(void)
{
	return m_threadID;
}

uint32_t Thread::GetCurrentThreadID(void)
{
	return 0;
}

void Thread::Sleep(double delay_in_seconds)
{
	struct timespec tv;
	tv.tv_sec  = (time_t) delay_in_seconds;
	tv.tv_nsec = (long) ((delay_in_seconds - tv.tv_sec) * 1E+9);
	while (1) {
		// Sleep for the time specified in tv.  If interrupt by a signal,
		// place the remaining time back into t.
		uint32_t error = nanosleep(&tv, &tv);
		if (error == 0)
			break;
		else if (errno == EINTR) // Interrupted by a signal, try again
			continue;
		else
			// TO DO:  Abort? return an error? What's a girl to do?
			break;
	}
	return;
}

void Thread::Sleep(uint32_t delay_in_milliseconds)
{
	struct timeval timeout;
	timeout.tv_sec  = (delay_in_milliseconds / 1000);
	timeout.tv_usec = (delay_in_milliseconds * 1000) % 1000000;
	select(0, (fd_set*) NULL, (fd_set*) NULL, (fd_set*) NULL, &timeout);
}

void* Thread::Run(void* parameter)
{
	return NULL;
}

void* ThreadFunction(void* object)
{
	Thread* thread = (Thread *) object;
	return thread->Run(thread->m_parameter);
}


