/* jvm.cpp
   Copyright (C) 2005, 2006 Free Software Foundation, Inc.

This file is part of Mysaifu JVM

Mysaifu JVM is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.

Mysaifu JVM is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
General Public License for more details.

You should have received a copy of the GNU General Public License
along with GNU Classpath; see the file COPYING.  If not, write to the
Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
02111-1307 USA.
*/

#include <windows.h>
#include <stdio.h>
#include <commctrl.h>
#include <sipapi.h>
#include "stdafx.h"
#include "resource.h"
#include "class_loader.h"
#include "ClassFile.h"
#include "instruction.h"
#include "class_loader.h"
#include "monitor.h"
#include "java_utf8.h"
#include "jni_funcs.h"
#include "java_thread.h"
#include "common_funcs.h"
#include "console.h"
#include "arguments.h"
#include <Commdlg.h>
#include "folder_dialog.h"

/**
 * ショートカットディレクトリ
 */
#define SHORTCUT_DIRECTORY_FORMAT	_T("%s\\recent")

/**
 * コントローラウインドウのウインドウクラス名
 */
#define	CONTROLLER_WINDOW_CLASS_NAME	_T("Mysaifu JVM")

/**
 * コントローラウインドウ用のメッセージ
 */
#define JVM_MESSAGE_START_JVM	(WM_APP+1)
#define JVM_MESSAGE_PREPARE		(WM_APP+2)

/**
 * まだ何も起動していないときに、ControllerWindowに設定するタイトル
 */
#define	NOT_STARTED_TITLE	_T("Mysaifu JVM")

/**
 * argumentsダイアログボックスのタイトル
 */
#define ARGUMENTS_DIALOG_TITLE	_T("Mysaifu JVM")

/**
 * インスタンス
 */
static HINSTANCE g_hInstance;

/**
 * コントローラウインドウのハンドル
 */
static HWND g_hwndController;

//static HWND g_hwndMainDialog;

/**
 * メインスレッド
 */
DWORD WINAPI main_routine(LPVOID pvarg);

/**
 * argumentsをショートカットディレクトリから読み込む
 */
static int load_recent_arguments(const _TCHAR* shortcut_dir, arguments*** args);
static arguments* load_arguments_from(const _TCHAR* filename);

/**
 * 指定された arguments をショートカットとして保存する
 */
bool save_arguments(const _TCHAR* shortcut_dir, arguments* config);

/**
 * プログラム終了時に実行されるハンドラ
 */
static void exit_handler();

/**
 * 引数入力ダイアログを表示する
 */
static int show_arguments_dialog(HWND hwndOwner, arguments** args);

/**b
 * シャットダウンフックを実行する
 */
static bool run_shutdown_hooks();

/**
 * 指定されたリストビューに、システムプロパティを追加する
 * 同一のキーを持つ行がすでに存在していた場合、内容を置き換える
 */
static void put_system_property(HWND hwndListView, _TCHAR* key, _TCHAR* value);

static void show_options_dialog(HWND hwndParent, arguments* args);

/**
 * ファイルの関連付けを初期化する
 */
static void init_file_associations();

/**
 * 「ファイルを開く」ダイアログを表示する
 */
static BOOL get_open_file_name(OPENFILENAME* ofn);

/**
 * 「ファイルを保存」ダイアログを表示する
 */
static BOOL get_save_file_name(OPENFILENAME* ofn);

/**
 * コールバック関数
 */
LRESULT CALLBACK ControllerWindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
LRESULT CALLBACK ArgumentsDialogProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
LRESULT CALLBACK SystemPropertyEditorDialogProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
LRESULT CALLBACK JVMListDialogProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
LRESULT CALLBACK AboutDialogProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
BOOL CALLBACK EnumControllerWindowsProc(HWND hwnd, LPARAM lParam);

// Optionsダイアログのプロパティシートページ
LRESULT CALLBACK GeneralDialogProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
LRESULT CALLBACK MemoryDialogProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
LRESULT CALLBACK PropertiesDialogProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
LRESULT CALLBACK VerifierDialogProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
LRESULT CALLBACK MiscDialogProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);

// Optionsプロパティシートのコールバック
int PropSheetProc(HWND hwnd, UINT uMsg, LPARAM lParam);

/**
 * ファイルの「最終更新日付」を比較する関数
 */
static int compare_last_write_time(WIN32_FIND_DATA* data1, WIN32_FIND_DATA* data2);

#include <Cmnintrin.h>

/**
 * WinMain
 */
int WINAPI WinMain(	HINSTANCE hInstance,
					HINSTANCE hPrevInstance,
					LPTSTR    lpCmdLine,
					int       nCmdShow)
{
	// 他のダイアログがオープンしている場合は、そのウインドウに切り替えてアプリケーションを終了する
	{
		HWND hwndOtherJVM = FindWindow(_T("Dialog"), ARGUMENTS_DIALOG_TITLE);
		if (hwndOtherJVM) {
			SetForegroundWindow(hwndOtherJVM);
			return 0;
		}
	}

	// hInstanceを保存する
	g_hInstance = hInstance;
	
	// コモンコントロールを初期化する
	InitCommonControls();

	// 入力ダイアログにSIPPREFコントロールを使用しているため、ここで初期化しておく
	SHInitExtraControls();

	// 拡張子 .mysaifu をこのファイルに関連付ける
	init_file_associations();

	// コントローラウインドウを作成し、メッセージループに入る
	WNDCLASS wc = {0};
	wc.lpfnWndProc		= (WNDPROC) ControllerWindowProc;
	wc.style			= CS_HREDRAW | CS_VREDRAW;
	wc.hbrBackground	= (HBRUSH)GetStockObject(WHITE_BRUSH); 
	wc.hInstance		= g_hInstance;
	wc.lpszClassName	= CONTROLLER_WINDOW_CLASS_NAME;
	wc.hIcon			= LoadIcon(g_hInstance, MAKEINTRESOURCE(IDI_MYSAIFU));

	if (! RegisterClass(&wc)) {
		MessageBox(NULL,
				   _T("Failed to register window class"),
				   _T("Error"),
				   MB_OK | MB_ICONERROR);
		return -1;
	}
	g_hwndController = CreateWindow(CONTROLLER_WINDOW_CLASS_NAME,
								 NOT_STARTED_TITLE,
								 WS_VISIBLE,
								 CW_USEDEFAULT,
								 CW_USEDEFAULT,
								 CW_USEDEFAULT,
								 CW_USEDEFAULT,
								 NULL,
								 NULL,
								 g_hInstance,
								 NULL);

	if (g_hwndController == NULL) {
		MessageBox(NULL,
				   _T("Failed to create controller window"),
				   _T("Error"),
				   MB_OK | MB_ICONERROR);
		return -1;
	}

	// 準備開始メッセージを送る
	PostMessage(g_hwndController,
				JVM_MESSAGE_PREPARE,
				NULL,
				(LPARAM) lpCmdLine);
	// メッセージループ
	MSG msg;
	// メッセージループ
	while (GetMessage(&msg, NULL, 0, 0) > 0) {
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}	
	return msg.wParam;
}


/**
 * 指定されたファイルから起動パラメータをロードする
 */
static arguments* load_arguments_from(const _TCHAR* filename) {
	FILE* file = _tfopen(filename, _T("r"));
	if (file) {
		// 最大 4096 バイトまで読み込める
		_TCHAR target[4096];
		_fgetts(target, sizeof(target) / sizeof(*target), file);
		fclose(file);
		arguments* a = arguments_load(target);
		return a;
	}
	return NULL;
}

/**
 * argumentsをrecentディレクトリから読み込む
 */
static int load_recent_arguments(const _TCHAR* recent_dir, arguments*** args) {
	_TCHAR filename[MAX_PATH+1];
	_stprintf(filename, _T("%s\\*.mysaifujvm"), recent_dir);

	WIN32_FIND_DATA all_files[128];	// 最大で128個のファイルまでサポート
	WIN32_FIND_DATA find_data;
	HANDLE hFind = FindFirstFile(filename, &find_data);
	int count = 0;
	if (hFind != INVALID_HANDLE_VALUE) {
		do {
			all_files[count] = find_data;
			count++;
		} while (FindNextFile(hFind, &find_data) && count < (sizeof(all_files) / sizeof(all_files[0])));
	}
	CloseHandle(hFind);


	// ファイルのタイムスタンプ逆順にソートする
	qsort(all_files,
		  count,
		  sizeof(all_files[0]),
		  (int (*) (const void*, const void*)) compare_last_write_time);
	*args = (arguments**) realloc(*args, sizeof(arguments*) * (count + 1));
	for (int i = 0; i < count; ++i) {
		WIN32_FIND_DATA find_data = all_files[i];
		_stprintf(filename, _T("%s\\%s"),
					recent_dir,
					find_data.cFileName);
		arguments* a = load_arguments_from(filename);
		if (a) {
			*(*args + i) = a;
		}
	}

	return count;
}

/**
 * 指定された arguments をrecentディレクトリ配下に保存する
 */
static bool save_arguments(const _TCHAR* recent_dir, arguments* args) {
	// recent_dir が存在しない場合は作成する
	WIN32_FIND_DATA find_data;
	HANDLE hFind = FindFirstFile(recent_dir, &find_data);
	if (hFind == INVALID_HANDLE_VALUE) {
		CreateDirectory(recent_dir, NULL);
	}
	CloseHandle(hFind);
	
	{
		// Version 0.1.0 以前では、.lnk ファイルとして作成していた。
		// このファイルを削除する
		_TCHAR shortcut_name[MAX_PATH + 1];
		_sntprintf(shortcut_name,
				   sizeof(shortcut_name) / sizeof(*shortcut_name),
				  _T("%s\\%s.lnk"),
				  recent_dir,
				  args->class_name);
		shortcut_name[MAX_PATH] = _T('\0');
		DeleteFile(shortcut_name);
	}

	// 保存用のファイルを作成する
	// jarファイルの実行が指定された場合、args->class_nameに '\' が含まれている
	// これはファイル名には使えないので _ で置換する
	_TCHAR class_name[MAX_PATH + 1];
	_tcscpy(class_name, args->class_name);
	_TCHAR* work = class_name;
	while (*work) {
		if (*work == _T('\\')) {
			*work = _T('_');
		}
		work++;
	}

	_TCHAR paramfile_name[MAX_PATH + 1];
	_sntprintf(paramfile_name,
			   sizeof(paramfile_name) / sizeof(*paramfile_name),
			  _T("%s\\%s.mysaifujvm"),
			  recent_dir,
			  class_name);
	paramfile_name[MAX_PATH] = _T('\0');
	// 既存のファイルは削除する
	DeleteFile(paramfile_name);

	// ファイルに保存する
	FILE* file = _tfopen(paramfile_name, _T("w"));
	arguments_store(args, file);
	fclose(file);

	return true;
}

/**
 * コンフィグ画面を表示する
 */
static int show_arguments_dialog(HWND hwndOwner, arguments** args) {
	// コンフィグダイアログを生成する
	return DialogBoxParam(g_hInstance,
						  MAKEINTRESOURCE(IDD_CONFIG),
						  hwndOwner,
						  (DLGPROC) ArgumentsDialogProc,
						  (LPARAM) args);

}

/**
 * コントローラウインドウ列挙プロシージャ
 */
BOOL CALLBACK EnumControllerWindowsProc(HWND hwnd, LPARAM lParam) {
	_TCHAR classname[256];
	if (GetClassName(hwnd, classname, sizeof(classname) / sizeof(classname[0]))) {
		if (_tcscmp(classname, CONTROLLER_WINDOW_CLASS_NAME) == 0) {
			_TCHAR text[256];
			// コントローラウインドウの場合、ウインドウのタイトルテキストを取得して、
			// リストボックスに追加する
			// （コントローラウインドウのタイトルは、メインクラスの名前になっている）
			if (SendMessage(hwnd, WM_GETTEXT, (WPARAM) (sizeof(text) / sizeof(text[0])), (LPARAM) text)) {
				if (_tcscmp(text, NOT_STARTED_TITLE) != 0) {
					HWND hwndList = (HWND) lParam;
					// リストビューにアイテムを追加する
					_TCHAR hwndstr[12];
					_itot((int) hwnd, hwndstr, 10);

					LVITEM item = {0};
					item.mask = LVIF_TEXT;
					item.pszText = hwndstr;
					SendMessage(hwndList, LVM_INSERTITEM, 0, (LPARAM) &item);
					
					item.iSubItem = 1;
					item.pszText = text;
					SendMessage(hwndList, LVM_SETITEM, 0, (LPARAM) &item);
				}
			}
		}
	}
	return TRUE;
}

/**
 * リストボックスで選択されているJVMのコントローラウインドウにメッセージを送る
 */
static void send_message_to_controller_window(HWND hDlg, UINT message) {
	HWND hwndList = GetDlgItem(hDlg, IDC_JVMLIST);
	int index = -1;
	while ((index = SendMessage(hwndList,
							LVM_GETNEXTITEM,			// リスト内のアイテムを検索
							index,						// 検索開始位置
							LVNI_SELECTED)) != -1) {	// 選択されているアイテムを検索
		LVITEM item = {0};
		item.mask = LVIF_TEXT;
		item.iItem = index;
		_TCHAR buff[256];
		item.pszText = buff;
		item.cchTextMax = sizeof(buff) / sizeof(*buff);
		if (SendMessage(hwndList, LVM_GETITEM, 0, (LPARAM) &item)) {
			SetCursor(LoadCursor(NULL, IDC_WAIT));
			// メッセージを送る
			HWND hwndController = (HWND) _ttoi(item.pszText);
			if (hwndController) {
				SendMessage(hwndController, message, 0, 0);
			}
			SetCursor(NULL);
		}
	}
	// リストビューを更新する
	SendMessage(hDlg, WM_COMMAND, (WPARAM) IDC_REFRESH, 0);
}

/**
 * Javaスレッドを開始し、全Javaスレッドの終了を待機するスレッド
 */
DWORD WINAPI main_routine(LPVOID pvarg) {
	arguments* config = (arguments*) pvarg;

	char main_class_name[256];
	class_loader* loader = get_bootstrap_class_loader();
	if (config->jar) {
		convert_to_utf8(config->jar_file_name, -1, main_class_name, sizeof(main_class_name) / sizeof(main_class_name[0]));
	} else {
		convert_to_utf8(config->class_name, -1, main_class_name, sizeof(main_class_name) / sizeof(main_class_name[0]));
	}
	
	if (start_main_thread(main_class_name, config->argv, config->jar)) {
		// デーモンスレッドだけになるまで待つ
		HANDLE hTerminate = get_terminate_handle();
		WaitForSingleObject(hTerminate, INFINITE);

		// シャットダウンフックを実行する
		run_shutdown_hooks();

		// 再度デーモンスレッドだけになるまで待つ
		hTerminate = get_terminate_handle();
		WaitForSingleObject(hTerminate, INFINITE);

	} else {
		// エラー発生
		MessageBox(NULL,
				   _T("Could not start main thread"),
				   _T("Error"),
				   MB_OK | MB_ICONERROR);
	}

	// コントローラウインドウを破棄する
	DestroyWindow(g_hwndController);

	return 0;
}

/**
 * exit()時に実行される
 */
static void exit_handler() {
	// コンソールがクローズするのを待つ
	console_wait_for_close();
}

/**
 * シャットダウンフック実行スレッドを初期化する
 */
static bool run_shutdown_hooks() {
	bool result = false;

	class_loader* loader = get_bootstrap_class_loader();

	// シャットダウンスレッドを作成する
	frame* frm = alloc_root_frame(NULL);
	ClassFile* cfile = find_ClassFile(frm,
									  intern_utf8("java/lang/VMShutdownHookThread"));
	free_root_frame(frm);
	if (cfile == NULL) {
		return false;
	}
	// ルートフレームを作成する
	frame* new_frame = alloc_root_frame(cfile);
	// メモリを確保し、スタックに積む
	jobject ref = alloc_object(cfile, new_frame);
	DUP_STACK(new_frame);	// コンストラクタ実行用

	// コンストラクタを実行する
	jmethodID constructor = get_method_id(cfile,
										  intern_utf8("<init>"),
										  intern_utf8("()V"));
	method_info* constructor_info = get_method_info(cfile, constructor);
	invoke_method(new_frame, ref, cfile, constructor_info);

	// start() を実行する
	jmethodID mid = get_method_id(cfile,
								  intern_utf8("start"),
								  intern_utf8("()V"));
	method_info* minfo = get_method_info(cfile, mid);
	cfile = minfo->declaring_ClassFile;
	invoke_method(new_frame, ref, cfile, minfo);
	if (! exception_occurred(new_frame)) {
		result = true;
	}
	free_root_frame(new_frame);

	// この後、スレッドが終了するまで待たなければならない

	return result;
}

/**
 * コントローラウインドウのプロシージャ
 */
LRESULT CALLBACK ControllerWindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
	static HWND hwndMessage, hwndInformation;
	static SHACTIVATEINFO sai;

	switch (message) {
	case WM_CREATE:
		{
			// ステータス表示エリア
			hwndMessage
				= CreateWindow(_T("STATIC"),
						 _T("Initializing"),
						 WS_CHILD | WS_VISIBLE,
						 0,
						 50,	// 初期位置
						 200,
						 30,
						 hWnd,
						 0,
						 g_hInstance,
						 NULL);
			hwndInformation
				= CreateWindow(WC_LISTVIEW,
							   _T(""),
							   WS_VISIBLE | WS_BORDER | WS_CHILD | LVS_REPORT,
							   0,
							   90,
							   200,
							   100,
							   hWnd,
							   0,
							   g_hInstance,
							   NULL);
			RECT rect;
			GetWindowRect(hwndInformation, &rect);
			LVCOLUMN lc = {0};
			lc.mask = LVCF_WIDTH | LVCF_TEXT;
			lc.cx = (rect.right - rect.left) / 2;
			lc.pszText = _T("Name");
			ListView_InsertColumn(hwndInformation, 0, &lc);
			lc.cx = (rect.right - rect.left) - lc.cx;
			lc.pszText = _T("Value");
			ListView_InsertColumn(hwndInformation, 1, &lc);
			
			sai.cbSize = sizeof(SHACTIVATEINFO);

		}
		break;

	case WM_SIZE:
		{
			// 各コンポーネントのサイズを調整する
			RECT rectWindow;
			GetWindowRect(hWnd, &rectWindow);

			// メッセージ表示エリア
			RECT rectMessage;
			GetWindowRect(hwndMessage, &rectMessage);
			
			SetWindowPos(hwndMessage,
						 NULL,
						 0,
						 0,
						 (rectWindow.right - rectWindow.left),
						 rectMessage.bottom - rectMessage.top,
						 SWP_NOMOVE | SWP_NOZORDER);

			// 情報表示エリア
			RECT rectInformation;
			GetWindowRect(hwndInformation, &rectInformation);
			POINT point;
			point.x = 0;
			point.y = rectMessage.bottom;
			ScreenToClient(hWnd, &point);
			SetWindowPos(hwndInformation,
						 NULL,
						 0,
						 point.y,
						 (rectWindow.right - rectWindow.left),
						 (rectWindow.bottom - rectMessage.bottom),	// メッセージウインドウの直下に置く
						 SWP_NOZORDER);

		}
		break;

	case WM_PAINT:
		{
			PAINTSTRUCT ps;
			HDC hDC = BeginPaint(hWnd, &ps);

			// Paint an Icon
			HICON hIcon = LoadIcon(g_hInstance, MAKEINTRESOURCE(IDI_MYSAIFU));
			DrawIcon(hDC, 10, 10, hIcon);
			DestroyIcon(hIcon);

			// Paint a Bitmap
			HBITMAP hBitmap = LoadBitmap(g_hInstance, MAKEINTRESOURCE(IDB_TITLE));
			BITMAP bmp;
			GetObject(hBitmap, sizeof(BITMAP), &bmp);
			HDC hCompatible = CreateCompatibleDC(hDC);
			SelectObject(hCompatible, hBitmap);
			BitBlt(hDC,
				   46,
				   10,
				   bmp.bmWidth,
				   bmp.bmHeight,
				   hCompatible,
				   0,
				   0,
				   SRCCOPY);
			DeleteDC(hCompatible);
			DeleteObject(hBitmap);

			EndPaint(hWnd, &ps);
		}
		break;

	case WM_ACTIVATE:
		SHHandleWMActivate(hWnd, wParam, lParam, &sai, FALSE);
		break;

	case WM_SETTINGCHANGE:
		SHHandleWMSettingChange(hWnd, wParam, lParam, &sai);
     	break;

	case JVM_MESSAGE_PREPARE:
		// 準備処理
		{
			const _TCHAR* lpCmdLine = (const _TCHAR*) lParam;
			// argumentsを初期化する
			arguments* args;
			init_arguments_settings();

			// 引数をパースする
			if (_tcslen(lpCmdLine) == 0) {
				// 引数なしの場合には、コンフィグ画面を表示する
				if (! show_arguments_dialog(g_hwndController, &args)) {
					// 終了
					DestroyWindow(hWnd);
					break;
				}
			} else {
				if (_tcsncmp(lpCmdLine, _T("-Xconfig "), 9) == 0) {
					// -Xconfigオプション
					// 次の引数がコンフィグファイル名
					lpCmdLine += 9;
					args = load_arguments_from(lpCmdLine);
				} else {
					args = arguments_load(lpCmdLine);
				}

				if (! args) {
					MessageBox(NULL,
							   _T("Invalid arguments."),
							   _T("Mysaifu JVM error"),
							   MB_OK | MB_ICONERROR);
					DestroyWindow(hWnd);
					break;
				}
			}
				
			// -jar オプションが指定された場合、CLASSPATHにjarファイル名を設定する
			if (args->jar) {
				_tcscpy(args->classpath, args->jar_file_name);
			}
			
			// コンソールを初期化する
			console_init(g_hInstance, hWnd, args->use_stdio);


			// JVM開始をControllerWindowに指示する
			PostMessage(g_hwndController,
						JVM_MESSAGE_START_JVM,
						NULL,
						(LPARAM) args);
		}
		break;

	case JVM_MESSAGE_START_JVM:
		{
			arguments* config = (arguments*) lParam;

			// カレントargumentsを設定する
			set_current_arguments(config);

			// ControllerWindowのタイトルを変更する
			SetWindowText(hWnd, config->class_name);

			// ウインドウの表示／非表示を決定する
			if (config->hide_vm_window) {
				ShowWindow(hWnd, SW_HIDE);
			} else {
				// メニューバーを作成する
				SHMENUBARINFO mbi = {0};
				mbi.cbSize		= sizeof(SHMENUBARINFO);
				mbi.hwndParent	= hWnd;
				mbi.nToolBarId	= IDR_MAINMENUBAR;
				mbi.hInstRes	= g_hInstance;
				mbi.dwFlags		= SHCMBF_HMENU;

				if (SHCreateMenuBar(&mbi)) {
					RECT rc;
					RECT rcMenuBar;
					GetWindowRect(hWnd, &rc);
					GetWindowRect(mbi.hwndMB, &rcMenuBar);
					rc.bottom -= (rcMenuBar.bottom - rcMenuBar.top);
					MoveWindow(hWnd,
							   rc.left,
							   rc.top,
							   rc.right-rc.left,
							   rc.bottom-rc.top,
							   FALSE);
				}

				ShowWindow(hWnd, SW_SHOW);
				UpdateWindow(hWnd);
				SetForegroundWindow(hWnd);
			}

			// ログファイルを作成する
			if (_tcslen(config->logfile) > 0) {
				HANDLE hFile = CreateFile(config->logfile,
										  GENERIC_WRITE,
										  FILE_SHARE_READ | FILE_SHARE_WRITE,
										  NULL,
										  OPEN_ALWAYS,
										  0,
										  NULL);
				if (hFile == INVALID_HANDLE_VALUE) {
					// ファイルオープン失敗
					MessageBox(hWnd,
							   _T("Failed to open logfile"),
							   _T("Mysaifu JVM error"),
							   MB_OK | MB_ICONERROR);
				} else {
					if (GetLastError() != ERROR_ALREADY_EXISTS) {
						// ファイル先頭にバイトオーダーマークを出力する
						_TCHAR bom = (_TCHAR) 0xFEFF;
						DWORD count;
						WriteFile(hFile,
								  &bom,
								  sizeof(bom),
								  &count,
								  NULL);
					}
					console_set_logfile(hFile);
				}
			}

			// コンソールを表示する
			if (config->show_console) {
				console_set_visible(true);
			}
			SendMessage(hwndMessage, WM_SETTEXT, 0, NULL);

			// JVMの初期化を開始する
			SendMessage(hwndMessage, WM_SETTEXT, 0, (LPARAM)_T("Initializing..."));

			// UTF-8に関する情報を初期化する
			// （これを一番先に初期化しておく必要がある）
			init_utf8_settings();
			
			// その他の初期化を実施する
			init_thread_settings(config->native_stack_size);
			init_boot_classpath(config->boot_classpath);
			init_system_classpath(config->classpath);
			initialize_heap(config->max_heap_size);
			init_ClassFile_settings();
			init_class_loader_settings(config->verify_mode);
			init_monitor_settings();
			init_instruction_settings();
			init_jni_funcs();
			init_frame_settings(config->java_stack_size);
			
			// assertionの有効／無効を設定する
			set_assertion_status(config->enableAssertions);

			if (! init_gc()) {
				// ガベージコレクタの初期化に失敗
				DBG(_T("Failed to initialize GC.\n"));
				MessageBox(NULL,
						   _T("Failed to initialize GC."),
						   _T("Error"),
						   MB_OK | MB_ICONERROR);
				DestroyWindow(hWnd);
				return 0;	// 初期化失敗
			}
			
			// -verbose:gcオプションを設定
			gc_set_verbose(config->verbose_gc);

			// ガベージコレクタログファイル名を設定
			set_loggc_filename(config->loggc);

			if (! init_finalizer()) {
				// ファイナライザの初期化に失敗
				DBG(_T("Failed to initialize finalizer thread.\n"));
				MessageBox(NULL,
						   _T("Failed to initialize finalizer thread."),
						   _T("Error"),
						   MB_OK | MB_ICONERROR);

				DestroyWindow(hWnd);
				return 0;	// 初期化失敗
			}
			
			// OutOfMemoryErrorとStackOverflowErrorを作成しておく
			if (! prepare_OutOfMemoryError() || ! prepare_StackOverflowError()) {
				// 生成に失敗
				DBG(_T("Failed to prepare OutOfMemoryError.\n"));
				MessageBox(NULL,
						   _T("Faild to create a instance of OutOfMemoryError.\n"),
						   _T("Error"),
						   MB_OK | MB_ICONERROR);
				DestroyWindow(hWnd);
				return 0;	// 初期化失敗
			}

			// メインスレッドを開始
			CreateThread(NULL,
						 0,
						 main_routine,
						 config,
						 0,
						 NULL);
			
			// JVMの走行が開始したことを表示する
			SendMessage(hwndMessage, WM_SETTEXT, 0, (LPARAM)_T("JVM is running"));
			UpdateWindow(hwndMessage);
			
			// exit()ハンドラを登録する
			atexit(exit_handler);

			// 実行中クラスの情報を表示する
			LVITEM item = {0};
			item.mask = LVIF_TEXT;

			// クラス名（またはjarファイル名)
			item.iSubItem = 0;
			if (config->jar) {
				item.pszText = _T("Jar file");
			} else {
				item.pszText = _T("Class name");
			}
			ListView_InsertItem(hwndInformation, &item);

			item.iSubItem = 1;
			item.pszText = config->class_name;
			ListView_SetItem(hwndInformation, &item);

			// コマンドライン引数
			item.iItem++;
			item.iSubItem = 0;
			item.pszText = _T("Command line");
			ListView_InsertItem(hwndInformation, &item);

			item.iSubItem = 1;
			item.pszText = config->argv;
			ListView_SetItem(hwndInformation, &item);

			// Classpath
			if (! config->jar) {
				item.iItem++;
				item.iSubItem = 0;
				item.pszText = _T("Classpath");
				ListView_InsertItem(hwndInformation, &item);

				item.iSubItem = 1;
				item.pszText = config->classpath;
				ListView_SetItem(hwndInformation, &item);
			}

			// カレントディレクトリ
			item.iItem++;
			item.iSubItem = 0;
			item.pszText = _T("Current directory");
			ListView_InsertItem(hwndInformation, &item);

			item.iSubItem = 1;
			item.pszText = config->current_directory;
			ListView_SetItem(hwndInformation, &item);

			// 最大ヒープサイズ
			item.iItem++;
			item.iSubItem = 0;
			item.pszText = _T("Max heap size");
			ListView_InsertItem(hwndInformation, &item);

			item.iSubItem = 1;
			_TCHAR buff[10];
			if (config->max_heap_size > 1024 * 1024) {
				_stprintf(buff, _T("%d MB"), config->max_heap_size / 1024 / 1024);
			} else if (config->max_heap_size > 1024) {
				_stprintf(buff, _T("%d KB"), config->max_heap_size / 1024);
			} else {
				_stprintf(buff, _T("%d"), config->max_heap_size);
			}
			item.pszText = buff;
			ListView_SetItem(hwndInformation, &item);


			// カラムサイズを自動調節する
			ListView_SetColumnWidth(hwndInformation, 0, LVSCW_AUTOSIZE);
			ListView_SetColumnWidth(hwndInformation, 1, LVSCW_AUTOSIZE);

		}
		break;

	case WM_CLOSE:
		{
			// コントローラウインドウを破棄する
			DestroyWindow(hWnd);
		}
		break;

	case WM_DESTROY:
		{
			// 終了する
			PostQuitMessage(0);
		}
		break;

	case WM_COMMAND:
		{
			switch (LOWORD(wParam)) {
			case IDC_SHOWCONSOLE:
				{
					// コンソールを表示する
					console_set_visible(true);
				}
				break;
			case IDC_EXITJVM:
				{
					// 確認ダイアログを表示し、OKがタップされた時点でJVMを終了する
					if (IDOK == MessageBox(hWnd,
										   _T("Exit JVM?"),
										   _T("Confirm"),
										   MB_OKCANCEL | MB_ICONQUESTION)) {
						DestroyWindow(hWnd);
					}
				}
				break;

			case IDM_ABOUT:
				{
					// Aboutダイアログを表示する
					DialogBox(g_hInstance,
						  MAKEINTRESOURCE(IDD_ABOUT),
						  hWnd,
						  (DLGPROC) AboutDialogProc);
				}
				break;
			}
		}
		break;

	default:
		return DefWindowProc(hWnd, message, wParam, lParam);
	}
	
	return 1;
}

/**
 * JVMリストダイアログのプロシージャ
 */
LRESULT CALLBACK JVMListDialogProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) {
	switch (message)
	{
		case WM_INITDIALOG:
			{
				// ダイアログボックスを最大化する
				SHINITDLGINFO shidi = {0};
				shidi.dwMask = SHIDIM_FLAGS;
				shidi.dwFlags = SHIDIF_DONEBUTTON | SHIDIF_SIPDOWN | SHIDIF_SIZEDLGFULLSCREEN | SHIDIF_EMPTYMENU;
				shidi.hDlg = hDlg;
				SHInitDialog(&shidi);

				// リストビューを初期化する
				HWND hwndListView = GetDlgItem(hDlg, IDC_JVMLIST);
				HDC hDC = GetDC(hwndListView);

				SIZE size;
				GetTextExtentPoint(hDC, _T("0000000000"), 10, &size);
				ReleaseDC(hwndListView, hDC);
				RECT rect;
				GetClientRect(hwndListView, &rect);

				LVCOLUMN lc = {0};
				lc.mask = LVCF_WIDTH | LVCF_TEXT;
				lc.cx = size.cx;
				lc.pszText = _T("ID");
				SendMessage(hwndListView, LVM_INSERTCOLUMN, 0, (LPARAM) &lc);
				lc.cx = (rect.right - rect.left) - size.cx - 2;
				lc.pszText = _T("Class name");
				SendMessage(hwndListView, LVM_INSERTCOLUMN, 1, (LPARAM) &lc);


				// コントローラウインドウを列挙して、リストに追加する
				HWND hwndList = GetDlgItem(hDlg, IDC_JVMLIST);
				EnumWindows(EnumControllerWindowsProc, (LPARAM) hwndList);

			}
			return TRUE; 
		
		case WM_NOTIFY:
			if (wParam == IDC_JVMLIST) {
				LV_DISPINFO* lvinfo = (LV_DISPINFO *)lParam;
				switch (lvinfo->hdr.code) {
				case LVN_ITEMCHANGED:
					{
						int index = SendMessage(GetDlgItem(hDlg, IDC_JVMLIST),
												LVM_GETNEXTITEM,	// リスト内のアイテムを検索
												-1,					// リストの先頭から検索
												LVNI_SELECTED);		// 選択されているアイテムを検索
						if (index == -1) {
							EnableWindow(GetDlgItem(hDlg, IDC_EXITJVM), FALSE);
						} else {
							EnableWindow(GetDlgItem(hDlg, IDC_EXITJVM), TRUE);
						}
					}
					return TRUE;
				}
			}
			break;

		case WM_COMMAND:
			switch (LOWORD(wParam)) {
			case IDOK:
				{
					// ダイアログを終了する
					EndDialog(hDlg, LOWORD(wParam));
					return TRUE;
				}
			case IDC_EXITJVM:
				// コントローラウインドウに WM_CLOSE メッセージを送る
				send_message_to_controller_window(hDlg, WM_CLOSE);
				return TRUE;

			case IDC_REFRESH:
				{
					HWND hwndList = GetDlgItem(hDlg, IDC_JVMLIST);
					SendMessage(hwndList, LVM_DELETEALLITEMS, 0, 0);
					EnumWindows(EnumControllerWindowsProc, (LPARAM) hwndList);
					EnableWindow(GetDlgItem(hDlg, IDC_EXITJVM), FALSE);
				}
				return TRUE;
			}
	}
    return FALSE;
}

/**
 * Aboutダイアログのプロシージャ
 */
LRESULT CALLBACK AboutDialogProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) {
	switch (message)
	{
		case WM_INITDIALOG:
			{
				// ダイアログボックスを最大化する
				SHINITDLGINFO shidi = {0};
				shidi.dwMask = SHIDIM_FLAGS;
				shidi.dwFlags = SHIDIF_DONEBUTTON | SHIDIF_SIPDOWN | SHIDIF_SIZEDLGFULLSCREEN | SHIDIF_EMPTYMENU;
				shidi.hDlg = hDlg;
				SHInitDialog(&shidi);

				LARGE_INTEGER version = get_jvm_version();
				// 16ビットごとに区切って文字列を作成する
				jstring result = NULL;
				_TCHAR buff[128];
				_sntprintf(buff,
						   sizeof(buff) / sizeof(*buff),
						  _T("Version %d.%d.%d.%d"),
						  HIWORD(version.HighPart),
						  LOWORD(version.HighPart),
						  HIWORD(version.LowPart),
						  LOWORD(version.LowPart));
				SetWindowText(GetDlgItem(hDlg, IDC_VERSION), buff);
			}
			return TRUE; 
		
		case WM_COMMAND:
			switch (LOWORD(wParam)) {
			case IDOK:
				{
					// ダイアログを終了する
					EndDialog(hDlg, LOWORD(wParam));
					return TRUE;
				}
			}
	}
    return FALSE;
}

/**
 * 引数設定ダイアログのプロシージャ
 */
LRESULT CALLBACK ArgumentsDialogProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) {
	// 構造体からコントロールにデータをコピーするマクロ
	#define COPY_FROM_CONTROLS(args) \
	{ \
		HWND hwndClassName = GetDlgItem(hDlg, IDC_CLASSNAME); \
		BOOL jar = (SendMessage(GetDlgItem(hDlg, IDC_TYPE), \
								CB_GETCURSEL, \
								0, \
								0) == 0); \
		if (jar) { \
			/* jarオプションが有効な場合 */ \
			args->jar = true; \
			/* jarファイル名を取得する */  \
			GetWindowText(hwndClassName, \
						  args->jar_file_name, \
						  sizeof(args->jar_file_name) / sizeof(*args->jar_file_name)); \
		} else { \
			args->jar = false; \
			/* クラス名 */ \
			GetWindowText(hwndClassName, \
						  args->class_name, \
						  (sizeof(args->class_name) / sizeof(*args->class_name))); \
		} \
		\
		/* コンソールを表示するか */ \
		HWND hwndShowConsole = GetDlgItem(hDlg, IDC_SHOWCONSOLE); \
		if (SendMessage(hwndShowConsole, BM_GETCHECK, 0, 0)) { \
			args->show_console = true; \
		} else { \
			args->show_console = false; \
		} \
	}
	#define COPY_TO_CONTROLS(args) \
	{ \
		HWND hwndClassName = GetDlgItem(hDlg, IDC_CLASSNAME); \
		HWND hwndType = GetDlgItem(hDlg, IDC_TYPE); \
		HWND hwndBrowse = GetDlgItem(hDlg, IDC_BROWSE); \
		if (args->jar) { \
			/* jarオプションが有効な場合 */ \
			SendMessage(hwndType, \
						CB_SETCURSEL, \
						0, \
						0); \
			SetWindowText(hwndClassName, args->jar_file_name); \
			EnableWindow(hwndBrowse, TRUE); \
		} else { \
			SendMessage(hwndType, \
						CB_SETCURSEL, \
						1, \
						0); \
			SetWindowText(hwndClassName, args->class_name); \
			EnableWindow(hwndBrowse, FALSE); \
		} \
		if (GetWindowTextLength(hwndClassName)) { \
			EnableWindow(GetDlgItem(hDlg, IDC_EXECUTE), TRUE); \
		} \
		/* コンソールを表示するか */ \
		HWND hwndShowConsole = GetDlgItem(hDlg, IDC_SHOWCONSOLE); \
		if (args->show_console) { \
			SendMessage(hwndShowConsole, BM_SETCHECK, BST_CHECKED, 0); \
		} else { \
			SendMessage(hwndShowConsole, BM_SETCHECK, BST_UNCHECKED, 0); \
		} \
	}
	
	static arguments** result;
	static arguments* args;
	static arguments** selectable_configs;
	static int selectable_count;
	static _TCHAR shortcut_dir[MAX_PATH + 1];
	static SHMENUBARINFO mbi = {0};
	switch (message)
	{
		case WM_INITDIALOG:
			{
				// ダイアログボックスを最大化する
				SHINITDLGINFO shidi = {0};
				shidi.dwMask = SHIDIM_FLAGS;
				shidi.dwFlags = SHIDIF_SIZEDLGFULLSCREEN;
				shidi.hDlg = hDlg;
				SHInitDialog(&shidi);

				// メニューバーを追加する
				mbi.cbSize = sizeof(SHMENUBARINFO);
				mbi.hwndParent = hDlg;
				mbi.nToolBarId = IDR_MENUBAR;
				mbi.hInstRes = g_hInstance;
				mbi.dwFlags = SHCMBF_HMENU;
				
				SHCreateMenuBar(&mbi); 

				SetWindowText(hDlg, _T("Mysaifu JVM"));
				result = (arguments**) lParam;
				args = arguments_create();
				
				// ヒストリを読み込む
				_sntprintf(shortcut_dir,
					       sizeof(shortcut_dir) / sizeof(shortcut_dir[0]),
						   SHORTCUT_DIRECTORY_FORMAT,
						   get_java_home());
				shortcut_dir[sizeof(shortcut_dir) / sizeof(shortcut_dir[0]) - 1] = _T('\0');
				selectable_count = load_recent_arguments(shortcut_dir, &selectable_configs);

				// リストボックスに追加する
				HWND hwndList = GetDlgItem(hDlg, IDC_RECENT_LIST);
				int max_width = 0;
				HDC hdc = GetDC(hwndList);
				for (int i = 0; i < selectable_count; ++i) {
					const _TCHAR* class_name = selectable_configs[i]->class_name;
					SIZE width;
					GetTextExtentPoint(hdc, class_name, _tcslen(class_name), &width);
					if (width.cx > max_width) {
						max_width = width.cx;
					}
					SendMessage(hwndList,
								LB_ADDSTRING,
								0,
								(LPARAM) class_name);
				}
				SendMessage(hwndList, LB_SETHORIZONTALEXTENT, (WPARAM) max_width, 0);
				ReleaseDC(hwndList, hdc);

				// Typeコンボボックスを初期化する
				HWND hwndCombo = GetDlgItem(hDlg, IDC_TYPE);
				SendMessage(hwndCombo,
							CB_ADDSTRING,
							0,
							(LPARAM) _T("JAR file"));
				SendMessage(hwndCombo,
							CB_ADDSTRING,
							0,
							(LPARAM) _T("Class"));

				// argumentsの内容を反映する
				COPY_TO_CONTROLS(args);
			}
			return TRUE; 
		
		case WM_INITMENUPOPUP:
			{
				// Save AS... メニューの有効／無効を切り替える
				BOOL enabled = GetWindowTextLength(GetDlgItem(hDlg, IDC_CLASSNAME)) > 0;
				EnableMenuItem((HMENU)wParam,
							   IDM_SAVE_AS,
							   MF_BYCOMMAND | (enabled ? MF_ENABLED : MF_GRAYED));
			}
			return TRUE;

		case WM_CLOSE:
			EndDialog(hDlg , 0);
			return TRUE;

		case WM_COMMAND:
			switch (LOWORD(wParam)) {
			case IDC_CLASSNAME:
				// クラス名
				if (HIWORD(wParam) == EN_CHANGE) {
					HWND hwndClassName = (HWND) lParam;
					HWND hwndExecute = GetDlgItem(hDlg, IDC_EXECUTE);
					if (GetWindowTextLength(hwndClassName)) {
						EnableWindow(hwndExecute, TRUE);
					} else {
						EnableWindow(hwndExecute, FALSE);
					}
				}
				break;

			case IDC_RECENT_LIST:
				switch (HIWORD(wParam)) {
				case LBN_SELCHANGE:
					{
						// リストボックスから選択された
						int select = SendMessage(GetDlgItem(hDlg, IDC_RECENT_LIST),
												 LB_GETCURSEL,
												 0,
												 0);
						if (select != -1) {
							args = selectable_configs[select];
							// 各コントロールを初期化する
							HWND hwndType = GetDlgItem(hDlg, IDC_TYPE);
							HWND hwndClassName = GetDlgItem(hDlg, IDC_CLASSNAME);
							if (args->jar) {
								// jarオプションが有効な場合
								SendMessage(hwndType, CB_SETCURSEL, (WPARAM) 0, 0);
								
								// Browseボタンを有効化する
								EnableWindow(GetDlgItem(hDlg, IDC_BROWSE), TRUE);

								// jarファイル名を設定する
								SetWindowText(hwndClassName, args->jar_file_name);

							} else {
								// 通常のクラス
								SendMessage(hwndType, CB_SETCURSEL, (WPARAM) 1, 0);

								// Browseボタンを無効化する
								EnableWindow(GetDlgItem(hDlg, IDC_BROWSE), FALSE);

								// クラス名を設定する
								SetWindowText(hwndClassName, args->class_name);
							}

							// コンソールを表示するか
							HWND hwndShowConsole = GetDlgItem(hDlg, IDC_SHOWCONSOLE);
							if (args->show_console) {
								SendMessage(hwndShowConsole, BM_SETCHECK, BST_CHECKED, 0);
							} else {
								SendMessage(hwndShowConsole, BM_SETCHECK, BST_UNCHECKED, 0);
							}
						}
						BOOL enabled = FALSE;
						if (GetWindowTextLength(GetDlgItem(hDlg, IDC_CLASSNAME))) {
							enabled = TRUE;
						}
						HWND hwndExecute = GetDlgItem(hDlg, IDC_EXECUTE);
						if (enabled != IsWindowEnabled(hwndExecute)) {
							EnableWindow(hwndExecute, enabled);
						}
					}
					return TRUE;
				}
				break;
			
			case IDC_BROWSE:
				{
					// jarファイルの選択画面を表示する
					_TCHAR filename[MAX_PATH + 1];
					filename[0] = _T('\0');
					OPENFILENAME ofn = {0};
					ofn.hwndOwner = hDlg;
					ofn.lpstrFilter = _T("JAR file (*.jar)\0*.jar\0\0");
					ofn.lpstrFile = filename;
					ofn.nMaxFile = sizeof(filename) / sizeof(*filename);
					ofn.lStructSize = sizeof(OPENFILENAME);
					if (get_open_file_name(&ofn)) {
						// コントロールに値を設定する
						SetDlgItemText(hDlg,
									   IDC_CLASSNAME,
									   filename);
						// Executeボタンを有効にする
						EnableWindow(GetDlgItem(hDlg, IDC_EXECUTE), TRUE);
					}
				}
				return TRUE;

			case IDM_EXIT:
				{
					// メニューバーを破棄する
					DestroyWindow(mbi.hwndMB);

					// ダイアログを終了する
					SendMessage(hDlg, WM_CLOSE, 0, 0);
					
					// この直後にプロセスは終了するので、
					// メモリの開放処理は行わない

				}
				return TRUE;

			case IDC_EXECUTE:
				// JVMを起動する
				{
					// コントロールから設定値をコピーする
					COPY_FROM_CONTROLS(args);

					// argumentsを保存する
					save_arguments(shortcut_dir, args);
					
					// メモリを開放する
					for (int i = 0; i < selectable_count; ++i) {
						arguments* a = selectable_configs[i];
						if (a != args) {
							arguments_delete(a);
						}
					}
					free(selectable_configs);

					// 結果を返す
					*result = args;

					// メニューバーを破棄する
					DestroyWindow(mbi.hwndMB);

					// ダイアログを終了する
					EndDialog(hDlg, 1);
				}
				return TRUE;
			
			case IDC_TYPE:
				switch (HIWORD(wParam)) {
				case CBN_SELCHANGE:
					{
						// コンボボックスが選択された
						int select = SendMessage(GetDlgItem(hDlg, IDC_TYPE), CB_GETCURSEL, 0, 0);
						if (select == 0) {
							// jarオプション有効
							args->jar = true;

							// Browseボタンを有効化する
							EnableWindow(GetDlgItem(hDlg, IDC_BROWSE), TRUE);
						} else if (select == 1) {
							// jarオプション無効
							args->jar = false;
							// Browseボタンを無効化する
							EnableWindow(GetDlgItem(hDlg, IDC_BROWSE), FALSE);
						}
					}
					return TRUE;
				}
				break;

			case IDC_OPTIONS:
				{
					// Optionsダイアログを表示する
					show_options_dialog(hDlg, args);
				}
				return TRUE;

			case IDM_OPEN:
				// コンフィグをオープンする
				{
					_TCHAR filename[MAX_PATH + 1] = _T("");
					OPENFILENAME ofn = {0};
					ofn.lStructSize = sizeof(OPENFILENAME);
					ofn.hwndOwner = hDlg;
					ofn.hInstance = g_hInstance;
					ofn.lpstrFilter = _T("Mysaifu JVM\0*.mysaifujvm\0\0");
					ofn.lpstrFile = filename;
					ofn.nMaxFile = MAX_PATH;
					ofn.Flags = OFN_FILEMUSTEXIST;
					if (get_open_file_name(&ofn)) {
						arguments_delete(args);
						args = load_arguments_from(filename);
						if (args) {
							COPY_TO_CONTROLS(args);
						}
					}
				}
				return TRUE;

			case IDM_SAVE_AS:
				// コンフィグを保存する
				{
					_TCHAR filename[MAX_PATH + 1] = _T("");
					OPENFILENAME ofn = {0};
					ofn.lStructSize = sizeof(OPENFILENAME);
					ofn.hwndOwner = hDlg;
					ofn.hInstance = g_hInstance;
					ofn.lpstrFilter = _T("Mysaifu JVM\0*.mysaifujvm\0\0");
					ofn.lpstrFile = filename;
					ofn.nMaxFile = MAX_PATH;
					ofn.Flags = OFN_OVERWRITEPROMPT;
					
					if (get_save_file_name(&ofn)) {
						COPY_FROM_CONTROLS(args);
						FILE* fp = _tfopen(filename, _T("w"));
						if (! fp) {
							MessageBox(hDlg,
								       _T("Failed to open file"),
									   _T("Mysaifu JVM error"),
									   MB_OK | MB_ICONERROR);
						} else {
							arguments_store(args, fp);
							fclose(fp);
						}
					}
				}
				return TRUE;

			case IDM_JVMLIST:
				{
					// JVMリストを表示する
					DialogBox(g_hInstance,
						  MAKEINTRESOURCE(IDD_JVMLIST),
						  hDlg,
						  (DLGPROC) JVMListDialogProc);
				}
				return TRUE;

			case IDM_ABOUT:
				{
					// Aboutダイアログを表示する
					DialogBox(g_hInstance,
						  MAKEINTRESOURCE(IDD_ABOUT),
						  hDlg,
						  (DLGPROC) AboutDialogProc);
				}
				return TRUE;

			}
		}
    return FALSE;

#undef COPY_FROM_CONTROLS
#undef COPY_TO_CONTROLS
}

/**
 * 指定されたリストビューに、システムプロパティの内容を表示させる
 */
static void set_system_properties(HWND hwndListView, arguments* args) {
	ListView_DeleteAllItems(hwndListView);
	LVITEM item = {0};
	item.mask = LVIF_TEXT;
	 for (int i = 0; i < args->properties_count; ++i) {
		item.iItem = i;
		item.iSubItem = 0;
		const _TCHAR* key = arguments_get_property_key(args, i);
		item.pszText = const_cast<_TCHAR*>(key);
		ListView_InsertItem(hwndListView, &item);

		item.iSubItem = 1;
		const _TCHAR* value = arguments_get_property(args, key);
		item.pszText = const_cast<_TCHAR*>(value);
		ListView_SetItem(hwndListView, &item);
	 }
}

/**
 * 指定されたリストビューに、システムプロパティを追加する
 * 同一のキーを持つ行がすでに存在していた場合、内容を置き換える
 */
static void put_system_property(HWND hwndListView, _TCHAR* key, _TCHAR* value) {
	LVFINDINFO info = {0};
	info.flags = LVFI_STRING;
	info.psz = key;
	int index = ListView_FindItem(hwndListView, -1, &info);

	int pos = -1;
	if (index == -1) {
		// 存在しない場合は、アイテムを挿入する
		pos = ListView_GetItemCount(hwndListView);
		LVITEM item = {0};
		item.mask = LVIF_TEXT;
		item.pszText = key;
		item.iItem = pos;
		ListView_InsertItem(hwndListView, &item);

		item.iSubItem = 1;
		item.pszText = value;
		ListView_SetItem(hwndListView, &item);
	} else {
		// 存在する場合は、アイテムを置き換える
		pos = index;
		LVITEM item = {0};
		item.mask = LVIF_TEXT;
		item.pszText = value;
		item.iItem = index;
		item.iSubItem = 1;
		ListView_SetItem(hwndListView, &item);
	}
	
	// 選択状態にする
	ListView_SetItemState(hwndListView, pos, LVIS_SELECTED, LVIS_SELECTED);
	// スクロールが必要な場合は、スクロールさせる
	ListView_EnsureVisible(hwndListView, pos, FALSE);
}

/**
 * 一般設定ページのプロシージャ
 */
LRESULT CALLBACK GeneralDialogProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) {
	// この関数内でのみ使用するマクロ
	// 指定された文字列を、CLASSPATH テキストボックスに追加する
	#define ADD_TO_CLASSPATH(file) \
	{ \
		HWND hwndClasspath = GetDlgItem(hDlg, IDC_CLASSPATH); \
		int textlength = GetWindowTextLength(hwndClasspath); \
		SendMessage(hwndClasspath, EM_SETSEL, (WPARAM) textlength, (LPARAM) textlength); \
		if (textlength) { \
			SendMessage(hwndClasspath, EM_REPLACESEL, (WPARAM) FALSE, (LPARAM) _T(";")); \
		} \
		SendMessage(hwndClasspath, EM_REPLACESEL, (WPARAM) FALSE, (LPARAM) file); \
	}
	// 指定されたITEMIDLISTを開放する
	#define FREE_ITEMIDLIST(idlist) \
	{ \
		if (idlist) { \
			IMalloc* imalloc; \
			SHGetMalloc(&imalloc); \
			imalloc->Free(idlist); \
			imalloc->Release(); \
			idlist = NULL; \
		} \
	}
		
	static arguments* args;
	static LPITEMIDLIST last_idlist;
	switch (message)
	{
	case WM_INITDIALOG:
		{
			args = (arguments*) ((LPPROPSHEETPAGE)(lParam))->lParam;

			// argv
			SetWindowText(GetDlgItem(hDlg, IDC_COMMANDLINE), args->argv);
			
			// CLASSPATH
			SetWindowText(GetDlgItem(hDlg, IDC_CLASSPATH), args->classpath);
		
			if (args->jar) {
				// -jar オプションがついている場合は、CLASSPATHを編集できない
				EnableWindow(GetDlgItem(hDlg, IDC_CLASSPATH), FALSE);
				EnableWindow(GetDlgItem(hDlg, IDC_ADD_FILE), FALSE);
				EnableWindow(GetDlgItem(hDlg, IDC_ADD_FOLDER), FALSE);
			}

			// カレントディレクトリ
			SetWindowText(GetDlgItem(hDlg, IDC_CURRENT_DIRECTORY), args->current_directory);
		}
		return TRUE;

	case WM_COMMAND:
		switch (LOWORD(wParam)) {
		case IDC_ADD_FILE:
			// ファイルを追加
			{
				OPENFILENAME ofn = {0};
				_TCHAR filename[MAX_PATH + 1] = _T("");
				
				ofn.lStructSize = sizeof(OPENFILENAME);
				ofn.hwndOwner = hDlg;
				ofn.hInstance = g_hInstance;
				// *.jar, *.zip, *.*
				ofn.lpstrFilter = _T("Jar file\0*.jar\0Zip file\0*.zip\0All files\0*.*\0");
				ofn.lpstrFile = filename;
				ofn.nMaxFile = MAX_PATH;

				if (get_open_file_name(&ofn)) {
					ADD_TO_CLASSPATH(filename);
				}
				return TRUE;
			}

		case IDC_ADD_FOLDER:
			// フォルダを追加
			{
				_TCHAR path[MAX_PATH];
				MYBROWSEINFO bi = {0};
				bi.pszDisplayName = path;
				bi.hwndOwner = hDlg;
				bi.pidlRoot = last_idlist;

				LPITEMIDLIST idlist = MySHBrowseForFolder(&bi);
				if (idlist) {
					SHGetPathFromIDList(idlist, path);
					FREE_ITEMIDLIST(last_idlist);
					last_idlist = idlist;
					ADD_TO_CLASSPATH(path);
				}
			}
			return TRUE;

		case IDC_SELECT_CURRENT_DIRECTORY:
			// カレントディレクトリを設定
			{
				_TCHAR path[MAX_PATH];
				MYBROWSEINFO bi = {0};
				bi.pszDisplayName = path;
				bi.hwndOwner = hDlg;

				LPITEMIDLIST idlist = MySHBrowseForFolder(&bi);
				if (idlist) {
					SHGetPathFromIDList(idlist, path);
					FREE_ITEMIDLIST(idlist);
					SetWindowText(GetDlgItem(hDlg, IDC_CURRENT_DIRECTORY), path);
				}
			}
			return TRUE;
		}
		break;

	case WM_NOTIFY:
		{
			NMHDR* hdr = (NMHDR*) lParam;
			switch (hdr->code) {
			case PSN_APPLY:
				{
					// コントロールの内容を、argumentsにコピーする
					_TCHAR buff[256];
					
					// コマンドラインオプション
					HWND hwndArgs = GetDlgItem(hDlg, IDC_COMMANDLINE);
					GetWindowText(hwndArgs, buff, sizeof(buff) / sizeof(*buff));
					int len = _tcslen(buff);
					args->argv = (_TCHAR*) realloc(args->argv, sizeof(_TCHAR) * (len + 1));
					if (! args->argv) {
						fatal_error(FATAL_ERROR_NO_MEMORY);
					}
					_tcscpy(args->argv, buff);

					// CLASSPATH				
					GetWindowText(GetDlgItem(hDlg, IDC_CLASSPATH),
								  args->classpath,
								  sizeof(args->classpath) / sizeof(*args->classpath));

					// ITEMIDLISTを開放する
					FREE_ITEMIDLIST(last_idlist);

					// カレントディレクトリ
					GetWindowText(GetDlgItem(hDlg, IDC_CURRENT_DIRECTORY),
								  args->current_directory,
								  sizeof(args->current_directory) / sizeof(args->current_directory[0]));
				}
				return TRUE;
			}
			break;
		}
		break;
	}
	return FALSE;

#undef ADD_TO_CLASSPATH
#undef FREE_ITEMIDLIST
}

/**
 * メモリ設定ページのプロシージャ
 */
LRESULT CALLBACK MemoryDialogProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) {
	static arguments* args;
	switch (message) {
	case WM_INITDIALOG:
		{
			args = (arguments*) ((LPPROPSHEETPAGE)(lParam))->lParam;

			// 最大ヒープサイズ
			{
				// コンボボックスを初期化する
				HWND hwndUnit = GetDlgItem(hDlg, IDC_MAXHEAPSIZE_UNIT);
				SendMessage(hwndUnit, CB_ADDSTRING, 0, (LPARAM) _T("KB"));
				SendMessage(hwndUnit, CB_ADDSTRING, 0, (LPARAM) _T("MB"));
				
				// 縦方向のサイズを調整する
				RECT rect;
				GetWindowRect(hwndUnit, &rect);
				SetWindowPos(hwndUnit,
							 NULL,
							 0,
							 0,
							 rect.right - rect.left,
							 (rect.bottom - rect.top) * 3,
							 SWP_NOMOVE);

				// テキストエリアに初期値を表示する
				int value = args->max_heap_size;
				if (value % (1024 * 1024)) {
					// KBに変換
					value /= 1024;	
					SendMessage(hwndUnit, CB_SETCURSEL, 0, 0);
				} else {
					// MBに変換
					value /= (1024 * 1024);
					SendMessage(hwndUnit, CB_SETCURSEL, 1, 0);
				}

				HWND hwndSize = GetDlgItem(hDlg, IDC_MAXHEAPSIZE);
				_TCHAR buff[32];
				_itot(value, buff, 10);
				SetWindowText(hwndSize, buff);
			}
			// Javaスタックサイズ
			{
				// テキストエリアに初期値を表示する
				int value = args->java_stack_size;
				// KBに変換
				value /= 1024;	

				HWND hwndSize = GetDlgItem(hDlg, IDC_JAVA_STACK_SIZE);
				_TCHAR buff[32];
				_itot(value, buff, 10);
				SetWindowText(hwndSize, buff);
			}
			// ネイティブスタックサイズ
			{
				// テキストエリアに初期値を表示する
				int value = args->native_stack_size;
				// KBに変換
				value /= 1024;	

				HWND hwndSize = GetDlgItem(hDlg, IDC_NATIVE_STACK_SIZE);
				_TCHAR buff[32];
				_itot(value, buff, 10);
				SetWindowText(hwndSize, buff);
			}

			return TRUE;
		}
		break;

	case WM_NOTIFY:
		{
			NMHDR* hdr = (NMHDR*) lParam;
			switch (hdr->code) {
			case PSN_APPLY:
				{
					// コントロールの内容を、argumentsにコピーする
					_TCHAR buff[256];
					// 最大ヒープサイズ
					{
						GetWindowText(GetDlgItem(hDlg, IDC_MAXHEAPSIZE),
									  buff,
									  (sizeof(buff) / sizeof(*buff)));
						int value = _ttoi(buff);
						GetWindowText(GetDlgItem(hDlg, IDC_MAXHEAPSIZE_UNIT),
									  buff,
									  (sizeof(buff) / sizeof(*buff)));
						if (_tcscmp(buff, _T("KB")) == 0) {
							value *= 1024;
						} else if (_tcscmp(buff, _T("MB")) == 0) {
							value *= (1024 * 1024);
						}
						if (value > 0) {
							// 最大ヒープサイズを設定する
							args->max_heap_size = value;
						}
					}
					// Javaスタックサイズ
					{
						GetWindowText(GetDlgItem(hDlg, IDC_JAVA_STACK_SIZE),
									  buff,
									  (sizeof(buff) / sizeof(*buff)));
						int value = _ttoi(buff);
						value *= 1024;
						if (value > 0) {
							// Javaスタックサイズを設定する
							args->java_stack_size = value;
						}
					}
					// ネイティブスタックサイズ
					{
						GetWindowText(GetDlgItem(hDlg, IDC_NATIVE_STACK_SIZE),
									  buff,
									  (sizeof(buff) / sizeof(*buff)));
						int value = _ttoi(buff);
						value *= 1024;
						if (value > 0) {
							// ネイティブスタックサイズを設定する
							args->native_stack_size = value;
						}
					}
				}
				return TRUE;
			}
			break;
		}
		break;
	}
	return FALSE;
}

/**
 * プロパティ設定ページのプロシージャ
 */
LRESULT CALLBACK PropertiesDialogProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) {
	static arguments* args;
	switch (message) {
	case WM_INITDIALOG:
		{
			args = (arguments*) ((LPPROPSHEETPAGE)(lParam))->lParam;

			// System properties
			RECT rect;
			HWND hwndList = GetDlgItem(hDlg, IDC_PROPERTIES);
			GetWindowRect(hwndList, &rect);
			LVCOLUMN lc = {0};
			lc.mask = LVCF_WIDTH | LVCF_TEXT;
			lc.cx = (rect.right - rect.left) / 4;
			lc.pszText = _T("Key");
			ListView_InsertColumn(hwndList, 0, &lc);
			lc.cx = (rect.right - rect.left) - (lc.cx * 2);
			lc.pszText = _T("Value");
			ListView_InsertColumn(hwndList, 1, &lc);

			set_system_properties(hwndList, args);

			// カラムサイズを自動調節する
			ListView_SetColumnWidth(hwndList, 0, LVSCW_AUTOSIZE);
			ListView_SetColumnWidth(hwndList, 1, LVSCW_AUTOSIZE);
		}
		return TRUE;

	case WM_NOTIFY:
		if (wParam == IDC_PROPERTIES) {
			// プロパティリストから送られてきた場合
			// リストビューの選択状態によって、ボタンの有効／無効を切り替える
			LV_DISPINFO* lvinfo = (LV_DISPINFO *)lParam;
			switch (lvinfo->hdr.code) {
			case LVN_ITEMCHANGED:
				{
					HWND hwndList = GetDlgItem(hDlg, IDC_PROPERTIES);
					int index = ListView_GetNextItem(
									hwndList,
									-1,				// リストの先頭から検索
									LVNI_SELECTED);	// 選択されているアイテムを検索
					HWND hwndEdit = GetDlgItem(hDlg, IDC_EDIT);
					HWND hwndDelete = GetDlgItem(hDlg, IDC_DELETE);
					if (index == -1) {
						if (IsWindowEnabled(hwndEdit)) {
							EnableWindow(GetDlgItem(hDlg, IDC_EDIT), FALSE);
						}
						if (IsWindowEnabled(hwndDelete)) {
							EnableWindow(GetDlgItem(hDlg, IDC_DELETE), FALSE);
						}
					} else {
						if (! IsWindowEnabled(hwndEdit)) {
							EnableWindow(GetDlgItem(hDlg, IDC_EDIT), TRUE);
						}
						if (! IsWindowEnabled(hwndDelete)) {
							EnableWindow(GetDlgItem(hDlg, IDC_DELETE), TRUE);
						}
					}
				}
				return TRUE;
			}
		} else {
			// プロパティシートのメッセージ
			NMHDR* hdr = (NMHDR*) lParam;
			switch (hdr->code) {
			case PSN_APPLY:
				{
					// コントロールから設定値をコピーする
					// システムプロパティ
					arguments_clear_all_properties(args);

					HWND hwndList = GetDlgItem(hDlg, IDC_PROPERTIES);
					int properties_count = ListView_GetItemCount(hwndList);
					LVITEM item = {0};
					item.mask = LVIF_TEXT;
					for (int i = 0; i < properties_count; ++i) {
						property prop;
						item.iItem = i;
						item.iSubItem = 0;
						item.cchTextMax = sizeof(prop.key) / sizeof(*prop.key);
						item.pszText = prop.key;
						ListView_GetItem(hwndList, &item);

						item.iSubItem = 1;
						item.cchTextMax = sizeof(prop.value) / sizeof(*prop.value);
						item.pszText = prop.value;
						ListView_GetItem(hwndList, &item);
						arguments_put_property(args, prop.key, prop.value);
					}
				}
				return TRUE;
			}
			break;
		}
	
	case WM_COMMAND:
		switch (LOWORD(wParam)) {
		case IDC_ADD:
			// プロパティエディタを表示する
			{
				property prop = {0};
				DialogBoxParam(g_hInstance,
						  MAKEINTRESOURCE(IDD_PROPERTY_EDITOR),
						  hDlg,
						  (DLGPROC) SystemPropertyEditorDialogProc,
						  (LPARAM) &prop);
				if (_tcslen(prop.key)) {
					// リストビューに追加する
					HWND hwndList = GetDlgItem(hDlg, IDC_PROPERTIES);
					put_system_property(hwndList, prop.key, prop.value);

					// カラムサイズを自動調節する
					ListView_SetColumnWidth(hwndList, 0, LVSCW_AUTOSIZE);
					ListView_SetColumnWidth(hwndList, 1, LVSCW_AUTOSIZE);
				}
			}
			return TRUE;
		
		case IDC_EDIT:
			// プロパティを編集する
			{
				HWND hwndList = GetDlgItem(hDlg, IDC_PROPERTIES);
				int index = ListView_GetNextItem(hwndList, -1, LVNI_SELECTED);
				if (index != -1) {
					property prop;
					LVITEM item = {0};
					item.mask = LVIF_TEXT;
					
					item.iItem = index;
					item.iSubItem = 0;
					item.pszText = prop.key;
					item.cchTextMax = sizeof(prop.key) / sizeof(*prop.key);
					ListView_GetItem(hwndList, &item);
					
					item.iSubItem = 1;
					item.pszText = prop.value;
					item.cchTextMax = sizeof(prop.value) / sizeof(*prop.value);
					ListView_GetItem(hwndList, &item);

					DialogBoxParam(g_hInstance,
							  MAKEINTRESOURCE(IDD_PROPERTY_EDITOR),
							  hDlg,
							  (DLGPROC) SystemPropertyEditorDialogProc,
							  (LPARAM) &prop);
					if (_tcslen(prop.key)) {
						// リストビューの内容を修正する
						put_system_property(hwndList, prop.key, prop.value);
						// カラムサイズを自動調節する
						ListView_SetColumnWidth(hwndList, 0, LVSCW_AUTOSIZE);
						ListView_SetColumnWidth(hwndList, 1, LVSCW_AUTOSIZE);
					}
				}
			}
			return TRUE;

		case IDC_DELETE:
			// プロパティを削除する
			{
				HWND hwndList = GetDlgItem(hDlg, IDC_PROPERTIES);
				int index = ListView_GetNextItem(hwndList, -1, LVNI_SELECTED);
				if (index != -1) {
					ListView_DeleteItem(hwndList, index);
					// カラムサイズを自動調節する
					ListView_SetColumnWidth(hwndList, 0, LVSCW_AUTOSIZE);
					ListView_SetColumnWidth(hwndList, 1, LVSCW_AUTOSIZE);
				}
			}
			return TRUE;

		}
	}
	return FALSE;
}

/**
 * ベリファイヤ設定ページのプロシージャ
 */
LRESULT CALLBACK VerifierDialogProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) {
	static arguments* args;
	switch (message) {
	case WM_INITDIALOG:
		{
			args = (arguments*) ((LPPROPSHEETPAGE)(lParam))->lParam;

			// コントロールの内容を初期化する
			// ベリファイモード
			switch (args->verify_mode) {
			case VERIFY_MODE_NONE:
				// -noverify
				SendMessage(GetDlgItem(hDlg, IDC_VERIFY_NONE),
							BM_SETCHECK,
							BST_CHECKED,
							0);
				break;

			case VERIFY_MODE_ALL:
				// -verify
				SendMessage(GetDlgItem(hDlg, IDC_VERIFY_ALL),
							BM_SETCHECK,
							BST_CHECKED,
							0);
				break;
			}

		}
		return TRUE;
	
	case WM_NOTIFY:
		{
			switch (((NMHDR*) lParam)->code) {
			case PSN_APPLY:
				{
					// ベリファイモード
					if (BST_CHECKED == SendMessage(GetDlgItem(hDlg, IDC_VERIFY_ALL),
													BM_GETCHECK,
													0,
													0)) {
						args->verify_mode = VERIFY_MODE_ALL;
					} else {
						args->verify_mode = VERIFY_MODE_NONE;
					}

				}
				return TRUE;
			}
			break;
		}
	}

	return FALSE;
}

/**
 * その他設定ページのプロシージャ
 */
LRESULT CALLBACK MiscDialogProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) {
	static arguments* args;
	switch (message) {
	case WM_INITDIALOG:
		{
			args = (arguments*) ((LPPROPSHEETPAGE)(lParam))->lParam;

			// コントロールの内容を初期化する

			// logfile
			SetWindowText(GetDlgItem(hDlg, IDC_LOGFILE), args->logfile);
			
			// Bootclasspath
			SetWindowText(GetDlgItem(hDlg, IDC_BOOTCLASSPATH), args->boot_classpath);

			// assertion
			SendMessage(GetDlgItem(hDlg, IDC_ENABLEASSERTIONS),
						BM_SETCHECK,
						(WPARAM) (args->enableAssertions ? BST_CHECKED : BST_UNCHECKED),
						0);

			// -verbose:gc
			SendMessage(GetDlgItem(hDlg, IDC_VERBOSE_GC),
						BM_SETCHECK,
						(WPARAM) (args->verbose_gc ? BST_CHECKED : BST_UNCHECKED),
						0);

			// -Xhidevmwindow
			SendMessage(GetDlgItem(hDlg, IDC_HIDE_VM_WINDOW),
						BM_SETCHECK,
						(WPARAM) (args->hide_vm_window ? BST_CHECKED : BST_UNCHECKED),
						0);

			// -Xusesdio
			SendMessage(GetDlgItem(hDlg, IDC_USE_STDIO),
						BM_SETCHECK,
						(WPARAM)( args->use_stdio ? BST_CHECKED : BST_UNCHECKED),
						0);

		}
		return TRUE;
	
	case WM_NOTIFY:
		{
			switch (((NMHDR*) lParam)->code) {
			case PSN_APPLY:
				{
					// ログファイルを設定する
					GetWindowText(GetDlgItem(hDlg, IDC_LOGFILE),
								  args->logfile,
								  (sizeof(args->logfile) / sizeof(*args->logfile)));

					// Bootclasspath
					GetWindowText(GetDlgItem(hDlg, IDC_BOOTCLASSPATH),
								  args->boot_classpath,
								  sizeof(args->boot_classpath) / sizeof(*args->boot_classpath));

					// assertionの有効／無効を設定する
					args->enableAssertions
						= (BST_CHECKED == SendMessage(GetDlgItem(hDlg, IDC_ENABLEASSERTIONS),
													  BM_GETCHECK,
													  0 ,
													  0)) ? true : false;

					// -verbose:gc
					args->verbose_gc = 
						(BST_CHECKED == SendMessage(GetDlgItem(hDlg, IDC_VERBOSE_GC),
													BM_GETCHECK,
													0,
													0)) ? true : false;
					// ベリファイモード
					if (BST_CHECKED == SendMessage(GetDlgItem(hDlg, IDC_VERIFY_ALL),
													BM_GETCHECK,
													0,
													0)) {
						args->verify_mode = VERIFY_MODE_ALL;
					} else {
						args->verify_mode = VERIFY_MODE_NONE;
					}

					// VMウインドウ非表示
					if (BST_CHECKED == SendMessage(GetDlgItem(hDlg, IDC_HIDE_VM_WINDOW),
												   BM_GETCHECK,
												   0,
												   0)) {
						args->hide_vm_window = true;
					} else {
						args->hide_vm_window = false;
					}

					// stdioを使う
					if (BST_CHECKED == SendMessage(GetDlgItem(hDlg, IDC_USE_STDIO),
												   BM_GETCHECK,
												   0,
												   0)) {
						args->use_stdio = true;
					} else {
						args->use_stdio = false;
					}				}
				return TRUE;
			}
			break;
		}
	}

	return FALSE;
}

/**
 * システムプロパティエディタダイアログのプロシージャ
 */
LRESULT CALLBACK SystemPropertyEditorDialogProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) {
	static property* prop;
	switch (message)
	{
		case WM_INITDIALOG:
			{
			
				prop = (property*) lParam;

				// コントロールを初期化する
				SetDlgItemText(hDlg, IDC_KEY, prop->key);
				SetDlgItemText(hDlg, IDC_VALUE, prop->value);
			}
			return TRUE; 
		
		case WM_COMMAND:
			switch (LOWORD(wParam)) {
			case IDC_KEY:
				{
					if (HIWORD(wParam) == EN_CHANGE) {
						BOOL enabled = FALSE;
						if (GetWindowTextLength(GetDlgItem(hDlg, IDC_KEY))) {
							enabled = TRUE;
						}
						if (enabled != IsWindowEnabled(GetDlgItem(hDlg, IDOK))) {
							EnableWindow(GetDlgItem(hDlg, IDOK), enabled);
						}
					}
				}
				return TRUE;

			case IDOK:
				{
					// コントロールから値をコピーする
					GetDlgItemText(hDlg, IDC_KEY, prop->key, sizeof(prop->key) / sizeof(*prop->key));
					GetDlgItemText(hDlg, IDC_VALUE, prop->value, sizeof(prop->key) / sizeof(*prop->key));

					// ダイアログを終了する
					EndDialog(hDlg, 1);
				}
				return TRUE;

			case IDCANCEL:
				// ダイアログを終了する
				EndDialog(hDlg, 0);
				return TRUE;
			}
	}
    return FALSE;
}

/**
 * プロパティシートのコールバック関数
 */
int PropSheetProc(HWND hwnd, UINT uMsg, LPARAM lParam) {
	// 以下のURLを参考に作成した
	// http://msdn.microsoft.com/library/default.asp?url=/library/en-us/win_ce/html/pwc_PropertySheets.asp
	
	switch (uMsg) {
	case PSCB_INITIALIZED:
		{
			// タブを一番下に持ってくる
			HWND hwndTabs = GetDlgItem(hwnd, 0x3020);
			DWORD dwStyle = GetWindowLong (hwndTabs, GWL_STYLE);
			SetWindowLong (hwndTabs, GWL_STYLE, dwStyle | TCS_BOTTOM);

			// メニューを消す
			SHINITDLGINFO shidi = {0};
			shidi.dwMask = SHIDIM_FLAGS;
			shidi.dwFlags = SHIDIF_SIZEDLGFULLSCREEN | SHIDIF_EMPTYMENU;
			shidi.hDlg = hwnd;
			SHInitDialog(&shidi);
		}
		break;

	case PSCB_GETVERSION:
		return COMCTL32_VERSION;
	}

	return 1;
}

/**
 * "Advanced" プロパティシートを表示する
 */
static void show_options_dialog(HWND hwndParent, arguments* args) {
	PROPSHEETPAGE psp = {0};
	HPROPSHEETPAGE pages[16];	// ページ数が増えた場合には変更が必要

	psp.dwSize = sizeof(PROPSHEETPAGE);
	psp.dwFlags = PSP_USETITLE;
	psp.hInstance = g_hInstance;

	int page = 0;
	// Command line
    psp.pszTemplate = MAKEINTRESOURCE(IDD_GENERAL);
    psp.pfnDlgProc = (DLGPROC) GeneralDialogProc;
	psp.lParam = (LPARAM) args;
	psp.pszTitle = _T("General");
	pages[page] = CreatePropertySheetPage(&psp);

	page++;

	// Memory
    psp.pszTemplate = MAKEINTRESOURCE(IDD_MEMORY);
	psp.pfnDlgProc = (DLGPROC) MemoryDialogProc;
	psp.pszTitle = _T("Memory");
	pages[page] = CreatePropertySheetPage(&psp);

	page++;

	// Properties
    psp.pszTemplate = MAKEINTRESOURCE(IDD_PROPERTIES);
	psp.pfnDlgProc = (DLGPROC) PropertiesDialogProc;
	psp.pszTitle = _T("Properties");
	pages[page] = CreatePropertySheetPage(&psp);

	page++;

	// Verifier
    psp.pszTemplate = MAKEINTRESOURCE(IDD_VERIFIER);
	psp.pfnDlgProc = (DLGPROC) VerifierDialogProc;
	psp.pszTitle = _T("Verifier");
	pages[page] = CreatePropertySheetPage(&psp);

	page++;

	// Misc
    psp.pszTemplate = MAKEINTRESOURCE(IDD_MISC);
	psp.pfnDlgProc = (DLGPROC) MiscDialogProc;
	psp.pszTitle = _T("Misc");
	pages[page] = CreatePropertySheetPage(&psp);

	page++;

	PROPSHEETHEADER psh = {0};
    psh.dwSize = sizeof(PROPSHEETHEADER);
    psh.dwFlags = PSH_DEFAULT | PSH_MAXIMIZE | PSH_USECALLBACK;
    psh.hInstance = g_hInstance;
    psh.hwndParent = hwndParent;
    psh.nPages = page;
    psh.phpage = pages;
	psh.pfnCallback = PropSheetProc;
    psh.pszCaption = _T("Advanced");
    PropertySheet(&psh);
}

/**
 * ファイルの関連付けを初期化する
 */
static void init_file_associations() {
	const _TCHAR* mysaifujvm = _T("MysaifuJVM");

	// レジストリをオープン
	HKEY hKey;
	long error = RegCreateKeyEx(HKEY_CLASSES_ROOT,
								_T(".mysaifujvm"),
								0,
								NULL,
								REG_OPTION_NON_VOLATILE,
								KEY_ALL_ACCESS,
								NULL,
								&hKey,
								NULL);
	// HKEY_CLASSES_ROOT\.mysaifujvm に、"MysaifuJVM"を作成する
	error = RegSetValueEx(hKey,
						  NULL,
						  0,
						  REG_SZ,
						  (CONST BYTE*) mysaifujvm,
						  (_tcslen(mysaifujvm) + 1) * sizeof(_TCHAR));
	RegCloseKey(hKey);
	
	// HKEY_CLASSES_ROOT\MysaifuJVM\shell\open\command\ キーに値を設定する
	error = RegCreateKeyEx(HKEY_CLASSES_ROOT,
						   mysaifujvm,
						   0,
						   NULL,
						   REG_OPTION_NON_VOLATILE,
						   KEY_ALL_ACCESS,
						   NULL,
						   &hKey,
						   NULL);
	HKEY hShellKey;
	error = RegCreateKeyEx(hKey,
						   _T("shell"),
						   0,
						   NULL,
						   REG_OPTION_NON_VOLATILE,
						   KEY_ALL_ACCESS,
						   NULL,
						   &hShellKey,
						   NULL);
	HKEY hOpenKey;
	error = RegCreateKeyEx(hShellKey,
						   _T("open"),
						   0,
						   NULL,
						   REG_OPTION_NON_VOLATILE,
						   KEY_ALL_ACCESS,
						   NULL,
						   &hOpenKey,
						   NULL);
	HKEY hCommandKey;
	error = RegCreateKeyEx(hOpenKey,
						   _T("command"),
						   0,
						   NULL,
						   REG_OPTION_NON_VOLATILE,
						   KEY_ALL_ACCESS,
						   NULL,
						   &hCommandKey,
						   NULL);
	
	// 実行ファイル名を取得し、レジストリに設定する
	_TCHAR exename[MAX_PATH + 1];
	GetModuleFileName(GetModuleHandle(NULL), exename, sizeof(exename) / sizeof(exename[0]));
	_TCHAR value[1024];
	_sntprintf(value,
			   sizeof(value) / sizeof(value[0]),
			   _T("\"%s\" -Xconfig %%1"),
			   exename);
	error = RegSetValueEx(hCommandKey,
						  NULL,
						  0,
						  REG_SZ,
						  (const BYTE*) value,
						  (_tcslen(value) + 1) * sizeof(_TCHAR));

	// デフォルトアイコンを設定する
	HKEY hDefaultIconKey;
	RegCreateKeyEx(hKey,
				   _T("DefaultIcon"),
					   0,
					   NULL,
					   REG_OPTION_NON_VOLATILE,
					   KEY_ALL_ACCESS,
					   NULL,
					   &hDefaultIconKey,
					   NULL);
	_sntprintf(value,
			   sizeof(value) / sizeof(value[0]),
			   _T("%s,-%d"),
			   exename,
			   IDI_MYSAIFU);
	RegSetValueEx(hDefaultIconKey,
				  NULL,
				  0,
				  REG_SZ,
				  (const BYTE*) value,
				  (_tcslen(value) + 1) * sizeof(_TCHAR));

	RegCloseKey(hDefaultIconKey);
	RegCloseKey(hCommandKey);
	RegCloseKey(hOpenKey);
	RegCloseKey(hShellKey);
	RegCloseKey(hKey);
}

static BOOL get_open_file_name(OPENFILENAME* ofn) {
	// gsgetfile.dll が存在する場合は、そちらを使用する
	HMODULE hModule = LoadLibrary(_T("gsgetfile.dll"));
	if (hModule) {
		typedef BOOL (*GSGETOPENFILENAME)(OPENFILENAME* pofn);
		GSGETOPENFILENAME gsGetOpenFileName
			= (GSGETOPENFILENAME) GetProcAddress(hModule, _T("gsGetOpenFileName"));
		if (gsGetOpenFileName) {
			return gsGetOpenFileName(ofn);
		}
	}

	// 標準のダイアログを表示する
	return GetOpenFileName(ofn);
}

static BOOL get_save_file_name(OPENFILENAME* ofn) {
	// gsgetfile.dll が存在する場合は、そちらを使用する
	HMODULE hModule = LoadLibrary(_T("gsgetfile.dll"));
	if (hModule) {
		typedef BOOL (*GSGETSAVEFILENAME)(OPENFILENAME* pofn);
		GSGETSAVEFILENAME gsGetSaveFileName
			= (GSGETSAVEFILENAME) GetProcAddress(hModule, _T("gsGetSaveFileName"));
		if (gsGetSaveFileName) {
			return gsGetSaveFileName(ofn);
		}
	}

	// 標準のダイアログを表示する
	return GetSaveFileName(ofn);
}

/**
 * ファイルの「最終更新日付」を比較する関数（逆順ソート用）
 */
static int compare_last_write_time(WIN32_FIND_DATA* data1, WIN32_FIND_DATA* data2) {
	LARGE_INTEGER time1;
	time1.HighPart = data1->ftLastWriteTime.dwHighDateTime;
	time1.LowPart = data1->ftLastWriteTime.dwLowDateTime;
	LARGE_INTEGER time2;
	time2.HighPart = data2->ftLastWriteTime.dwHighDateTime;
	time2.LowPart = data2->ftLastWriteTime.dwLowDateTime;

	// 逆順にソートする
	__int64 diff = time2.QuadPart - time1.QuadPart;
	if (diff < 0) {
		return -1;
	} else if (diff == 0) {
		return 0;
	} else {
		return 1;
	}
}
