/*
 * Copyright 1993-2010 NVIDIA Corporation.  All rights reserved.
 *
 * NVIDIA Corporation and its licensors retain all intellectual property and 
 * proprietary rights in and to this software and related documentation. 
 * Any use, reproduction, disclosure, or distribution of this software 
 * and related documentation without an express license agreement from
 * NVIDIA Corporation is strictly prohibited.
 */

/* 
 * nvThread.cpp
 * Encapsulates all functionality for threading and events
 * Currently on Windows only
 */


#include <windows.h>
#include <assert.h>
#include "nvThread.h"

typedef struct InternalThreadDataRec {
    void *userThreadData;
    unsigned int (*userThreadFunc)(void *);
} InternalThreadData;


// Specify an infinite timeout.
#define WIN_THREAD_TIMEOUT INFINITE

struct nvThreadRec {
    InternalThreadData threadData;
    HANDLE threadHandle;
    DWORD  threadId;
};

static DWORD WINAPI WinThreadFunc(LPVOID *param)
{
    InternalThreadData *threadData = (InternalThreadData *)param;
    return (DWORD)(*threadData->userThreadFunc)(threadData->userThreadData);
}

nvThread nvCreateThread(unsigned (*threadFunc)(void *), void *threadData)
{
    nvThread thread = (nvThread)malloc(sizeof(struct nvThreadRec));

    if (!thread) {
        return NULL;
    }

    thread->threadId = -1;
    thread->threadData.userThreadFunc = threadFunc;
    thread->threadData.userThreadData = threadData;

    thread->threadHandle = CreateThread(NULL,
                                        0,
                                        (LPTHREAD_START_ROUTINE)WinThreadFunc,
                                        &thread->threadData,
                                        0,
                                        &thread->threadId);

    if (!thread->threadHandle) {
        free(thread);

        return NULL;
    }

    return thread;
}

int nvWaitForAndDestroyThread(nvThread thread, unsigned int *threadReturn)
{
    DWORD waitResult = WaitForSingleObject(thread->threadHandle,
                                           WIN_THREAD_TIMEOUT);
    DWORD exitCode;

    switch (waitResult) {
    case WAIT_OBJECT_0:
        break;

    case WAIT_TIMEOUT:
        return 0;

    case WAIT_FAILED:
    case WAIT_ABANDONED:
    default:
        assert(!"Failed to wait for thread to finish");
        return 0;
    }

    if (GetExitCodeThread(thread->threadHandle, &exitCode)) {
        if (exitCode == STILL_ACTIVE) {
            // Just waited for the thread.  It should be finished.
            assert(!"Thread not finished after waiting for completion");
            return 0;
        }
    } else {
        return 0;
    }

    CloseHandle(thread->threadHandle);

    if (threadReturn) {
        *threadReturn = exitCode;
    }

    free(thread);

    return 1;
}

struct nvEventRec {
    HANDLE hEvent;
};

//CReates an autoreset event object
nvEvent nvCreateEvent(void)
{
    nvEvent event = (nvEvent)malloc(sizeof(struct nvEventRec));

    if (!event) {
        return NULL;
    }

    event->hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);

    if (!event->hEvent) {
        free(event);
        return NULL;
    }

    return event;
}

void nvDestroyEvent(nvEvent event)
{
    assert(event->hEvent);

    CloseHandle(event->hEvent);
    event->hEvent = NULL;

    free(event);
}

int nvWaitForEvent(nvEvent event)
{
    assert(event->hEvent);

    if (WaitForSingleObject(event->hEvent, WIN_THREAD_TIMEOUT) != WAIT_OBJECT_0) {
        assert(!"WaitForSingleObjet failed");
        return 0;
    }

    return 1;
}

int nvSignalEvent(nvEvent event)
{
    assert(event->hEvent);

    if (!SetEvent(event->hEvent)) {
        assert(!"SetEvent failed");
        return 0;
    }

    return 1;
}

