/**
 * VMProcessのWindows CE実装
 */
#include <jni.h>

JNIEXPORT jint JNICALL Java_java_lang_VMProcess_createNativeProcess(JNIEnv* env, jobject obj, jobjectArray cmd) {
	_TCHAR* progname = NULL;
	_TCHAR* params = NULL;
	jint cmdlen = (*env)->GetArrayLength(env, cmd);
	PROCESS_INFORMATION pinfo = {0};

	if (cmdlen > 0) {
		// 先頭の要素がプログラム名
		jstring tmpstr = (*env)->GetObjectArrayElement(env, cmd, 0);
		jint tmplen = (*env)->GetStringLength(env, tmpstr);
		const _TCHAR* tmpbuff = (*env)->GetStringChars(env, tmpstr, NULL);
		progname = (_TCHAR*) malloc(sizeof(_TCHAR) * (tmplen + 1));
		_tcsncpy(progname, tmpbuff, tmplen);
		progname[tmplen] = _T('\0');
		(*env)->ReleaseStringChars(env, tmpstr, 0);
	}
	
	// 引数を _TCHAR に変換する
	if (cmdlen > 1) {
		jint i;
		jint totallen = 0;
		for (i = 1; i < cmdlen; ++i) {
			jstring tmp = (*env)->GetObjectArrayElement(env, cmd, i);
			jint len = (*env)->GetStringLength(env, tmp);
			const _TCHAR* tmpchars = (*env)->GetStringChars(env, tmp, NULL);
			params = (_TCHAR*) realloc(params, sizeof(_TCHAR) * (totallen + len + 2));
			if (params == NULL) {
				break;
			}
			_tcsncpy(params + totallen, tmpchars, len);
			(*env)->ReleaseStringChars(env, tmp, 0);
			params[totallen + len] = _T(' ');
			params[totallen + len + 1] = _T('\0');

			totallen += len + 1;
		}

	}
	
	if (CreateProcess(progname, params, NULL, NULL, FALSE, 0, NULL, NULL, NULL, &pinfo)) {
		// プロセスの生成に成功
		// プロセスハンドルは使用しないのですぐにクローズする
		CloseHandle(pinfo.hProcess);
		CloseHandle(pinfo.hThread);
	} else {
		DWORD lastError = GetLastError();
		_TCHAR* message;
		// エラーメッセージをフォーマットする
		if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
 						  NULL,
						  lastError,
						  LANG_USER_DEFAULT,
						  (LPTSTR) &message,
						  0,
						  NULL)) {
			jclass clazz = (*env)->FindClass(env, "java/io/IOException");
			jmethodID mid = (*env)->GetMethodID(env,
												clazz,
												"<init>",
												"(Ljava/lang/String;)V");
			jstring arg = (*env)->NewString(env, message, _tcslen(message));

			jthrowable ioexception = (*env)->NewObject(env,
														clazz,
														mid,
														arg);
			(*env)->Throw(env, ioexception);
		}
		LocalFree(message);
	}

	// メモリを開放する
	free(progname);
	free(params);

	return pinfo.dwProcessId;
}

JNIEXPORT jint JNICALL Java_java_lang_VMProcess_waitForNative(JNIEnv* env, jobject obj, jint pid) {
	DWORD exitcode;
	HANDLE hProcess = OpenProcess(0,
								  FALSE,
								  (DWORD) pid);
	if (hProcess) {
		// プロセスの終了を待つ
		WaitForSingleObject(hProcess, INFINITE);
		GetExitCodeProcess(hProcess, &exitcode);
		CloseHandle(hProcess);
	}
	return (jint) exitcode;
}

JNIEXPORT jint JNICALL Java_java_lang_VMProcess_getNativeExitCode(JNIEnv* env, jobject obj, jint pid) {
	DWORD exitcode;
	HANDLE hProcess = OpenProcess(0,
								  FALSE,
								  (DWORD) pid);
	if (hProcess) {
		GetExitCodeProcess(hProcess, &exitcode);
		CloseHandle(hProcess);
	}
	return (jint) exitcode;
}

JNIEXPORT void JNICALL Java_java_lang_VMProcess_destroyNative(JNIEnv* env, jobject obj, jint pid) {
	HANDLE hProcess = OpenProcess(0,
								  FALSE,
								  (DWORD) pid);
	if (hProcess) {
		// プロセスを強制終了させる
		TerminateProcess(hProcess, 0);
		CloseHandle(hProcess);
	}

}