/*
 *
 * api.c
 *
 * Implementation of the Win32 APIs needed for winsock, on top of ntoskrnl
 *
 */

#include "precomp.h"
#include "dm.h"

void SetLastError(DWORD dwErrCode)
{
    DmGetCurrentDmtd()->LastError = dwErrCode;
}

DWORD GetLastError(void)
{
    return DmGetCurrentDmtd()->LastError;
}

void Sleep(DWORD dw)
{
    SleepEx(dw, FALSE);
}

DWORD SleepEx(DWORD dw, BOOL f)
{
    LARGE_INTEGER li;
    NTSTATUS st;

    if(dw == -1)
        {
        li.LowPart = 0;
        li.HighPart = 0x80000000;
        }
    else
        {
        li.QuadPart = UInt32x32To64(dw, 10000);
        li.QuadPart *= -1;
        }
    do
        st = KeDelayExecutionThread(UserMode, (BOOLEAN)f, &li);
    while(f && STATUS_ALERTED == st);
    return st == STATUS_USER_APC ? WAIT_IO_COMPLETION : 0;
}

HANDLE CreateEventA(LPSECURITY_ATTRIBUTES psa, BOOL fManualReset,
    BOOL fInitState, LPCSTR lpwzName)
{
    NTSTATUS st;
    HANDLE h;
    OBJECT_ATTRIBUTES oa;

    ASSERT(NULL == lpwzName);

    InitializeObjectAttributes(&oa, NULL, 0, NULL, NULL);
    st = NtCreateEvent(&h, EVENT_ALL_ACCESS, &oa, fManualReset ?
        NotificationEvent : SynchronizationEvent, (BOOLEAN)fInitState);
    if(NT_SUCCESS(st))
        SetLastError(0);
    else
        {
        SetLastError(RtlNtStatusToDosError(st));
        h = NULL;
        }
    return h;
}

void _ThreadStartup(PKSTART_ROUTINE StartRoutine, PVOID StartContext)
{
    DmGetCurrentDmtd()->DebugFlags |= DMFLAG_DEBUGTHREAD;
    StartRoutine(StartContext);
    PsTerminateSystemThread(STATUS_SUCCESS);
}

HANDLE CreateThread(LPSECURITY_ATTRIBUTES psa, DWORD dwStack,
    LPTHREAD_START_ROUTINE lpfn, LPVOID lpv, DWORD dwFlags, LPDWORD lpdwId)
{
    NTSTATUS st;
    HANDLE h;
    CLIENT_ID clid;

    st = PsCreateSystemThreadEx(&h, 0, dwStack, (PHANDLE)lpdwId,
		(PKSTART_ROUTINE)lpfn, lpv,
		(BOOLEAN)(dwFlags & CREATE_SUSPENDED ? TRUE : FALSE), _ThreadStartup);
    
    if(!NT_SUCCESS(st))
        {
        SetLastError(RtlNtStatusToDosError(st));
        return NULL;
        }
    
    return h;
}

BOOL CloseHandle(HANDLE h)
{
    NTSTATUS st = NtClose(h);
    if(NT_SUCCESS(st))
        return TRUE;
    SetLastError(RtlNtStatusToDosError(st));
    return FALSE;
}

BOOL SetEvent(HANDLE h)
{
    NTSTATUS st = NtSetEvent(h, NULL);
    if(NT_SUCCESS(st))
        return TRUE;
    SetLastError(RtlNtStatusToDosError(st));
    return FALSE;
}

BOOL ResetEvent(HANDLE h)
{
    NTSTATUS st = NtClearEvent(h);
    if(NT_SUCCESS(st))
        return TRUE;
    SetLastError(RtlNtStatusToDosError(st));
    return FALSE;
}

#if 0
DWORD WaitForMultipleObjectsEx(DWORD nCount, const HANDLE *lph, BOOL fWaitAll,
    DWORD dwTimeOut, BOOL fAlertable)
{
    NTSTATUS st;
    LARGE_INTEGER li;
    PLARGE_INTEGER pli;
    HANDLE rgh[MAXIMUM_WAIT_OBJECTS];

    if(nCount > MAXIMUM_WAIT_OBJECTS)
    {
        SetLastError(RtlNtStatusToDosError(STATUS_INVALID_PARAMETER));
        return (DWORD)-1;
    }

    RtlCopyMemory(rgh, lph, nCount * sizeof(HANDLE));
    if(dwTimeOut == -1)
        pli = NULL;
    else
        {
        pli = &li;
        li.QuadPart = UInt32x32To64(dwTimeOut, 10000);
        li.QuadPart *= -1;
        }
    do
        st = NtWaitForMultipleObjectsEx((CHAR)nCount, rgh,
            fWaitAll ? WaitAll : WaitAny, UserMode, (BOOLEAN)fAlertable, pli);
    while(st == STATUS_ALERTED && fAlertable);
    if(!NT_SUCCESS(st)) {
        SetLastError(RtlNtStatusToDosError(st));
        st = (DWORD)-1;
    }
    return st;
}
#endif

DWORD WaitForSingleObject(HANDLE h, DWORD dwTimeOut)
{
    NTSTATUS st;
    LARGE_INTEGER li;
    PLARGE_INTEGER pli;

    if(dwTimeOut == -1)
        pli = NULL;
    else
        {
        pli = &li;
        li.QuadPart = UInt32x32To64(dwTimeOut, 10000);
        li.QuadPart *= -1;
        }
    st = NtWaitForSingleObjectEx(h, UserMode, FALSE, pli);
    if(!NT_SUCCESS(st)) {
        SetLastError(RtlNtStatusToDosError(st));
        st = (DWORD)-1;
    }
    return st;
}
