/////////////////////////////////////////////////////////////////////////////
// Name:        src/msw/utils.cpp
// Purpose:     Various utilities
// Author:      Julian Smart
// Modified by:
// Created:     04/01/98
// RCS-ID:      $Id: utils.cpp 43347 2006-11-12 15:39:12Z JS $
// Copyright:   (c) Julian Smart
// Licence:     wxWindows licence
/////////////////////////////////////////////////////////////////////////////

// ============================================================================
// declarations
// ============================================================================

// ----------------------------------------------------------------------------
// headers
// ----------------------------------------------------------------------------

// For compilers that support precompilation, includes "wx.h".
#include "wx/wxprec.h"

#ifdef __BORLANDC__
    #pragma hdrstop
#endif

#ifndef WX_PRECOMP
    #include "wx/msw/missing.h"     // CHARSET_HANGUL
    #include "wx/utils.h"
    #include "wx/app.h"
    #include "wx/intl.h"
    #include "wx/log.h"
    #include "wx/timer.h"
#endif  //WX_PRECOMP

#include "wx/msw/registry.h"
#include "wx/apptrait.h"
#include "wx/dynlib.h"
#include "wx/dynload.h"
#include "wx/scopeguard.h"

#include "wx/confbase.h"        // for wxExpandEnvVars()

#include "wx/msw/private.h"     // includes <windows.h>

#if defined(__CYGWIN__)
    //CYGWIN gives annoying warning about runtime stuff if we don't do this
#   define USE_SYS_TYPES_FD_SET
#   include <sys/types.h>
#endif

// Doesn't work with Cygwin at present
#if wxUSE_SOCKETS && (defined(__GNUWIN32_OLD__) || defined(__WXWINCE__) || defined(__CYGWIN32__))
    // apparently we need to include winsock.h to get WSADATA and other stuff
    // used in wxGetFullHostName() with the old mingw32 versions
    #include <winsock.h>
#endif

#if !defined(__GNUWIN32__) && !defined(__SALFORDC__) && !defined(__WXMICROWIN__) && !defined(__WXWINCE__)
    #include <direct.h>

    #ifndef __MWERKS__
        #include <dos.h>
    #endif
#endif  //GNUWIN32

#if defined(__CYGWIN__)
    #include <sys/unistd.h>
    #include <sys/stat.h>
    #include <sys/cygwin.h> // for cygwin_conv_to_full_win32_path()
#endif  //GNUWIN32

#ifdef __BORLANDC__ // Please someone tell me which version of Borland needs
                    // this (3.1 I believe) and how to test for it.
                    // If this works for Borland 4.0 as well, then no worries.
    #include <dir.h>
#endif

// VZ: there is some code using NetXXX() functions to get the full user name:
//     I don't think it's a good idea because they don't work under Win95 and
//     seem to return the same as wxGetUserId() under NT. If you really want
//     to use them, just #define USE_NET_API
#undef USE_NET_API

#ifdef USE_NET_API
    #include <lm.h>
#endif // USE_NET_API

#if defined(__WIN32__) && !defined(__WXMICROWIN__) && !defined(__WXWINCE__)
    #ifndef __UNIX__
        #include <io.h>
    #endif

    #ifndef __GNUWIN32__
        #include <shellapi.h>
    #endif
#endif

#ifndef __WATCOMC__
    #if !(defined(_MSC_VER) && (_MSC_VER > 800))
        #include <errno.h>
    #endif
#endif

// For wxKillAllChildren
#include <tlhelp32.h>

// ----------------------------------------------------------------------------
// constants
// ----------------------------------------------------------------------------

// In the WIN.INI file
#if (!defined(USE_NET_API) && !defined(__WXWINCE__)) || defined(__WXMICROWIN__)
static const wxChar WX_SECTION[] = wxT("wxWindows");
#endif

#if (!defined(USE_NET_API) && !defined(__WXWINCE__))
static const wxChar eUSERNAME[]  = wxT("UserName");
#endif

// ============================================================================
// implementation
// ============================================================================

// ----------------------------------------------------------------------------
// get host name and related
// ----------------------------------------------------------------------------

// Get hostname only (without domain name)
bool wxGetHostName(wxChar *WXUNUSED_IN_WINCE(buf),
                   int WXUNUSED_IN_WINCE(maxSize))
{
#if defined(__WXWINCE__)
    // TODO-CE
    return false;
#elif defined(__WIN32__) && !defined(__WXMICROWIN__)
    DWORD nSize = maxSize;
    if ( !::GetComputerName(buf, &nSize) )
    {
        wxLogLastError(wxT("GetComputerName"));

        return false;
    }

    return true;
#else
    wxChar *sysname;
    const wxChar *default_host = wxT("noname");

    if ((sysname = wxGetenv(wxT("SYSTEM_NAME"))) == NULL) {
        GetProfileString(WX_SECTION, eHOSTNAME, default_host, buf, maxSize - 1);
    } else
        wxStrncpy(buf, sysname, maxSize - 1);
    buf[maxSize] = wxT('\0');
    return *buf ? true : false;
#endif
}

// get full hostname (with domain name if possible)
bool wxGetFullHostName(wxChar *buf, int maxSize)
{
#if !defined( __WXMICROWIN__) && wxUSE_DYNAMIC_LOADER && wxUSE_SOCKETS
    // TODO should use GetComputerNameEx() when available

    // we don't want to always link with Winsock DLL as we might not use it at
    // all, so load it dynamically here if needed (and don't complain if it is
    // missing, we handle this)
    wxLogNull noLog;

    wxDynamicLibrary dllWinsock(_T("ws2_32.dll"), wxDL_VERBATIM);
    if ( dllWinsock.IsLoaded() )
    {
        typedef int (PASCAL *WSAStartup_t)(WORD, WSADATA *);
        typedef int (PASCAL *gethostname_t)(char *, int);
        typedef hostent* (PASCAL *gethostbyname_t)(const char *);
        typedef hostent* (PASCAL *gethostbyaddr_t)(const char *, int , int);
        typedef int (PASCAL *WSACleanup_t)(void);

        #define LOAD_WINSOCK_FUNC(func)                                       \
            func ## _t                                                        \
                pfn ## func = (func ## _t)dllWinsock.GetSymbol(_T(#func))

        LOAD_WINSOCK_FUNC(WSAStartup);

        WSADATA wsa;
        if ( pfnWSAStartup && pfnWSAStartup(MAKEWORD(1, 1), &wsa) == 0 )
        {
            LOAD_WINSOCK_FUNC(gethostname);

            wxString host;
            if ( pfngethostname )
            {
                char bufA[256];
                if ( pfngethostname(bufA, WXSIZEOF(bufA)) == 0 )
                {
                    // gethostname() won't usually include the DNS domain name,
                    // for this we need to work a bit more
                    if ( !strchr(bufA, '.') )
                    {
                        LOAD_WINSOCK_FUNC(gethostbyname);

                        struct hostent *pHostEnt = pfngethostbyname
                                                    ? pfngethostbyname(bufA)
                                                    : NULL;

                        if ( pHostEnt )
                        {
                            // Windows will use DNS internally now
                            LOAD_WINSOCK_FUNC(gethostbyaddr);

                            pHostEnt = pfngethostbyaddr
                                        ? pfngethostbyaddr(pHostEnt->h_addr,
                                                           4, AF_INET)
                                        : NULL;
                        }

                        if ( pHostEnt )
                        {
                            host = wxString::FromAscii(pHostEnt->h_name);
                        }
                    }
                }
            }

            LOAD_WINSOCK_FUNC(WSACleanup);
            if ( pfnWSACleanup )
                pfnWSACleanup();


            if ( !host.empty() )
            {
                wxStrncpy(buf, host, maxSize);

                return true;
            }
        }
    }
#endif // !__WXMICROWIN__

    return wxGetHostName(buf, maxSize);
}

// Get user ID e.g. jacs
bool wxGetUserId(wxChar *WXUNUSED_IN_WINCE(buf),
                 int WXUNUSED_IN_WINCE(maxSize))
{
#if defined(__WXWINCE__)
    // TODO-CE
    return false;
#elif defined(__WIN32__) && !defined(__WXMICROWIN__)
    DWORD nSize = maxSize;
    if ( ::GetUserName(buf, &nSize) == 0 )
    {
        // actually, it does happen on Win9x if the user didn't log on
        DWORD res = ::GetEnvironmentVariable(wxT("username"), buf, maxSize);
        if ( res == 0 )
        {
            // not found
            return false;
        }
    }

    return true;
#else   // __WXMICROWIN__
    wxChar *user;
    const wxChar *default_id = wxT("anonymous");

    // Can't assume we have NIS (PC-NFS) or some other ID daemon
    // So we ...
    if ( (user = wxGetenv(wxT("USER"))) == NULL &&
         (user = wxGetenv(wxT("LOGNAME"))) == NULL )
    {
        // Use wxWidgets configuration data (comming soon)
        GetProfileString(WX_SECTION, eUSERID, default_id, buf, maxSize - 1);
    }
    else
    {
        wxStrncpy(buf, user, maxSize - 1);
    }

    return *buf ? true : false;
#endif
}

// Get user name e.g. Julian Smart
bool wxGetUserName(wxChar *buf, int maxSize)
{
    wxCHECK_MSG( buf && ( maxSize > 0 ), false,
                    _T("empty buffer in wxGetUserName") );
#if defined(__WXWINCE__)
    wxLogNull noLog;
    wxRegKey key(wxRegKey::HKCU, wxT("ControlPanel\\Owner"));
    if(!key.Open(wxRegKey::Read))
        return false;
    wxString name;
    if(!key.QueryValue(wxT("Owner"),name))
        return false;
    wxStrncpy(buf, name.c_str(), maxSize-1);
    buf[maxSize-1] = _T('\0');
    return true;
#elif defined(USE_NET_API)
    CHAR szUserName[256];
    if ( !wxGetUserId(szUserName, WXSIZEOF(szUserName)) )
        return false;

    // TODO how to get the domain name?
    CHAR *szDomain = "";

    // the code is based on the MSDN example (also see KB article Q119670)
    WCHAR wszUserName[256];          // Unicode user name
    WCHAR wszDomain[256];
    LPBYTE ComputerName;

    USER_INFO_2 *ui2;         // User structure

    // Convert ANSI user name and domain to Unicode
    MultiByteToWideChar( CP_ACP, 0, szUserName, strlen(szUserName)+1,
            wszUserName, WXSIZEOF(wszUserName) );
    MultiByteToWideChar( CP_ACP, 0, szDomain, strlen(szDomain)+1,
            wszDomain, WXSIZEOF(wszDomain) );

    // Get the computer name of a DC for the domain.
    if ( NetGetDCName( NULL, wszDomain, &ComputerName ) != NERR_Success )
    {
        wxLogError(wxT("Can not find domain controller"));

        goto error;
    }

    // Look up the user on the DC
    NET_API_STATUS status = NetUserGetInfo( (LPWSTR)ComputerName,
            (LPWSTR)&wszUserName,
            2, // level - we want USER_INFO_2
            (LPBYTE *) &ui2 );
    switch ( status )
    {
        case NERR_Success:
            // ok
            break;

        case NERR_InvalidComputer:
            wxLogError(wxT("Invalid domain controller name."));

            goto error;

        case NERR_UserNotFound:
            wxLogError(wxT("Invalid user name '%s'."), szUserName);

            goto error;

        default:
            wxLogSysError(wxT("Can't get information about user"));

            goto error;
    }

    // Convert the Unicode full name to ANSI
    WideCharToMultiByte( CP_ACP, 0, ui2->usri2_full_name, -1,
            buf, maxSize, NULL, NULL );

    return true;

error:
    wxLogError(wxT("Couldn't look up full user name."));

    return false;
#else  // !USE_NET_API
    // Could use NIS, MS-Mail or other site specific programs
    // Use wxWidgets configuration data
    bool ok = GetProfileString(WX_SECTION, eUSERNAME, wxEmptyString, buf, maxSize - 1) != 0;
    if ( !ok )
    {
        ok = wxGetUserId(buf, maxSize);
    }

    if ( !ok )
    {
        wxStrncpy(buf, wxT("Unknown User"), maxSize);
    }

    return true;
#endif // Win32/16
}

const wxChar* wxGetHomeDir(wxString *pstr)
{
    wxString& strDir = *pstr;

    // first branch is for Cygwin
#if defined(__UNIX__) && !defined(__WINE__)
    const wxChar *szHome = wxGetenv("HOME");
    if ( szHome == NULL ) {
      // we're homeless...
      wxLogWarning(_("can't find user's HOME, using current directory."));
      strDir = wxT(".");
    }
    else
       strDir = szHome;

    // add a trailing slash if needed
    if ( strDir.Last() != wxT('/') )
      strDir << wxT('/');

    #ifdef __CYGWIN__
        // Cygwin returns unix type path but that does not work well
        static wxChar windowsPath[MAX_PATH];
        cygwin_conv_to_full_win32_path(strDir, windowsPath);
        strDir = windowsPath;
    #endif
#elif defined(__WXWINCE__)
    strDir = wxT("\\");
#else
    strDir.clear();

    // If we have a valid HOME directory, as is used on many machines that
    // have unix utilities on them, we should use that.
    const wxChar *szHome = wxGetenv(wxT("HOME"));

    if ( szHome != NULL )
    {
        strDir = szHome;
    }
    else // no HOME, try HOMEDRIVE/PATH
    {
        szHome = wxGetenv(wxT("HOMEDRIVE"));
        if ( szHome != NULL )
            strDir << szHome;
        szHome = wxGetenv(wxT("HOMEPATH"));

        if ( szHome != NULL )
        {
            strDir << szHome;

            // the idea is that under NT these variables have default values
            // of "%systemdrive%:" and "\\". As we don't want to create our
            // config files in the root directory of the system drive, we will
            // create it in our program's dir. However, if the user took care
            // to set HOMEPATH to something other than "\\", we suppose that he
            // knows what he is doing and use the supplied value.
            if ( wxStrcmp(szHome, wxT("\\")) == 0 )
                strDir.clear();
        }
    }

    if ( strDir.empty() )
    {
        // If we have a valid USERPROFILE directory, as is the case in
        // Windows NT, 2000 and XP, we should use that as our home directory.
        szHome = wxGetenv(wxT("USERPROFILE"));

        if ( szHome != NULL )
            strDir = szHome;
    }

    if ( !strDir.empty() )
    {
        // sometimes the value of HOME may be "%USERPROFILE%", so reexpand the
        // value once again, it shouldn't hurt anyhow
        strDir = wxExpandEnvVars(strDir);
    }
    else // fall back to the program directory
    {
        // extract the directory component of the program file name
        wxSplitPath(wxGetFullModuleName(), &strDir, NULL, NULL);
    }
#endif  // UNIX/Win

    return strDir.c_str();
}

wxChar *wxGetUserHome(const wxString& WXUNUSED(user))
{
    // VZ: the old code here never worked for user != "" anyhow! Moreover, it
    //     returned sometimes a malloc()'d pointer, sometimes a pointer to a
    //     static buffer and sometimes I don't even know what.
    static wxString s_home;

    return (wxChar *)wxGetHomeDir(&s_home);
}

bool wxGetDiskSpace(const wxString& WXUNUSED_IN_WINCE(path),
                    wxDiskspaceSize_t *WXUNUSED_IN_WINCE(pTotal),
                    wxDiskspaceSize_t *WXUNUSED_IN_WINCE(pFree))
{
#ifdef __WXWINCE__
    // TODO-CE
    return false;
#else
    if ( path.empty() )
        return false;

// old w32api don't have ULARGE_INTEGER
#if defined(__WIN32__) && \
    (!defined(__GNUWIN32__) || wxCHECK_W32API_VERSION( 0, 3 ))
    // GetDiskFreeSpaceEx() is not available under original Win95, check for
    // it
    typedef BOOL (WINAPI *GetDiskFreeSpaceEx_t)(LPCTSTR,
                                                PULARGE_INTEGER,
                                                PULARGE_INTEGER,
                                                PULARGE_INTEGER);

    GetDiskFreeSpaceEx_t
        pGetDiskFreeSpaceEx = (GetDiskFreeSpaceEx_t)::GetProcAddress
                              (
                                ::GetModuleHandle(_T("kernel32.dll")),
#if wxUSE_UNICODE
                                "GetDiskFreeSpaceExW"
#else
                                "GetDiskFreeSpaceExA"
#endif
                              );

    if ( pGetDiskFreeSpaceEx )
    {
        ULARGE_INTEGER bytesFree, bytesTotal;

        // may pass the path as is, GetDiskFreeSpaceEx() is smart enough
        if ( !pGetDiskFreeSpaceEx(path,
                                  &bytesFree,
                                  &bytesTotal,
                                  NULL) )
        {
            wxLogLastError(_T("GetDiskFreeSpaceEx"));

            return false;
        }

        // ULARGE_INTEGER is a union of a 64 bit value and a struct containing
        // two 32 bit fields which may be or may be not named - try to make it
        // compile in all cases
#if defined(__BORLANDC__) && !defined(_ANONYMOUS_STRUCT)
        #define UL(ul) ul.u
#else // anon union
        #define UL(ul) ul
#endif
        if ( pTotal )
        {
#if wxUSE_LONGLONG
            *pTotal = wxDiskspaceSize_t(UL(bytesTotal).HighPart, UL(bytesTotal).LowPart);
#else
            *pTotal = wxDiskspaceSize_t(UL(bytesTotal).LowPart);
#endif
        }

        if ( pFree )
        {
#if wxUSE_LONGLONG
            *pFree = wxLongLong(UL(bytesFree).HighPart, UL(bytesFree).LowPart);
#else
            *pFree = wxDiskspaceSize_t(UL(bytesFree).LowPart);
#endif
        }
    }
    else
#endif // Win32
    {
        // there's a problem with drives larger than 2GB, GetDiskFreeSpaceEx()
        // should be used instead - but if it's not available, fall back on
        // GetDiskFreeSpace() nevertheless...

        DWORD lSectorsPerCluster,
              lBytesPerSector,
              lNumberOfFreeClusters,
              lTotalNumberOfClusters;

        // FIXME: this is wrong, we should extract the root drive from path
        //        instead, but this is the job for wxFileName...
        if ( !::GetDiskFreeSpace(path,
                                 &lSectorsPerCluster,
                                 &lBytesPerSector,
                                 &lNumberOfFreeClusters,
                                 &lTotalNumberOfClusters) )
        {
            wxLogLastError(_T("GetDiskFreeSpace"));

            return false;
        }

        wxDiskspaceSize_t lBytesPerCluster = (wxDiskspaceSize_t) lSectorsPerCluster;
        lBytesPerCluster *= lBytesPerSector;

        if ( pTotal )
        {
            *pTotal = lBytesPerCluster;
            *pTotal *= lTotalNumberOfClusters;
        }

        if ( pFree )
        {
            *pFree = lBytesPerCluster;
            *pFree *= lNumberOfFreeClusters;
        }
    }

    return true;
#endif
    // __WXWINCE__
}

// ----------------------------------------------------------------------------
// env vars
// ----------------------------------------------------------------------------

bool wxGetEnv(const wxString& WXUNUSED_IN_WINCE(var),
              wxString *WXUNUSED_IN_WINCE(value))
{
#ifdef __WXWINCE__
    // no environment variables under CE
    return false;
#else // Win32
    // first get the size of the buffer
    DWORD dwRet = ::GetEnvironmentVariable(var, NULL, 0);
    if ( !dwRet )
    {
        // this means that there is no such variable
        return false;
    }

    if ( value )
    {
        (void)::GetEnvironmentVariable(var, wxStringBuffer(*value, dwRet),
                                       dwRet);
    }

    return true;
#endif // WinCE/32
}

bool wxSetEnv(const wxString& WXUNUSED_IN_WINCE(var),
              const wxChar *WXUNUSED_IN_WINCE(value))
{
    // some compilers have putenv() or _putenv() or _wputenv() but it's better
    // to always use Win32 function directly instead of dealing with them
#ifdef __WXWINCE__
    // no environment variables under CE
    return false;
#else
    if ( !::SetEnvironmentVariable(var, value) )
    {
        wxLogLastError(_T("SetEnvironmentVariable"));

        return false;
    }

    return true;
#endif
}

// ----------------------------------------------------------------------------
// process management
// ----------------------------------------------------------------------------

// structure used to pass parameters from wxKill() to wxEnumFindByPidProc()
struct wxFindByPidParams
{
    wxFindByPidParams() { hwnd = 0; pid = 0; }

    // the HWND used to return the result
    HWND hwnd;

    // the PID we're looking from
    DWORD pid;

    DECLARE_NO_COPY_CLASS(wxFindByPidParams)
};

// wxKill helper: EnumWindows() callback which is used to find the first (top
// level) window belonging to the given process
BOOL CALLBACK wxEnumFindByPidProc(HWND hwnd, LPARAM lParam)
{
    DWORD pid;
    (void)::GetWindowThreadProcessId(hwnd, &pid);

    wxFindByPidParams *params = (wxFindByPidParams *)lParam;
    if ( pid == params->pid )
    {
        // remember the window we found
        params->hwnd = hwnd;

        // return FALSE to stop the enumeration
        return FALSE;
    }

    // continue enumeration
    return TRUE;
}

int wxKillAllChildren(long pid, wxSignal sig, wxKillError *krc);

int wxKill(long pid, wxSignal sig, wxKillError *krc, int flags)
{
    if (flags & wxKILL_CHILDREN)
        wxKillAllChildren(pid, sig, krc);

    // get the process handle to operate on
    HANDLE hProcess = ::OpenProcess(SYNCHRONIZE |
                                    PROCESS_TERMINATE |
                                    PROCESS_QUERY_INFORMATION,
                                    FALSE, // not inheritable
                                    (DWORD)pid);
    if ( hProcess == NULL )
    {
        if ( krc )
        {
            // recognize wxKILL_ACCESS_DENIED as special because this doesn't
            // mean that the process doesn't exist and this is important for
            // wxProcess::Exists()
            *krc = ::GetLastError() == ERROR_ACCESS_DENIED
                        ? wxKILL_ACCESS_DENIED
                        : wxKILL_NO_PROCESS;
        }

        return -1;
    }

    wxON_BLOCK_EXIT1(::CloseHandle, hProcess);

    bool ok = true;
    switch ( sig )
    {
        case wxSIGKILL:
            // kill the process forcefully returning -1 as error code
            if ( !::TerminateProcess(hProcess, (UINT)-1) )
            {
                wxLogSysError(_("Failed to kill process %d"), pid);

                if ( krc )
                {
                    // this is not supposed to happen if we could open the
                    // process
                    *krc = wxKILL_ERROR;
                }

                ok = false;
            }
            break;

        case wxSIGNONE:
            // do nothing, we just want to test for process existence
            if ( krc )
                *krc = wxKILL_OK;
            return 0;

        default:
            // any other signal means "terminate"
            {
                wxFindByPidParams params;
                params.pid = (DWORD)pid;

                // EnumWindows() has nice semantics: it returns 0 if it found
                // something or if an error occurred and non zero if it
                // enumerated all the window
                if ( !::EnumWindows(wxEnumFindByPidProc, (LPARAM)&params) )
                {
                    // did we find any window?
                    if ( params.hwnd )
                    {
                        // tell the app to close
                        //
                        // NB: this is the harshest way, the app won't have an
                        //     opportunity to save any files, for example, but
                        //     this is probably what we want here. If not we
                        //     can also use SendMesageTimeout(WM_CLOSE)
                        if ( !::PostMessage(params.hwnd, WM_QUIT, 0, 0) )
                        {
                            wxLogLastError(_T("PostMessage(WM_QUIT)"));
                        }
                    }
                    else // it was an error then
                    {
                        wxLogLastError(_T("EnumWindows"));

                        ok = false;
                    }
                }
                else // no windows for this PID
                {
                    if ( krc )
                        *krc = wxKILL_ERROR;

                    ok = false;
                }
            }
    }

    // the return code
    DWORD rc wxDUMMY_INITIALIZE(0);
    if ( ok )
    {
        // as we wait for a short time, we can use just WaitForSingleObject()
        // and not MsgWaitForMultipleObjects()
        switch ( ::WaitForSingleObject(hProcess, 500 /* msec */) )
        {
            case WAIT_OBJECT_0:
                // process terminated
                if ( !::GetExitCodeProcess(hProcess, &rc) )
                {
                    wxLogLastError(_T("GetExitCodeProcess"));
                }
                break;

            default:
                wxFAIL_MSG( _T("unexpected WaitForSingleObject() return") );
                // fall through

            case WAIT_FAILED:
                wxLogLastError(_T("WaitForSingleObject"));
                // fall through

            case WAIT_TIMEOUT:
                if ( krc )
                    *krc = wxKILL_ERROR;

                rc = STILL_ACTIVE;
                break;
        }
    }


    // the return code is the same as from Unix kill(): 0 if killed
    // successfully or -1 on error
    if ( !ok || rc == STILL_ACTIVE )
        return -1;

    if ( krc )
        *krc = wxKILL_OK;

    return 0;
}

typedef HANDLE (WINAPI *CreateToolhelp32Snapshot_t)(DWORD,DWORD);
typedef BOOL (WINAPI *Process32_t)(HANDLE,LPPROCESSENTRY32);

CreateToolhelp32Snapshot_t lpfCreateToolhelp32Snapshot;
Process32_t lpfProcess32First, lpfProcess32Next;

static void InitToolHelp32()
{
    static bool s_initToolHelpDone = false;

    if (s_initToolHelpDone)
        return;

    s_initToolHelpDone = true;

    lpfCreateToolhelp32Snapshot = NULL;
    lpfProcess32First = NULL;
    lpfProcess32Next = NULL;

#if wxUSE_DYNLIB_CLASS

    wxDynamicLibrary dllKernel(_T("kernel32.dll"), wxDL_VERBATIM);

    // Get procedure addresses.
    // We are linking to these functions of Kernel32
    // explicitly, because otherwise a module using
    // this code would fail to load under Windows NT,
    // which does not have the Toolhelp32
    // functions in the Kernel 32.
    lpfCreateToolhelp32Snapshot =
        (CreateToolhelp32Snapshot_t)dllKernel.RawGetSymbol(_T("CreateToolhelp32Snapshot"));

    lpfProcess32First =
        (Process32_t)dllKernel.RawGetSymbol(_T("Process32First"));

    lpfProcess32Next =
        (Process32_t)dllKernel.RawGetSymbol(_T("Process32Next"));

#endif // wxUSE_DYNLIB_CLASS
}

// By John Skiff
int wxKillAllChildren(long pid, wxSignal sig, wxKillError *krc)
{
    InitToolHelp32();

    if (krc)
        *krc = wxKILL_OK;

    // If not implemented for this platform (e.g. NT 4.0), silently ignore
    if (!lpfCreateToolhelp32Snapshot || !lpfProcess32First || !lpfProcess32Next)
        return 0;

    // Take a snapshot of all processes in the system.
    HANDLE hProcessSnap = lpfCreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
    if (hProcessSnap == INVALID_HANDLE_VALUE) {
        if (krc)
            *krc = wxKILL_ERROR;
        return -1;
    }

    //Fill in the size of the structure before using it.
    PROCESSENTRY32 pe;
    wxZeroMemory(pe);
    pe.dwSize = sizeof(PROCESSENTRY32);

    // Walk the snapshot of the processes, and for each process,
    // kill it if its parent is pid.
    if (!lpfProcess32First(hProcessSnap, &pe)) {
        // Can't get first process.
        if (krc)
            *krc = wxKILL_ERROR;
        CloseHandle (hProcessSnap);
        return -1;
    }

    do {
        if (pe.th32ParentProcessID == (DWORD) pid) {
            if (wxKill(pe.th32ProcessID, sig, krc))
                return -1;
        }
    } while (lpfProcess32Next (hProcessSnap, &pe));


    return 0;
}

// Execute a program in an Interactive Shell
bool wxShell(const wxString& command)
{
    wxString cmd;

#ifdef __WXWINCE__
    cmd = command;
#else
    wxChar *shell = wxGetenv(wxT("COMSPEC"));
    if ( !shell )
        shell = (wxChar*) wxT("\\COMMAND.COM");

    if ( !command )
    {
        // just the shell
        cmd = shell;
    }
    else
    {
        // pass the command to execute to the command processor
        cmd.Printf(wxT("%s /c %s"), shell, command.c_str());
    }
#endif

    return wxExecute(cmd, wxEXEC_SYNC) == 0;
}

// Shutdown or reboot the PC
bool wxShutdown(wxShutdownFlags WXUNUSED_IN_WINCE(wFlags))
{
#ifdef __WXWINCE__
    // TODO-CE
    return false;
#elif defined(__WIN32__)
    bool bOK = true;

    if ( wxGetOsVersion(NULL, NULL) == wxOS_WINDOWS_NT ) // if is NT or 2K
    {
        // Get a token for this process.
        HANDLE hToken;
        bOK = ::OpenProcessToken(GetCurrentProcess(),
                                 TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,
                                 &hToken) != 0;
        if ( bOK )
        {
            TOKEN_PRIVILEGES tkp;

            // Get the LUID for the shutdown privilege.
            ::LookupPrivilegeValue(NULL, SE_SHUTDOWN_NAME,
                                   &tkp.Privileges[0].Luid);

            tkp.PrivilegeCount = 1;  // one privilege to set
            tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;

            // Get the shutdown privilege for this process.
            ::AdjustTokenPrivileges(hToken, FALSE, &tkp, 0,
                                    (PTOKEN_PRIVILEGES)NULL, 0);

            // Cannot test the return value of AdjustTokenPrivileges.
            bOK = ::GetLastError() == ERROR_SUCCESS;
        }
    }

    if ( bOK )
    {
        UINT flags = EWX_SHUTDOWN | EWX_FORCE;
        switch ( wFlags )
        {
            case wxSHUTDOWN_POWEROFF:
                flags |= EWX_POWEROFF;
                break;

            case wxSHUTDOWN_REBOOT:
                flags |= EWX_REBOOT;
                break;

            default:
                wxFAIL_MSG( _T("unknown wxShutdown() flag") );
                return false;
        }

        bOK = ::ExitWindowsEx(flags, 0) != 0;
    }

    return bOK;
#endif // Win32/16
}

// ----------------------------------------------------------------------------
// misc
// ----------------------------------------------------------------------------

// Get free memory in bytes, or -1 if cannot determine amount (e.g. on UNIX)
wxMemorySize wxGetFreeMemory()
{
#if defined(__WIN64__)
    MEMORYSTATUSEX memStatex;
    memStatex.dwLength = sizeof (memStatex);
    ::GlobalMemoryStatusEx (&memStatex);
    return (wxMemorySize)memStatex.ullAvailPhys;
#else /* if defined(__WIN32__) */
    MEMORYSTATUS memStatus;
    memStatus.dwLength = sizeof(MEMORYSTATUS);
    ::GlobalMemoryStatus(&memStatus);
    return (wxMemorySize)memStatus.dwAvailPhys;
#endif
}

unsigned long wxGetProcessId()
{
    return ::GetCurrentProcessId();
}

// Emit a beeeeeep
void wxBell()
{
    ::MessageBeep((UINT)-1);        // default sound
}

bool wxIsDebuggerRunning()
{
#if wxUSE_DYNLIB_CLASS
    // IsDebuggerPresent() is not available under Win95, so load it dynamically
    wxDynamicLibrary dll(_T("kernel32.dll"), wxDL_VERBATIM);

    typedef BOOL (WINAPI *IsDebuggerPresent_t)();
    if ( !dll.HasSymbol(_T("IsDebuggerPresent")) )
    {
        // no way to know, assume no
        return false;
    }

    return (*(IsDebuggerPresent_t)dll.GetSymbol(_T("IsDebuggerPresent")))() != 0;
#else
    return false;
#endif
}

// ----------------------------------------------------------------------------
// OS version
// ----------------------------------------------------------------------------

wxString wxGetOsDescription()
{
    wxString str;

    OSVERSIONINFO info;
    wxZeroMemory(info);

    info.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
    if ( ::GetVersionEx(&info) )
    {
        switch ( info.dwPlatformId )
        {
#ifdef VER_PLATFORM_WIN32_CE
            case VER_PLATFORM_WIN32_CE:
                str.Printf(_("Windows CE (%d.%d)"),
                           info.dwMajorVersion,
                           info.dwMinorVersion);
                break;
#endif
            case VER_PLATFORM_WIN32s:
                str = _("Win32s on Windows 3.1");
                break;

            case VER_PLATFORM_WIN32_WINDOWS:
                switch (info.dwMinorVersion)
                {
                    case 0:
                        if ( info.szCSDVersion[1] == 'B' ||
                             info.szCSDVersion[1] == 'C' )
                        {
                            str = _("Windows 95 OSR2");
                        }
                        else
                        {
                            str = _("Windows 95");
                        }
                        break;
                    case 10:
                        if ( info.szCSDVersion[1] == 'B' ||
                             info.szCSDVersion[1] == 'C' )
                        {
                            str = _("Windows 98 SE");
                        }
                        else
                        {
                            str = _("Windows 98");
                        }
                        break;
                    case 90:
                        str = _("Windows ME");
                        break;
                    default:
                        str.Printf(_("Windows 9x (%d.%d)"),
                                   info.dwMajorVersion,
                                   info.dwMinorVersion);
                        break;
                }
                if ( !wxIsEmpty(info.szCSDVersion) )
                {
                    str << _T(" (") << info.szCSDVersion << _T(')');
                }
                break;

            case VER_PLATFORM_WIN32_NT:
                if ( info.dwMajorVersion == 5 )
                {
                    switch ( info.dwMinorVersion )
                    {
                        case 0:
                            str.Printf(_("Windows 2000 (build %lu"),
                                       info.dwBuildNumber);
                            break;
                        case 1:
                            str.Printf(_("Windows XP (build %lu"),
                                       info.dwBuildNumber);
                            break;
                        case 2:
                            str.Printf(_("Windows Server 2003 (build %lu"),
                                       info.dwBuildNumber);
                            break;
                    }
                }
                if ( str.empty() )
                {
                    str.Printf(_("Windows NT %lu.%lu (build %lu"),
                           info.dwMajorVersion,
                           info.dwMinorVersion,
                           info.dwBuildNumber);
                }
                if ( !wxIsEmpty(info.szCSDVersion) )
                {
                    str << _T(", ") << info.szCSDVersion;
                }
                str << _T(')');
                break;
        }
    }
    else
    {
        wxFAIL_MSG( _T("GetVersionEx() failed") ); // should never happen
    }

    return str;
}

bool wxIsPlatform64Bit()
{
#if defined(_WIN64)
    return true;  // 64-bit programs run only on Win64
#else // Win32
    // 32-bit programs run on both 32-bit and 64-bit Windows so check
    typedef BOOL (WINAPI *IsWow64Process_t)(HANDLE, BOOL *);

    wxDynamicLibrary dllKernel32(_T("kernel32.dll"));
    IsWow64Process_t pfnIsWow64Process =
        (IsWow64Process_t)dllKernel32.RawGetSymbol(_T("IsWow64Process"));

    BOOL wow64 = FALSE;
    if ( pfnIsWow64Process )
    {
        pfnIsWow64Process(::GetCurrentProcess(), &wow64);
    }
    //else: running under a system without Win64 support

    return wow64 != FALSE;
#endif // Win64/Win32
}

wxOperatingSystemId wxGetOsVersion(int *verMaj, int *verMin)
{
    OSVERSIONINFO info;
    wxZeroMemory(info);

    info.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
    if ( ::GetVersionEx(&info) )
    {
        if (verMaj) *verMaj = info.dwMajorVersion;
        if (verMin) *verMin = info.dwMinorVersion;
    }

#if defined( __WXWINCE__ )
    return wxOS_WINDOWS_CE;
#elif defined( __WXMICROWIN__ )
    return wxOS_WINDOWS_MICRO;
#else
    switch ( info.dwPlatformId )
    {
    case VER_PLATFORM_WIN32_NT:
        return wxOS_WINDOWS_NT;

    case VER_PLATFORM_WIN32_WINDOWS:
        return wxOS_WINDOWS_9X;
    }

    return wxOS_UNKNOWN;
#endif
}

wxWinVersion wxGetWinVersion()
{
    int verMaj,
        verMin;
    switch ( wxGetOsVersion(&verMaj, &verMin) )
    {
        case wxOS_WINDOWS_9X:
            if ( verMaj == 4 )
            {
                switch ( verMin )
                {
                    case 0:
                        return wxWinVersion_95;

                    case 10:
                        return wxWinVersion_98;

                    case 90:
                        return wxWinVersion_ME;
                }
            }
            break;

        case wxOS_WINDOWS_NT:
            switch ( verMaj )
            {
                case 3:
                    return wxWinVersion_NT3;

                case 4:
                    return wxWinVersion_NT4;

                case 5:
                    switch ( verMin )
                    {
                        case 0:
                            return wxWinVersion_2000;

                        case 1:
                            return wxWinVersion_XP;

                        case 2:
                            return wxWinVersion_2003;
                    }
                    break;

                case 6:
                    return wxWinVersion_NT6;
            }
            break;

        default:
            // Do nothing just to silence GCC warning
            break;
    }

    return wxWinVersion_Unknown;
}

// ----------------------------------------------------------------------------
// sleep functions
// ----------------------------------------------------------------------------

void wxMilliSleep(unsigned long milliseconds)
{
    ::Sleep(milliseconds);
}

void wxMicroSleep(unsigned long microseconds)
{
    wxMilliSleep(microseconds/1000);
}

void wxSleep(int nSecs)
{
    wxMilliSleep(1000*nSecs);
}

// ----------------------------------------------------------------------------
// font encoding <-> Win32 codepage conversion functions
// ----------------------------------------------------------------------------

extern WXDLLIMPEXP_BASE long wxEncodingToCharset(wxFontEncoding encoding)
{
    switch ( encoding )
    {
        // although this function is supposed to return an exact match, do do
        // some mappings here for the most common case of "standard" encoding
        case wxFONTENCODING_SYSTEM:
            return DEFAULT_CHARSET;

        case wxFONTENCODING_ISO8859_1:
        case wxFONTENCODING_ISO8859_15:
        case wxFONTENCODING_CP1252:
            return ANSI_CHARSET;

#if !defined(__WXMICROWIN__)
        // The following four fonts are multi-byte charsets
        case wxFONTENCODING_CP932:
            return SHIFTJIS_CHARSET;

        case wxFONTENCODING_CP936:
            return GB2312_CHARSET;

#ifndef __WXWINCE__
        case wxFONTENCODING_CP949:
            return HANGUL_CHARSET;
#endif

        case wxFONTENCODING_CP950:
            return CHINESEBIG5_CHARSET;

        // The rest are single byte encodings
        case wxFONTENCODING_CP1250:
            return EASTEUROPE_CHARSET;

        case wxFONTENCODING_CP1251:
            return RUSSIAN_CHARSET;

        case wxFONTENCODING_CP1253:
            return GREEK_CHARSET;

        case wxFONTENCODING_CP1254:
            return TURKISH_CHARSET;

        case wxFONTENCODING_CP1255:
            return HEBREW_CHARSET;

        case wxFONTENCODING_CP1256:
            return ARABIC_CHARSET;

        case wxFONTENCODING_CP1257:
            return BALTIC_CHARSET;

        case wxFONTENCODING_CP874:
            return THAI_CHARSET;
#endif // !__WXMICROWIN__

        case wxFONTENCODING_CP437:
            return OEM_CHARSET;

        default:
            // no way to translate this encoding into a Windows charset
            return -1;
    }
}

// we have 2 versions of wxCharsetToCodepage(): the old one which directly
// looks up the vlaues in the registry and the new one which is more
// politically correct and has more chances to work on other Windows versions
// as well but the old version is still needed for !wxUSE_FONTMAP case
#if wxUSE_FONTMAP

#include "wx/fontmap.h"

extern WXDLLIMPEXP_BASE long wxEncodingToCodepage(wxFontEncoding encoding)
{
    // There don't seem to be symbolic names for
    // these under Windows so I just copied the
    // values from MSDN.

    unsigned int ret;

    switch (encoding)
    {
        case wxFONTENCODING_ISO8859_1:      ret = 28591; break;
        case wxFONTENCODING_ISO8859_2:      ret = 28592; break;
        case wxFONTENCODING_ISO8859_3:      ret = 28593; break;
        case wxFONTENCODING_ISO8859_4:      ret = 28594; break;
        case wxFONTENCODING_ISO8859_5:      ret = 28595; break;
        case wxFONTENCODING_ISO8859_6:      ret = 28596; break;
        case wxFONTENCODING_ISO8859_7:      ret = 28597; break;
        case wxFONTENCODING_ISO8859_8:      ret = 28598; break;
        case wxFONTENCODING_ISO8859_9:      ret = 28599; break;
        case wxFONTENCODING_ISO8859_10:     ret = 28600; break;
        case wxFONTENCODING_ISO8859_11:     ret = 874; break;
        // case wxFONTENCODING_ISO8859_12,      // doesn't exist currently, but put it
        case wxFONTENCODING_ISO8859_13:     ret = 28603; break;
        // case wxFONTENCODING_ISO8859_14:     ret = 28604; break; // no correspondence on Windows
        case wxFONTENCODING_ISO8859_15:     ret = 28605; break;
        case wxFONTENCODING_KOI8:           ret = 20866; break;
        case wxFONTENCODING_KOI8_U:         ret = 21866; break;
        case wxFONTENCODING_CP437:          ret = 437; break;
        case wxFONTENCODING_CP850:          ret = 850; break;
        case wxFONTENCODING_CP852:          ret = 852; break;
        case wxFONTENCODING_CP855:          ret = 855; break;
        case wxFONTENCODING_CP866:          ret = 866; break;
        case wxFONTENCODING_CP874:          ret = 874; break;
        case wxFONTENCODING_CP932:          ret = 932; break;
        case wxFONTENCODING_CP936:          ret = 936; break;
        case wxFONTENCODING_CP949:          ret = 949; break;
        case wxFONTENCODING_CP950:          ret = 950; break;
        case wxFONTENCODING_CP1250:         ret = 1250; break;
        case wxFONTENCODING_CP1251:         ret = 1251; break;
        case wxFONTENCODING_CP1252:         ret = 1252; break;
        case wxFONTENCODING_CP1253:         ret = 1253; break;
        case wxFONTENCODING_CP1254:         ret = 1254; break;
        case wxFONTENCODING_CP1255:         ret = 1255; break;
        case wxFONTENCODING_CP1256:         ret = 1256; break;
        case wxFONTENCODING_CP1257:         ret = 1257; break;
        case wxFONTENCODING_EUC_JP:         ret = 20932; break;
        case wxFONTENCODING_MACROMAN:       ret = 10000; break;
        case wxFONTENCODING_MACJAPANESE:    ret = 10001; break;
        case wxFONTENCODING_MACCHINESETRAD: ret = 10002; break;
        case wxFONTENCODING_MACKOREAN:      ret = 10003; break;
        case wxFONTENCODING_MACARABIC:      ret = 10004; break;
        case wxFONTENCODING_MACHEBREW:      ret = 10005; break;
        case wxFONTENCODING_MACGREEK:       ret = 10006; break;
        case wxFONTENCODING_MACCYRILLIC:    ret = 10007; break;
        case wxFONTENCODING_MACTHAI:        ret = 10021; break;
        case wxFONTENCODING_MACCHINESESIMP: ret = 10008; break;
        case wxFONTENCODING_MACCENTRALEUR:  ret = 10029; break;
        case wxFONTENCODING_MACCROATIAN:    ret = 10082; break;
        case wxFONTENCODING_MACICELANDIC:   ret = 10079; break;
        case wxFONTENCODING_MACROMANIAN:    ret = 10009; break;
        case wxFONTENCODING_UTF7:           ret = 65000; break;
        case wxFONTENCODING_UTF8:           ret = 65001; break;
        default:                            return -1;
    }

    if (::IsValidCodePage(ret) == 0)
        return -1;

    CPINFO info;
    if (::GetCPInfo(ret, &info) == 0)
        return -1;

    return (long) ret;
}

extern long wxCharsetToCodepage(const wxChar *name)
{
    // first get the font encoding for this charset
    if ( !name )
        return -1;

    wxFontEncoding enc = wxFontMapperBase::Get()->CharsetToEncoding(name, false);
    if ( enc == wxFONTENCODING_SYSTEM )
        return -1;

    // the use the helper function
    return wxEncodingToCodepage(enc);
}

#else // !wxUSE_FONTMAP

#include "wx/msw/registry.h"

// this should work if Internet Exploiter is installed
extern long wxCharsetToCodepage(const wxChar *name)
{
    if (!name)
        return GetACP();

    long CP = -1;

    wxString path(wxT("MIME\\Database\\Charset\\"));
    wxString cn(name);

    // follow the alias loop
    for ( ;; )
    {
        wxRegKey key(wxRegKey::HKCR, path + cn);

        if (!key.Exists())
            break;

        // two cases: either there's an AliasForCharset string,
        // or there are Codepage and InternetEncoding dwords.
        // The InternetEncoding gives us the actual encoding,
        // the Codepage just says which Windows character set to
        // use when displaying the data.
        if (key.HasValue(wxT("InternetEncoding")) &&
            key.QueryValue(wxT("InternetEncoding"), &CP))
            break;

        // no encoding, see if it's an alias
        if (!key.HasValue(wxT("AliasForCharset")) ||
            !key.QueryValue(wxT("AliasForCharset"), cn))
            break;
    }

    return CP;
}

#endif // wxUSE_FONTMAP/!wxUSE_FONTMAP

/*
  Creates a hidden window with supplied window proc registering the class for
  it if necesssary (i.e. the first time only). Caller is responsible for
  destroying the window and unregistering the class (note that this must be
  done because wxWidgets may be used as a DLL and so may be loaded/unloaded
  multiple times into/from the same process so we cna't rely on automatic
  Windows class unregistration).

  pclassname is a pointer to a caller stored classname, which must initially be
  NULL. classname is the desired wndclass classname. If function successfully
  registers the class, pclassname will be set to classname.
 */
extern "C" WXDLLIMPEXP_BASE HWND
wxCreateHiddenWindow(LPCTSTR *pclassname, LPCTSTR classname, WNDPROC wndproc)
{
    wxCHECK_MSG( classname && pclassname && wndproc, NULL,
                    _T("NULL parameter in wxCreateHiddenWindow") );

    // register the class fi we need to first
    if ( *pclassname == NULL )
    {
        WNDCLASS wndclass;
        wxZeroMemory(wndclass);

        wndclass.lpfnWndProc   = wndproc;
        wndclass.hInstance     = wxGetInstance();
        wndclass.lpszClassName = classname;

        if ( !::RegisterClass(&wndclass) )
        {
            wxLogLastError(wxT("RegisterClass() in wxCreateHiddenWindow"));

            return NULL;
        }

        *pclassname = classname;
    }

    // next create the window
    HWND hwnd = ::CreateWindow
                  (
                    *pclassname,
                    NULL,
                    0, 0, 0, 0,
                    0,
                    (HWND) NULL,
                    (HMENU)NULL,
                    wxGetInstance(),
                    (LPVOID) NULL
                  );

    if ( !hwnd )
    {
        wxLogLastError(wxT("CreateWindow() in wxCreateHiddenWindow"));
    }

    return hwnd;
}
