/*
 * Win32 miscellaneous functions
 *
 * Copyright 1995 Thomas Sandford (tdgsandf@prds-grn.demon.co.uk)
 */

/* Misc. new functions - they should be moved into appropriate files
at a later date. */

#include "config.h"

#include <stdio.h>
#include <string.h>
#ifdef HAVE_SYS_TIME_H
# include <sys/time.h>
#endif
#include <unistd.h>
#include "windef.h"
#include "winbase.h"
#include "winnls.h"
#include "winerror.h"
#include "wine/debug.h"

WINE_DEFAULT_DEBUG_CHANNEL(win32);
WINE_DECLARE_DEBUG_CHANNEL(debug);

static BOOL	QUERYPERF_Initialized		= 0;

#if defined(__i386__) && defined(__GNUC__)
static BOOL	QUERYPERF_RDTSC_Use		= 0;

/* Instead of using the rdtsc frequency, we're going to scale things down.
 * We'll use a simple shift instead of a divide to scale things down *fast*.
 *
 * It would be best if we could use the fixed frequency on both RDTSC and
 * gettimeofday, however gettimeofday's resolution is only 1000000.
 */
static LONGLONG QUERYPERF_RDTSC_FixedFrequency  = 0;
#define QUERYPERF_RDTSC_Shift 10
static const double QUERYPERF_RDTSC_Divisor = 1 << QUERYPERF_RDTSC_Shift;

#endif

static void QUERYPERF_Init(void)
{
#if defined(__i386__) && defined(__GNUC__)
    /* We are running on i386 and compiling on GCC.
     * Do a runtime check to see if we have the rdtsc instruction available
     */
    FILE		*fp;
    char		line[256], *s, *value;
    double		cpuMHz;

    TRACE("()\n");

    if (IsProcessorFeaturePresent( PF_RDTSC_INSTRUCTION_AVAILABLE ))
    {
	/* rdtsc is available.  However, in order to use it
	 * we also need to be able to get the processor's
	 * speed.  Currently we do this by reading /proc/cpuinfo
	 * which makes it Linux-specific.
	 */

	TRACE("rdtsc available\n");

	fp = fopen( "/proc/cpuinfo", "r" );
	if (fp)
	{
	    while(fgets( line, sizeof(line), fp ))
	    {
		/* NOTE: the ':' is the only character we can rely on */
		if (!(value = strchr( line, ':' )))
		    continue;

		/* terminate the valuename */
		*value++ = '\0';
		/* skip any leading spaces */
		while (*value == ' ') value++;
		if ((s = strchr( value, '\n' )))
		    *s = '\0';

		if (!strncasecmp( line, "cpu MHz", strlen( "cpu MHz" ) ))
		{
		    if (sscanf( value, "%lf", &cpuMHz ) == 1)
		    {
			QUERYPERF_RDTSC_FixedFrequency =  (cpuMHz * 1000000.0) / QUERYPERF_RDTSC_Divisor;
			QUERYPERF_RDTSC_Use = TRUE;
			TRACE("using frequency: %luMHz\n", (ULONG)cpuMHz);
			break;
		    }
		}
	    }
	    fclose(fp);
	}
    }
#endif
    QUERYPERF_Initialized = TRUE;
}


/****************************************************************************
 *		QueryPerformanceCounter (KERNEL32.@)
 */
BOOL WINAPI QueryPerformanceCounter(PLARGE_INTEGER counter)
{
    struct timeval tv;

    if (!QUERYPERF_Initialized)
	QUERYPERF_Init();

#if defined(__i386__) && defined(__GNUC__)
    if (QUERYPERF_RDTSC_Use)
    {
	/* i586 optimized version */
	LONGLONG tsc;

	__asm__ __volatile__ ( "rdtsc" : "=A" (tsc) );

	counter->QuadPart = tsc >>  QUERYPERF_RDTSC_Shift;

	return TRUE;
    }
    /* fall back to generic routine (ie, for i386, i486) */
#endif

    /* generic routine */
    gettimeofday( &tv, NULL );
    counter->QuadPart = (LONGLONG)tv.tv_usec + (LONGLONG)tv.tv_sec * 1000000LL;
    return TRUE;
}

/****************************************************************************
 *		QueryPerformanceFrequency (KERNEL32.@)
 */
BOOL WINAPI QueryPerformanceFrequency(PLARGE_INTEGER frequency)
{
    if (!QUERYPERF_Initialized)
	QUERYPERF_Init();

#if defined(__i386__) && defined(__GNUC__)
    if (QUERYPERF_RDTSC_Use)
    {
	frequency->QuadPart = QUERYPERF_RDTSC_FixedFrequency;
	return TRUE;
    }
#endif

    frequency->s.LowPart	= 1000000;
    frequency->s.HighPart	= 0;
    return TRUE;
}

/****************************************************************************
 *		FlushInstructionCache (KERNEL32.@)
 */
BOOL WINAPI FlushInstructionCache(HANDLE hProcess, LPCVOID lpBaseAddress, DWORD dwSize)
{
    if (GetVersion() & 0x80000000) return TRUE; /* not NT, always TRUE */
    FIXME_(debug)("(0x%08lx,%p,0x%08lx): stub\n",(DWORD)hProcess, lpBaseAddress, dwSize);
    return TRUE;
}

/***********************************************************************
 *           GetSystemPowerStatus      (KERNEL32.@)
 */
BOOL WINAPI GetSystemPowerStatus(LPSYSTEM_POWER_STATUS sps_ptr)
{
    return FALSE;   /* no power management support */
}


/***********************************************************************
 *           SetSystemPowerState      (KERNEL32.@)
 */
BOOL WINAPI SetSystemPowerState(BOOL suspend_or_hibernate,
                                  BOOL force_flag)
{
    /* suspend_or_hibernate flag: w95 does not support
       this feature anyway */

    for ( ;0; )
    {
        if ( force_flag )
        {
        }
        else
        {
        }
    }
    return TRUE;
}


/******************************************************************************
 * CreateMailslotA [KERNEL32.@]
 */
HANDLE WINAPI CreateMailslotA( LPCSTR lpName, DWORD nMaxMessageSize,
                                   DWORD lReadTimeout, LPSECURITY_ATTRIBUTES sa)
{
    FIXME("(%s,%ld,%ld,%p): stub\n", debugstr_a(lpName),
          nMaxMessageSize, lReadTimeout, sa);
    return 1;
}


/******************************************************************************
 * CreateMailslotW [KERNEL32.@]  Creates a mailslot with specified name
 *
 * PARAMS
 *    lpName          [I] Pointer to string for mailslot name
 *    nMaxMessageSize [I] Maximum message size
 *    lReadTimeout    [I] Milliseconds before read time-out
 *    sa              [I] Pointer to security structure
 *
 * RETURNS
 *    Success: Handle to mailslot
 *    Failure: INVALID_HANDLE_VALUE
 */
HANDLE WINAPI CreateMailslotW( LPCWSTR lpName, DWORD nMaxMessageSize,
                                   DWORD lReadTimeout, LPSECURITY_ATTRIBUTES sa )
{
    FIXME("(%s,%ld,%ld,%p): stub\n", debugstr_w(lpName),
          nMaxMessageSize, lReadTimeout, sa);
    return 1;
}


/******************************************************************************
 * GetMailslotInfo [KERNEL32.@]  Retrieves info about specified mailslot
 *
 * PARAMS
 *    hMailslot        [I] Mailslot handle
 *    lpMaxMessageSize [O] Address of maximum message size
 *    lpNextSize       [O] Address of size of next message
 *    lpMessageCount   [O] Address of number of messages
 *    lpReadTimeout    [O] Address of read time-out
 *
 * RETURNS
 *    Success: TRUE
 *    Failure: FALSE
 */
BOOL WINAPI GetMailslotInfo( HANDLE hMailslot, LPDWORD lpMaxMessageSize,
                               LPDWORD lpNextSize, LPDWORD lpMessageCount,
                               LPDWORD lpReadTimeout )
{
    FIXME("(%04x): stub\n",hMailslot);
    if (lpMaxMessageSize) *lpMaxMessageSize = (DWORD)NULL;
    if (lpNextSize) *lpNextSize = (DWORD)NULL;
    if (lpMessageCount) *lpMessageCount = (DWORD)NULL;
    if (lpReadTimeout) *lpReadTimeout = (DWORD)NULL;
    return TRUE;
}


/******************************************************************************
 * GetCompressedFileSizeA [KERNEL32.@]
 *
 * NOTES
 *    This should call the W function below
 */
DWORD WINAPI GetCompressedFileSizeA(
    LPCSTR lpFileName,
    LPDWORD lpFileSizeHigh)
{
    FIXME("(...): stub\n");
    return 0xffffffff;
}


/******************************************************************************
 * GetCompressedFileSizeW [KERNEL32.@]
 *
 * RETURNS
 *    Success: Low-order doubleword of number of bytes
 *    Failure: 0xffffffff
 */
DWORD WINAPI GetCompressedFileSizeW(
    LPCWSTR lpFileName,     /* [in]  Pointer to name of file */
    LPDWORD lpFileSizeHigh) /* [out] Receives high-order doubleword of size */
{
    FIXME("(%s,%p): stub\n",debugstr_w(lpFileName),lpFileSizeHigh);
    return 0xffffffff;
}


/******************************************************************************
 * SetComputerNameA [KERNEL32.@]
 */
BOOL WINAPI SetComputerNameA( LPCSTR lpComputerName )
{
    BOOL ret;
    DWORD len = MultiByteToWideChar( CP_ACP, 0, lpComputerName, -1, NULL, 0 );
    LPWSTR nameW = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );

    MultiByteToWideChar( CP_ACP, 0, lpComputerName, -1, nameW, len );
    ret = SetComputerNameW( nameW );
    HeapFree( GetProcessHeap(), 0, nameW );
    return ret;
}


/******************************************************************************
 * SetComputerNameW [KERNEL32.@]
 *
 * PARAMS
 *    lpComputerName [I] Address of new computer name
 *
 * RETURNS STD
 */
BOOL WINAPI SetComputerNameW( LPCWSTR lpComputerName )
{
    FIXME("(%s): stub\n", debugstr_w(lpComputerName));
    return TRUE;
}

/******************************************************************************
 *		CreateIoCompletionPort (KERNEL32.@)
 */
HANDLE WINAPI CreateIoCompletionPort(HANDLE hFileHandle,
HANDLE hExistingCompletionPort, DWORD dwCompletionKey,
DWORD dwNumberOfConcurrentThreads)
{
    FIXME("(%04x, %04x, %08lx, %08lx): stub.\n", hFileHandle, hExistingCompletionPort, dwCompletionKey, dwNumberOfConcurrentThreads);
    return (HANDLE)NULL;
}

/******************************************************************************
 *		GetQueuedCompletionStatus (KERNEL32.@)
 */
BOOL WINAPI GetQueuedCompletionStatus(
    HANDLE CompletionPort, LPDWORD lpNumberOfBytesTransferred,
    LPDWORD lpCompletionKey, LPOVERLAPPED *lpOverlapped, DWORD dwMilliseconds
) {
    FIXME("(%x,%p,%p,%p,%ld), stub!\n",CompletionPort,lpNumberOfBytesTransferred,lpCompletionKey,lpOverlapped,dwMilliseconds);
    SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
    return FALSE;
}
