/***************************************************************************

  win32.c

  OS dependent stuff

 ***************************************************************************/

#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <windowsx.h>
#include <shellapi.h>
#include <assert.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>

#include "driver.h"
#include "osdepend.h"
#include "mame.h"
#include "common.h"
#include "usrintrf.h"
#include "datafile.h"
#include "info.h"
#include "unzip.h"
#include "mzip.h"

#include "win32.h"
#include "win32ui.h"
#include "misc.h"

#include "DirectDraw.h"
#include "DirectInput.h"

#include "config.h"
#include "video.h"
#include "sound.h"
#include "mouse.h"
#include "Keyboard.h"
#include "Joystick.h"
#include "DIMouse.h"
#include "DIKeyboard.h"
#include "DIJoystick.h"
#include "ticker.h"
#include "fileio.h"
#include "tsv.h"
#include "resource.h"

#ifdef KAILLERA
#include "kailleraclient.h"
#endif /* KAILLERA */

/***************************************************************************
    Function prototypes
 ***************************************************************************/

#ifndef BUILD_CONSOLE
static int               decode_commandline(LPSTR lpCmdLine);
#endif

static LRESULT CALLBACK  MAME32_MessageProc(HWND, UINT, WPARAM, LPARAM);
static void              MAME32_ProcessMessages(void);
static BOOL              MAME32_PumpAndReturnMessage(MSG *pMsg);
static void              MAME32_HandleAutoPause(void);
static void              MAME32_Quit(void);
static BOOL              MAME32_DetectMMX(void);
static BOOL              MAME32_Done(void);

static BOOL              OnMessage(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam, LRESULT *pResult);
static void              OnActivateApp(HWND hWnd, BOOL fActivate, DWORD dwThreadId);
static void              OnSysCommand(HWND hWnd, UINT cmd, int x, int y);
static void              OnClose(HWND hWnd);

static INT_PTR CALLBACK DirectXDialogProc(HWND hDlg, UINT Msg, WPARAM wParam, LPARAM lParam);

static HICON             LoadGameIcon(int index);

static int               fuzzycmp(const char *s, const char *l);
static void              show_fuzzy_matches(char *gamename, char *buf);

#ifdef BUILD_CONSOLE
extern int               frontend_help(char *gamename, int argc, char *argv[]);
#endif
extern int __cdecl       CheckCPUID(void);

#ifdef KAILLERA
extern int kPlay;
#endif /* KAILLERA */

/***************************************************************************
    External variables
 ***************************************************************************/

struct tMAME32App MAME32App =
{
    NULL,                           /* m_hwndUI             */
    NULL,                           /* m_hWnd               */
    NULL,                           /* m_hIcon              */
    NULL,                           /* m_hCursor            */
    MAME32NAME,                     /* m_pName              */
    NULL,                           /* m_Options            */

    FALSE,                          /* m_bIsInitialized     */
    FALSE,                          /* m_bIsActive          */
    FALSE,                          /* m_bAutoPaused        */
    FALSE,                          /* m_bMamePaused        */
    FALSE,                          /* m_bDone              */
    0,                              /* m_nMMXDetected       */

    FALSE,                          /* m_bUseAIMouse        */

    NULL,                           /* m_pKeyboard          */
    NULL,                           /* m_pJoystick          */
    NULL,                           /* m_pMouse              */

    MAME32_ProcessMessages,         /* ProcessMessages      */
    MAME32_PumpAndReturnMessage,    /* PumpAndReturnMessage */
    MAME32_HandleAutoPause,         /* HandleAutoPause      */
    MAME32_Quit,                    /* Quit                 */
    MAME32_DetectMMX,               /* detect MMX           */
    MAME32_Done                     /* close was clicked    */
};

#ifdef KAILLERA
extern int kPlay;
#ifdef MAME32JP
int WINAPI (*kailleraGetVersion)(char *);
int WINAPI (*kailleraInit)(void);
int WINAPI (*kailleraShutdown)(void);
int WINAPI (*kailleraSetInfos)(kailleraInfos *);
int WINAPI (*kailleraSelectServerDialog)(HWND);
int WINAPI (*kailleraModifyPlayValues)(void *, int);
int WINAPI (*kailleraChatSend)(char *);
int WINAPI (*kailleraEndGame)(void);

HINSTANCE hKailleraDLL = NULL;
#endif
#endif

/***************************************************************************
    Internal variables
 ***************************************************************************/

#ifndef BUILD_CONSOLE
static char         *argv[256];
#endif
static int          game_count;
static FILE         *errorlog;
static BOOL         auto_pause;
static options_type playing_game_options;

/***************************************************************************
    Internal functions
 ***************************************************************************/

#ifdef KAILLERA
#ifdef MAME32JP
static int WINAPI kailleraNullFunc0(void)
{
	return 0;
}
static int WINAPI kailleraNullFunc1(char *version)
{
	return 0;
}
static int WINAPI kailleraNullFunc2(kailleraInfos *infos)
{
	return 0;
}

static int WINAPI kailleraNullFunc3(HWND parent)
{
	return 0;
}
static int WINAPI kailleraNullFunc4(void *values, int size)
{
	return 0;
}
static void KailleraFuncReset(void)
{
	kailleraGetVersion			 = kailleraNullFunc1;
	kailleraInit				 = kailleraNullFunc0;
	kailleraShutdown			 = kailleraNullFunc0;
	kailleraSetInfos			 = kailleraNullFunc2;
	kailleraSelectServerDialog	 = kailleraNullFunc3;
	kailleraModifyPlayValues	 = kailleraNullFunc4;
	kailleraChatSend			 = kailleraNullFunc1;
	kailleraEndGame				 = kailleraNullFunc0;
}

static void Kaillera_Load(void)
{
	if(hKailleraDLL) {
		FreeLibrary(hKailleraDLL);
	}
	KailleraFuncReset();
	if ((hKailleraDLL=LoadLibrary("kailleraclient.dll")) == NULL) {
		return;
	}
	kailleraGetVersion			= GetProcAddress(hKailleraDLL, "_kailleraGetVersion@4");
	kailleraInit				= GetProcAddress(hKailleraDLL, "_kailleraInit@0");
	kailleraShutdown			= GetProcAddress(hKailleraDLL, "_kailleraShutdown@0");
	kailleraSetInfos			= GetProcAddress(hKailleraDLL, "_kailleraSetInfos@4");
	kailleraSelectServerDialog	= GetProcAddress(hKailleraDLL, "_kailleraSelectServerDialog@4");
	kailleraModifyPlayValues	= GetProcAddress(hKailleraDLL, "_kailleraModifyPlayValues@8");
	kailleraChatSend			= GetProcAddress(hKailleraDLL, "_kailleraChatSend@4");
	kailleraEndGame				= GetProcAddress(hKailleraDLL, "_kailleraEndGame@0");

	if(!kailleraGetVersion || !kailleraInit ||!kailleraShutdown ||!kailleraSetInfos ||!kailleraSelectServerDialog ||!kailleraModifyPlayValues ||!kailleraChatSend ||!kailleraEndGame) {
		FreeLibrary(hKailleraDLL);
		KailleraFuncReset();
		hKailleraDLL = NULL;
	}
}

static void Kaillera_Unload(void)
{
	if(hKailleraDLL) {
		FreeLibrary(hKailleraDLL);
	}
	KailleraFuncReset();
	hKailleraDLL = NULL;
}
#endif
#endif

/***************************************************************************
    External functions
 ***************************************************************************/

#ifdef BUILD_CONSOLE
int main(int argc, char * argv[])
{
    int   i;
    int   game_index = -1;
    int   res = 0;
    char  *gamename = NULL;

    MAME32App.m_Options = &playing_game_options;

    game_count = 0;
    while (drivers[game_count] != 0)
        game_count++;

    if (argc >= 2 && argv[1][0] != '-')
        gamename = argv[1];

    InitZip(NULL);
    LoadTSV(game_count);
    win32_config_init(game_count);
    win32_file_init();

    res = frontend_help(gamename, argc, argv);
    if (res != 1234)
        goto error_exit;

    if (gamename != NULL)
    {
        if (game_index == -1)
        {
            for (i = 0; drivers[i]; i++)
            {
                if (stricmp(gamename, drivers[i]->name) == 0)
                    game_index = i;
            }
        }

        /* we give up. print a few fuzzy matches */
        if (game_index == -1)
        {
            char buf[1024];

#ifdef JAPANESE
            printf("\"%s\" というゲームには対応していません。\n\n", gamename);
            printf("類似した名前の以下のゲームがサポートされています:\n");
#else
            printf("game \"%s\" not supported\n\n", gamename);
            printf("the name fuzzy matches the following supported games:\n");
#endif
            show_fuzzy_matches(gamename, buf);
            printf(buf);
            res = 1;
        }
        else
        {
            options.record     = NULL;
            options.playback   = NULL;
            options.game_index = game_index;

            ParseCommandLine(argc, argv, game_index, 0);
            SetPlayingGameOptions(game_index);

            if (!DirectDraw_Initialize())
            {
                DialogBox(GetModuleHandle(NULL), MAKEINTRESOURCE(IDD_DIRECTX), NULL, DirectXDialogProc);
                goto error_exit;
            }

            if (!DirectInputInitialize())
            {
                DialogBox(GetModuleHandle(NULL),MAKEINTRESOURCE(IDD_DIRECTX), NULL, DirectXDialogProc);
                DirectDraw_Close();
                goto error_exit;
            }

            if (run_game(game_index) == 0)
            {
#ifdef MAME32JP
                options_type *o = GetGameOptions(game_index);
                o->autofiredelay = options.autofiredelay;
#endif
                IncrementPlayCount(game_index);
                res = 0;
            }
            else
                res = 1;

            DirectInputClose();
            DirectDraw_Close();
        }
    }

error_exit:
    unzip_cache_clear();
    free_datafile();

    win32_file_exit();
    win32_config_exit();
    SaveTSV();
    ExitZip();

    return 0;
}
#else
int WINAPI WinMain(HINSTANCE hInstance,
                   HINSTANCE hPrevInstance,
                   LPSTR     lpCmdLine,
                   int       nCmdShow)
{
    int   i;
    int   argc;
    int   game_index = -1;
    int   res = 0;
    BOOL  ignoregui = 0;
    char  *gamename = NULL;

    MAME32App.m_Options = &playing_game_options;

    game_count = 0;
    while (drivers[game_count] != 0)
        game_count++;

    argc = decode_commandline(lpCmdLine);

    if (argc >= 2 && argv[1][0] != '-')
        gamename = argv[1];

    for (i = 1; argv[i]; i++)
    {
        if (!stricmp(argv[i], "-nogui"))
            ignoregui = 1;

        if (!stricmp(argv[i], "-listinfo"))
        {
            print_mame_info(stdout, drivers);
            return 0;
        }
    }

    InitZip(NULL);
    LoadTSV(game_count);
    win32_config_init(game_count);
    win32_file_init();

#ifdef KAILLERA
#ifdef MAME32JP
	Kaillera_Load();
#endif
    kailleraInit();
#endif

    if (!DirectDraw_Initialize())
    {
        DialogBox(GetModuleHandle(NULL), MAKEINTRESOURCE(IDD_DIRECTX), NULL, DirectXDialogProc);
        goto error_exit;
    }

    if (!DirectInputInitialize())
    {
        DialogBox(GetModuleHandle(NULL),MAKEINTRESOURCE(IDD_DIRECTX), NULL, DirectXDialogProc);
        goto error_exit;
    }

    if (ignoregui || gamename)
    {
        if (gamename != NULL)
        {
            if (game_index == -1)
            {
                for (i = 0; drivers[i]; i++)
                {
                    if (stricmp(gamename, drivers[i]->name) == 0)
                        game_index = i;
                }
            }

            /* we give up. print a few fuzzy matches */
            if (game_index == -1)
            {
                char buf[1024];

#ifdef JAPANESE
                sprintf(buf, "\"%s\" というゲームには対応していません。\n\n", gamename);
                strcat(buf, "類似した名前の以下のゲームがサポートされています:\n");
#else
                sprintf(buf, "game \"%s\" not supported\n\n", gamename);
                strcat(buf, "the name fuzzy matches the following supported games:\n");
#endif
                show_fuzzy_matches(gamename, buf);
                MessageBox(NULL, buf, "未対応のゲーム", MB_OK);
                res = 1;
            }
            else
            {
                options.record     = NULL;
                options.playback   = NULL;
                options.game_index = game_index;

                ParseCommandLine(argc, argv, game_index, 0);
                SetPlayingGameOptions(game_index);

                if (run_game(game_index) == 0)
                {
#ifdef MAME32JP
                    options_type *o = GetGameOptions(game_index);
                    o->autofiredelay = options.autofiredelay;
#endif
                    IncrementPlayCount(game_index);
                    res = 0;
                }
                else
                {
                    res = 1;
                }
            }
        }
    }
    else
    {
        srand((unsigned)time(NULL));
        res = MAME32UI(hInstance, argc, argv, nCmdShow);
    }

error_exit:
    unzip_cache_clear();
    free_datafile();

    DirectInputClose();
    DirectDraw_Close();

#ifdef KAILLERA
    kailleraShutdown();
#ifdef MAME32JP
    Kaillera_Unload();
#endif
#endif

    win32_file_exit();
    win32_config_exit();
    SaveTSV();
    ExitZip();

    return 0;
}
#endif


int osd_init(void)
{
#ifdef MAME32JP
	extern HICON GetGameIcon(int index);
#endif

    WNDCLASS     WndClass;
    HINSTANCE    hInstance;
    BOOL         bUseAIMouse;
    BOOL         bUseDIMouse;
    BOOL         bUseDIKeyboard;
    BOOL         bUseDIJoystick;
    BOOL         bUseDJoystick;
    options_type *osd_options;


    bUseAIMouse    = FALSE;
    bUseDIMouse    = FALSE;
    bUseDIKeyboard = FALSE;
    bUseDIJoystick = FALSE;
    bUseDJoystick = FALSE;
    osd_options    = MAME32App.m_Options;
    hInstance      = GetModuleHandle(NULL);

    MAME32App.m_hWnd           = NULL;
    MAME32App.m_bIsInitialized = FALSE;
    MAME32App.m_bIsActive      = FALSE;
    MAME32App.m_bAutoPaused    = FALSE;
    MAME32App.m_bMamePaused    = FALSE;
    MAME32App.m_pKeyboard      = NULL;
    MAME32App.m_pJoystick      = NULL;
    MAME32App.m_pMouse          = NULL;
    MAME32App.m_bDone          = FALSE;
    MAME32App.m_hCursor        = LoadCursor(NULL, IDC_ARROW);
    MAME32App.m_hIcon          = LoadGameIcon(options.game_index);
#ifdef MAME32JP
    MAME32App.m_hIcon          = GetGameIcon(options.game_index);
#endif
    if (MAME32App.m_hIcon == NULL)
        MAME32App.m_hIcon = LoadIcon(hInstance, MAKEINTATOM(IDI_MAME32_ICON));

    if (osd_options->errorlog)
        errorlog = fopen("error.log", "wa");
    else
        errorlog = NULL;

    if (osd_options->use_mouse)
        bUseAIMouse = TRUE;

    if (osd_options->di_mouse)
        bUseDIMouse = TRUE;

    if (osd_options->di_keyboard)
        bUseDIKeyboard = TRUE;

    if (osd_options->di_joystick)
        bUseDIJoystick = TRUE;

    if (osd_options->use_djoystick)
        bUseDJoystick = TRUE;

#if defined(KAILLERA)
    auto_pause = kPlay ? FALSE : osd_options->auto_pause;
#elif defined(MAME_DEBUG)
    auto_pause = FALSE;
#else
    auto_pause = osd_options->auto_pause;
#endif

    MAME32App.m_bUseAIMouse = bUseAIMouse;

    if (bUseDIMouse == TRUE)
        MAME32App.m_pMouse = &DIMouse;
    else
        MAME32App.m_pMouse = &Trak;

    if (bUseDIKeyboard == TRUE)
        MAME32App.m_pKeyboard = &DIKeyboard;
    else
        MAME32App.m_pKeyboard = &Keyboard;

    if (bUseDIJoystick == TRUE)
        MAME32App.m_pJoystick = &DIJoystick;
    else
        MAME32App.m_pJoystick = &Joystick;

    WndClass.style          = CS_SAVEBITS | CS_BYTEALIGNCLIENT | CS_OWNDC;
    WndClass.lpfnWndProc    = MAME32_MessageProc;
    WndClass.cbClsExtra     = 0;
    WndClass.cbWndExtra     = 0;
    WndClass.hInstance      = hInstance;
    WndClass.hIcon          = MAME32App.m_hIcon;
    WndClass.hCursor        = MAME32App.m_hCursor;
    WndClass.hbrBackground  = (HBRUSH)GetStockObject(NULL_BRUSH);
    WndClass.lpszMenuName   = NULL;
    WndClass.lpszClassName  = (LPCSTR)"classMAME32";

    if (RegisterClass(&WndClass) == 0)
        return 1;

    MAME32App.m_hWnd = CreateWindowEx(0,
                          "classMAME32",
                          MAME32App.m_Name,
                          (WS_OVERLAPPEDWINDOW & ~WS_THICKFRAME) | WS_BORDER,
                          CW_USEDEFAULT,
                          CW_USEDEFAULT,
                          328,228,
                          NULL,
                          NULL,
                          hInstance,
                          NULL);

    if (MAME32App.m_hWnd != NULL)
    {
        ticker_init(osd_options);
        win32_sound_init(osd_options);

        if (win32_video_init(osd_options) == 0)
        {

            if (MAME32App.m_pKeyboard->init(osd_options) == 0)
            {
                if (MAME32App.m_pJoystick->init(osd_options) == 0)
                {
                    if (MAME32App.m_pMouse->init(osd_options) == 0)
                    {
                        osd_set_mastervolume(-osd_options->attenuation);
                        return 0;
                    }
                    MAME32App.m_pJoystick->exit();
                }
                MAME32App.m_pKeyboard->exit();
            }
            win32_video_exit();
        }

        win32_sound_exit();
        ticker_exit();

        ShowWindow(MAME32App.m_hWnd, SW_SHOW);
        DestroyWindow(MAME32App.m_hWnd);
    }

    return 1;
}


void osd_exit(void)
{
    MAME32App.m_bIsInitialized = FALSE;
    MAME32App.m_bDone = FALSE;

    if (MAME32App.m_hWnd != NULL)
    {
        if (MAME32App.m_pMouse    != NULL) MAME32App.m_pMouse->exit();
        if (MAME32App.m_pJoystick != NULL) MAME32App.m_pJoystick->exit();
        if (MAME32App.m_pKeyboard != NULL) MAME32App.m_pKeyboard->exit();

        win32_video_exit();
        win32_sound_exit();
        ticker_exit();

        ShowWindow(MAME32App.m_hWnd, SW_SHOW);
        DestroyWindow(MAME32App.m_hWnd);
        UnregisterClass((LPCSTR)"classMAME32", GetModuleHandle(NULL));

        DestroyIcon(MAME32App.m_hIcon);
        DestroyCursor(MAME32App.m_hCursor);
    }

#ifndef JAPANESE
    if (options.language_file)
    {
        osd_fclose(options.language_file);
        options.language_file = NULL;
    }
#endif

    if (errorlog)
        fclose(errorlog);
    errorlog = NULL;
}


int osd_quit_window(void)
{
    if (MAME32App.Done())
    {
        extern int time_to_quit;

        time_to_quit = 1;
        return 1;
    }
    return 0;
}


void CLIB_DECL logerror(const char *text, ...)
{
    va_list arg;

    if (!errorlog) return;

    va_start(arg, text);
    vfprintf(errorlog, text, arg);
    va_end(arg);
}


int get_game_count(void)
{
    return game_count;
}

/***************************************************************************
    Internal functions
 ***************************************************************************/

#ifndef BUILD_CONSOLE
static int decode_commandline(LPSTR lpCmdLine)
{
    int argc;
    static char command_line[4096];

    argc = 1;
    strcpy(command_line, lpCmdLine);
    argv[argc] = strtok(command_line, " \t\n");

    if (argv[1] != NULL)
    {
        argc++;
        while ((argv[argc] = strtok(NULL, " \t\n")) != NULL)
            argc++;
    }

    return argc;
}
#endif

static void MAME32_ProcessMessages(void)
{
    MSG Msg;

    while (PeekMessage(&Msg, NULL, 0, 0, PM_REMOVE))
    {
        if (Msg.message == WM_QUIT)
        {
            MAME32App.Quit();
            return;
        }

        TranslateMessage(&Msg);
        DispatchMessage(&Msg);
    }
}


static BOOL MAME32_PumpAndReturnMessage(MSG *pMsg)
{
    if (!(GetMessage(pMsg, NULL, 0, 0)))
        return FALSE;

    TranslateMessage(pMsg);
    DispatchMessage(pMsg);
    return TRUE;
}


static void MAME32_HandleAutoPause(void)
{
    if (MAME32App.m_bAutoPaused == TRUE)
    {
        MSG msg;
        /* Go into 'nice' Pause loop until re-activated. */
        while (GetMessage(&msg, NULL, 0, 0) != 0)
        {
            DispatchMessage(&msg);

            if (msg.message == WM_QUIT)
            {
                MAME32App.Quit();
                return;
            }

            if (MAME32App.m_bAutoPaused == FALSE)
                return;
        }
    }
}

static void MAME32_Quit(void)
{
    /* Tell MAME to quit. */
    MAME32App.m_bDone = TRUE;
}


static BOOL MAME32_Done(void)
{
    return MAME32App.m_bDone;
}


static int MAME32_DetectMMX(void)
{
    return CheckCPUID();
}


static LRESULT CALLBACK MAME32_MessageProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam)
{
    LRESULT Result = 0;

    if (OnMessage(hWnd, Msg, wParam, lParam, &Result) == TRUE)
        return Result;

    if (DirectDraw_OnMessage(hWnd, Msg, wParam, lParam, &Result) == TRUE)
        return Result;

    if (MAME32App.m_pMouse != NULL)
        if (MAME32App.m_pMouse->OnMessage(hWnd, Msg, wParam, lParam, &Result) == TRUE)
            return Result;

    if (MAME32App.m_pKeyboard->OnMessage(hWnd, Msg, wParam, lParam, &Result) == TRUE)
        return Result;

    if (MAME32App.m_pJoystick->OnMessage(hWnd, Msg, wParam, lParam, &Result) == TRUE)
        return Result;

    return DefWindowProc(hWnd, Msg, wParam, lParam);
}


static BOOL OnMessage(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam, LRESULT* pResult)
{
    if (Msg == WM_SYSCOMMAND)
    {
        int  cmd = (int)wParam;

        if (cmd >= IDM_FULLSCREEN && cmd <= IDM_DDEMULATION)
            return DirectDraw_OnMessage(hWnd, Msg, wParam, lParam, pResult);
    }

    switch (Msg)
    {
        PEEK_MESSAGE(hWnd, WM_ACTIVATEAPP,    OnActivateApp);
        HANDLE_MESSAGE(hWnd, WM_SYSCOMMAND,   OnSysCommand);
        HANDLE_MESSAGE(hWnd, WM_CLOSE,        OnClose);
    }
    return FALSE;
}


static void OnActivateApp(HWND hWnd, BOOL fActivate, DWORD dwThreadId)
{
    static float orig_brt;

    MAME32App.m_bIsActive = fActivate && MAME32App.m_hWnd == hWnd;

    if (MAME32App.m_bIsInitialized == FALSE)
        return;

    if (MAME32App.m_bIsActive == FALSE)
    {
        if (auto_pause && MAME32App.m_bAutoPaused == FALSE)
        {
            MAME32App.m_bAutoPaused = TRUE;

            if (MAME32App.m_bMamePaused == FALSE)
            {
                orig_brt = osd_get_brightness();
                osd_set_brightness(orig_brt * 0.65);
                osd_refresh_display();
                osd_sound_enable(0);
            }
        }
    }
    else
    {
        if (auto_pause && MAME32App.m_bAutoPaused == TRUE)
        {
            MAME32App.m_bAutoPaused = FALSE;

            if (MAME32App.m_bMamePaused == FALSE)
            {
                osd_set_brightness(orig_brt);
                osd_sound_enable(1);
            }

            osd_refresh_display();
        }
    }
}


static void OnSysCommand(HWND hWnd, UINT cmd, int x, int y)
{
    if (cmd != SC_KEYMENU || MAME32App.m_bMamePaused == TRUE)
        FORWARD_WM_SYSCOMMAND(hWnd, cmd, x, y, DefWindowProc);
}


static void OnClose(HWND hWnd)
{
#ifndef KAILLERA
    /* This makes sure we don't autopause after the call below. */
    MAME32App.m_bIsInitialized = FALSE;

    /* hide window to prevent orphan empty rectangles on the taskbar */
    ShowWindow(hWnd, SW_HIDE);
#endif /* KAILLERA */

    /* Don't call DestroyWindow, it will be called by osd_exit. */
    MAME32App.Quit();
}


static HICON LoadGameIcon(int index)
{
    HICON hIcon = NULL;

    if ((hIcon = LoadIconFromFile((char *)drivers[index]->name)) == 0)
    {
        if (drivers[index]->clone_of != 0)
        {
            hIcon = LoadIconFromFile((char *)drivers[index]->clone_of->name);
            if (!hIcon && drivers[index]->clone_of->clone_of)
                hIcon = LoadIconFromFile((char *)drivers[index]->clone_of->clone_of->name);
        }
    }
    return hIcon;
}


/* fuzzy string compare, compare short string against long string        */
/* e.g. astdel == "Asteroids Deluxe". The return code is the fuzz index, */
/* we simply count the gaps between maching chars.                       */
static int fuzzycmp(const char *s, const char *l)
{
    int gaps = 0;
    int match = 0;
    int last = 1;

    for (; *s && *l; l++)
    {
        if (*s == *l)
            match = 1;
        else if (*s >= 'a' && *s <= 'z' && (*s - 'a') == (*l - 'A'))
            match = 1;
        else if (*s >= 'A' && *s <= 'Z' && (*s - 'A') == (*l - 'a'))
            match = 1;
        else
            match = 0;

        if (match)
            s++;

        if (match != last)
        {
            last = match;
            if (!match)
                gaps++;
        }
    }

    /* penalty if short string does not completely fit in */
    for (; *s; s++)
        gaps++;

    return gaps;
}

static void show_fuzzy_matches(char *gamename, char *buf)
{
    struct { int fuzz; int index; } topten[10];
    int i,j;
    int fuzz; /* best fuzz factor so far */

    for (i = 0; i < 10; i++)
    {
        topten[i].fuzz = 9999;
        topten[i].index = -1;
    }

    for (i = 0; (drivers[i] != 0); i++)
    {
        fuzz = fuzzycmp(gamename, drivers[i]->description);
        for (j = 0; j < 10; j++)
        {
            if (fuzz >= topten[j].fuzz) break;
            if (j > 0)
            {
                topten[j-1].fuzz = topten[j].fuzz;
                topten[j-1].index = topten[j].index;
            }
            topten[j].index = i;
            topten[j].fuzz = fuzz;
        }
    }

    for (i = 0; i < 10; i++)
    {
        if (topten[i].index != -1)
        {
            char tmp[512];

            sprintf(tmp, "%-10s%s\n", drivers[topten[i].index]->name, drivers[topten[i].index]->description);
            strcat(buf, tmp);
        }
    }
}

static INT_PTR CALLBACK DirectXDialogProc(HWND hDlg, UINT Msg, WPARAM wParam, LPARAM lParam)
{
   HWND hEdit;

   char *directx_help =
#ifdef JAPANESE
       MAME32NAME " は、Microsoft Windows 9x/Me/NT/2000のOS拡張セットであるDirectX\n"
       "バージョン3.0以降が必要です。\n\n"
       "MicrosoftのDirectXホームページ(http://www.microsoft.com/japan/directx)\n"
       "からDirectXをダウンロードし、インストール後に " MAME32NAME " を起動してください。";
#else
       MAME32NAME " requires DirectX version 3 or later, which is a set of operating\r\n"
       "system extensions by Microsoft for Windows 9x, Me, NT and 2000.\r\n\r\n"
       "Visit Microsoft's DirectX web page at http://www.microsoft.com/directx\r\n"
       "download DirectX, install it, and then run " MAME32NAME " again.\r\n";
#endif

    switch (Msg)
    {
    case WM_INITDIALOG:
        hEdit = GetDlgItem(hDlg, IDC_DIRECTX_HELP);
        Edit_SetSel(hEdit, Edit_GetTextLength(hEdit), Edit_GetTextLength(hEdit));
        Edit_ReplaceSel(hEdit, directx_help);
        return 1;

    case WM_COMMAND:
        if (LOWORD(wParam) == IDC_WEB_PAGE)
#ifdef JAPANESE
            ShellExecute(hDlg, NULL, "http://www.microsoft.com/japan/directx", NULL, NULL, SW_SHOWNORMAL);
#else
            ShellExecute(hDlg, NULL, "http://www.microsoft.com/directx", NULL, NULL, SW_SHOWNORMAL);
#endif

        if (LOWORD(wParam) == IDCANCEL
        ||  LOWORD(wParam) == IDC_WEB_PAGE)
            EndDialog(hDlg, 0);
        return 1;
    }
    return 0;
}
