/* radare - LGPL - Copyright 2019-2024 - gustavo, pancake */

#include <r_types.h>

#if R2__WINDOWS__
#include <windows.h>
#include <r_util/r_assert.h>
#include <r_util/r_w32dw.h>

static DWORD WINAPI __w32dbg_thread(LPVOID param) {
	RW32Dw *inst = param;
	RW32DwParams *params = &inst->params;
	PROCESS_INFORMATION *pi = &inst->pi;
	for (;;) {
		WaitForSingleObject (inst->request_sem, INFINITE);
		switch (params->type) {
		case W32_CONTINUE:
			params->ret = ContinueDebugEvent (pi->dwProcessId, pi->dwThreadId, params->continue_status);
			break;
		case W32_WAIT:
			params->ret = WaitForDebugEvent (params->wait.de, params->wait.wait_time);
			if (params->ret) {
				pi->dwProcessId = params->wait.de->dwProcessId;
				pi->dwThreadId = params->wait.de->dwThreadId;
			}
			break;
		case W32_CALL_FUNC:
			params->ret = params->func.func (params->func.user);
			break;
		case W32_ATTACH:
			params->ret = DebugActiveProcess (pi->dwProcessId);
			break;
		case W32_DETACH:
		case W32_STOP:
			params->ret = DebugActiveProcessStop (pi->dwProcessId);
			break;
		default:
			break;
		}
		if (!params->ret) {
			params->err = GetLastError ();
		}
		ReleaseSemaphore (inst->result_sem, 1, NULL);
		if (params->type == W32_STOP) {
			break;
		}
	}
	return 0;
}

R_API RW32Dw *r_w32dw_new(void) {
	RW32Dw *inst = R_NEW0 (RW32Dw);
	if (R_LIKELY (inst)) {
		inst->request_sem = CreateSemaphore (NULL, 0, 1, NULL);
		inst->result_sem = CreateSemaphore (NULL, 0, 1, NULL);
		inst->debugThread = CreateThread (NULL, 0, __w32dbg_thread, inst, 0, NULL);
	}
	return inst;
}

R_API void r_w32dw_free(RW32Dw *inst) {
	if (inst) {
		inst->params.type = W32_STOP;
		ReleaseSemaphore (inst->request_sem, 1, NULL);
		CloseHandle (inst->request_sem);
		CloseHandle (inst->result_sem);
		free (inst);
	}
}

R_API int r_w32dw_waitret(RW32Dw *inst) {
	R_RETURN_VAL_IF_FAIL (inst, -1);
	ReleaseSemaphore (inst->request_sem, 1, NULL);
	WaitForSingleObject (inst->result_sem, INFINITE);
	return r_w32dw_ret (inst);
}
#endif
