/*
 * tclWinCE.c --
 *
 *	This file contains emulation code for Tcl on Windows CE.
 *	It is adapted from Rainer Keuchel's celib-based port.
 *	Original celib Time-stamp: <23/12/01 16:44:22 keuchel@netwave.de>
 *
 * Copyright (c) 2003 ActiveState Corporation
 *
 * See the file "license.terms" for information on usage and redistribution
 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
 *
 * RCS: @(#) $Id: Exp $
 */

#include "tclWinInt.h"

DWORD	async_func(LPDWORD param);

#undef isatty

int
isatty(int fd)
{
#if 1
    return xceisatty(fd);
#else
#include <conioctl.h>
    DWORD magic, numbytes;
    if (!DeviceIoControl((HANDLE)fh, IOCTL_CONSOLE_VALIDATEHANDLE, NULL, 0,
		(void*) &magic, sizeof(DWORD), &numbytes, NULL)) {
	return 0;
    }
    return (magic == ':NOC');
#endif
}

BOOL
XXCEGetFileAttributesExA(LPCTSTR lpFileName, 
	GET_FILEEX_INFO_LEVELS fInfoLevelId, 
	LPVOID lpFileInformation)
{
    wchar_t wfname[MAX_PATH];

    MultiByteToWideChar(CP_ACP, 0, lpFileName, -1, wfname, COUNTOF(wfname));

    return GetFileAttributesExW(wfname, fInfoLevelId, lpFileInformation);
}

BOOL
XXCEGetComputerNameW(WCHAR *wbuf, LPDWORD lpdwSize)
{
    char buf[MAX_PATH];

    if (XCEGetEnvironmentVariableFromRegA("COMPUTERNAME", buf, sizeof(buf))
	    == 0) {
	return FALSE;
    }

    if (wbuf != NULL) {
	MultiByteToWideChar(CP_ACP, 0, buf, -1, wbuf, *lpdwSize);
    }

    *lpdwSize = wcslen(wbuf);

    return TRUE;
}

DWORD
XXCEGetFullPathNameW(LPCTSTR lpFileName,
	DWORD nBufferLength, WCHAR *lpBuffer, LPTSTR *lpFilePart)
{
    char tmp[MAX_PATH];
    char tmp2[MAX_PATH];
    int len;

    WideCharToMultiByte(CP_ACP, 0, lpFileName, -1, tmp, sizeof(tmp),
	    NULL, NULL);
    len = XCEGetFullPathNameA(tmp, sizeof(tmp2), tmp2, NULL);
    if (len) {
	MultiByteToWideChar(CP_ACP, 0, tmp2, -1, lpBuffer, nBufferLength);
    }
    return len;
}

DWORD
XXCEGetShortPathNameW(LPCTSTR lpszLongPath, WCHAR *lpszShortPath,
	DWORD cchBuffer)
{
    char tmp[MAX_PATH];
    char tmp2[MAX_PATH];
    int len;

    WideCharToMultiByte(CP_ACP, 0, lpszLongPath, -1, tmp, sizeof(tmp),
	    NULL, NULL);
    len = XCEGetShortPathNameA(tmp, tmp2, sizeof(tmp2));
    if (len) {
	MultiByteToWideChar(CP_ACP, 0, tmp2, -1, lpszShortPath, cchBuffer);
    }
    return len;
}

BOOL
XXCEGetVolumeInformationW(
    LPCTSTR  lpRootPathName,	
    WCHAR *lpVolumeNameBuffer,
    DWORD  nVolumeNameSize,	
    LPDWORD  lpVolumeSerialNumber,
    LPDWORD  lpMaximumComponentLength,
    LPDWORD  lpFileSystemFlags,	
    WCHAR *lpFileSystemNameBuffer,
    DWORD  nFileSystemNameSize)
{
    /*
     * FIX: HACK!  This assumes we are passed a local path.
     */
    if (lpMaximumComponentLength) {
	*lpMaximumComponentLength = MAX_PATH;
    }
    if (lpFileSystemNameBuffer) {
	memset(lpFileSystemNameBuffer, 0, nFileSystemNameSize);
	xcesnprintf(lpFileSystemNameBuffer, nFileSystemNameSize, L"MEM");
    }
    return TRUE;
}

//////////////////////////////////////////////////////////////////////
// WSAASyncSelect emulation - needed for xmaxima...

HANDLE hAsyncThread;

#define MAXASYNCSOCKS 10

typedef struct async_entry
{
    SOCKET s;
    HWND hwnd;
    UINT msg;
    LONG events;
    UINT posted;
} ASYNC_DATA;

ASYNC_DATA asynctab[MAXASYNCSOCKS];

int
StartAsyncThread()
{
    DWORD dwThreadId;

    hAsyncThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) async_func,
	    NULL, 0, &dwThreadId);

    return 0;
}

int
WSAAsyncSelect (SOCKET skt, HWND hwnd, unsigned int msg, long events)
{
    int i;
    int one = 1;
    int zero = 0;

    XCETrace("WSAAsyncSelect(%d, %x, %x, %x)",
	    skt, hwnd, msg, events);

    // seems to have problems...
    if(hAsyncThread == NULL)
	StartAsyncThread();

    // update old
    for (i = 0; i < MAXASYNCSOCKS; i++) {
	ASYNC_DATA *a = &asynctab[i];

	if(a->s == skt) {
	    a->s = skt; a->hwnd = hwnd; a->msg = msg; a->events = events;

	    if (events == 0) {
		// Seems to be wrong!?!!!!
		// ioctlsocket(a->s, FIONBIO, &zero);
	    } else {
		ioctlsocket(a->s, FIONBIO, &one);
	    }

	    return 0;
	}
    }

    // new
    for (i = 0; i < MAXASYNCSOCKS; i++) {
	ASYNC_DATA *a = &asynctab[i];

	if (a->hwnd == 0) {
	    a->s = skt; a->hwnd = hwnd; a->msg = msg; a->events = events;

	    ioctlsocket(a->s, FIONBIO, &one);

	    return 0;
	}
    }

    return SOCKET_ERROR;
}

// thread func

DWORD
async_func(LPDWORD param)
{
    struct timeval tv;
    fd_set r, w, e;
    int n;
    int i;

    while(1) {
	FD_ZERO(&r);
	FD_ZERO(&w);
	FD_ZERO(&e);

	tv.tv_sec = 1;
	tv.tv_usec = 0;

	for (i = 0; i < MAXASYNCSOCKS; i++) {
	    int one = 1;
	    ASYNC_DATA *a = &asynctab[i];

	    if(a->hwnd == 0)
		continue;

	    if((a->events & FD_ACCEPT) && !(a->posted & FD_ACCEPT))
		FD_SET(a->s, &r);
	    if((a->events & FD_READ) && !(a->posted & FD_READ))
		FD_SET(a->s, &r);
	    if((a->events & FD_WRITE) && !(a->posted & FD_WRITE))
		FD_SET(a->s, &w);
	}

	if ((n = select(0, &r, &w, &e, &tv)) == SOCKET_ERROR) {
	    DWORD dwError = WSAGetLastError();

	    XCETrace("select: %d", dwError);

	    if (dwError == WSAENOTSOCK) {
		// could be a socket that has been closed, probably a
		// listensock that has accepted something...
	    } else {
		TclWinConvertWSAError(dwError);
		XCEShowMessageA("tcl: select: %s", dwError);
		exit(1);
	    }
	    continue;
	}

	if (n == 0) {
	    Sleep(100);
	    continue;
	}

	for (i = 0; i < MAXASYNCSOCKS; i++) {
	    ASYNC_DATA *a = &asynctab[i];

	    if (a->hwnd == 0)
		continue;

	    if ((a->events & FD_ACCEPT) && FD_ISSET(a->s, &r)) {
		if(!(a->posted & FD_ACCEPT)) {
		    XCETrace("Sending FD_ACCEPT to %x, sock %d",
			    a->hwnd, a->s);

		    PostMessage(a->hwnd, a->msg, a->s, FD_ACCEPT);
		    a->posted |= FD_ACCEPT;
		}
	    }
	    if ((a->events & FD_READ) && FD_ISSET(a->s, &r)) {
		if (!(a->posted & FD_READ)) {
		    XCETrace("Sending FD_READ to %x, sock %d",
			    a->hwnd, a->s);

		    PostMessage(a->hwnd, a->msg, a->s, FD_READ);
		    a->posted |= FD_READ;
		}
	    }
	    if ((a->events & FD_WRITE) && FD_ISSET(a->s, &w)) {
		if (!(a->posted & FD_WRITE)) {
		    XCETrace("Sending FD_WRITE to %x, sock %d",
			    a->hwnd, a->s);

		    PostMessage(a->hwnd, a->msg, a->s, FD_WRITE);
		    a->posted |= FD_WRITE;
		}
	    }
	}
	Sleep(0);
    }
}

//////////////////////////////////////////////////////////////////////

int
wce_killpid(unsigned long pid)
{
    HANDLE hProc;
    int res;

    XCETrace("Killing %u", pid);

    if ((hProc = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid)) == NULL) {
	XCETrace("OpenProcess: %d", GetLastError());
	return -1;
    }

    if ((res = TerminateProcess(hProc, -1)) != TRUE) {
	XCETrace("TerminateProcess: %d", GetLastError());
	return -1;
    }

    CloseHandle(hProc);

    if (res == FALSE) {
	return -1;
    }
    return 0;
}
