// Devenv.c
//
// This is a simple command line wrapper for rovc that handles choosing the
// correct working directory, and running programs with the correct command line
// arguments.
//
// This is helpful when developing on Windows XP since the method of dragging
// and dropping files onto programs to get the right command line arguments
// doesn't properly set working directories, and using cmd.exe is inconvenient.

#define WIN32_LEAN_AND_MEAN
#include <Windows.h>

#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <wchar.h>

BOOL FindWorkingDirectory(void);
BOOL RunProgram(LPCWSTR lpProgramName, LPCWSTR lpArguments);

#define ROVC_PATH L"bin\\rovc.exe"
#define GAME_PATH L"Rovgend.exe"
#define MAX_MODNAME 32

WCHAR workdir[MAX_PATH];
WCHAR modname[MAX_MODNAME];
WCHAR rpkname[MAX_MODNAME];

int main(void)
{
	char choice;

	if (!FindWorkingDirectory())
		return EXIT_FAILURE;
	printf("Using working directory: %ls\n", workdir);

	printf("Mod folder: ");
	_getws(modname);
	wcscpy_s(rpkname, MAX_MODNAME, modname);
	wcscat_s(rpkname, MAX_MODNAME, L".rpk");

	while ((choice = getchar()) != 'q')
	{
		if (isspace(choice))
			continue;
		switch (choice)
		{
		case 'b':
			RunProgram(ROVC_PATH, modname);
			break;
		case 'r':
			RunProgram(GAME_PATH, rpkname);
			break;
		default:
			printf("Unknown command: %c\n", choice);
			break;
		}
	}

	return EXIT_SUCCESS;
}

void PrintWin32Error(LPCSTR lpMessage, DWORD dwErrorCode)
{
	LPWSTR lpMsgBuffer;
	FormatMessageW(
		FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
		NULL, dwErrorCode, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
		(LPWSTR)&lpMsgBuffer, 0, NULL
	);
	fprintf(stderr, "%s: %ls\n", lpMessage, lpMsgBuffer);
	LocalFree(lpMsgBuffer);
}

int RemovePathComponent(LPWSTR path, int len)
{
	int i;
	for (i = len - 1; i >= 0; i--)
	{
		len--;
		if (path[i] == '\\' || path[i] == '/')
		{
			path[i + 1] = '\0';
			return len;
		}
	}
	return len;
}

// Finds the correct working directory path based on the executable path of this program.
// This assumes that the program is located in (gamedir)/bin/devenv.exe
BOOL FindWorkingDirectory(void)
{
	DWORD pathLen;
	int i;

	pathLen = GetModuleFileNameW(NULL, workdir, MAX_PATH);
	if (pathLen == 0)
	{
		PrintWin32Error("Could not get executable path", GetLastError());
		return FALSE;
	}

	pathLen = RemovePathComponent(workdir, pathLen); // Remove executable name from path
	pathLen = RemovePathComponent(workdir, pathLen); // Remove bin/ directory from path

	// TODO: Verify the path by checking that it contains Rovgend.exe

	return TRUE;
}

#define CMDBUF_SIZE 512
BOOL RunProgram(LPCWSTR lpProgramName, LPCWSTR lpArguments)
{
	STARTUPINFOW si;
	PROCESS_INFORMATION pi;
	DWORD exitCode;

	WCHAR exepath[CMDBUF_SIZE];
	WCHAR cmdline[CMDBUF_SIZE];

	wcscpy_s(exepath, CMDBUF_SIZE, workdir);
	wcscat_s(exepath, CMDBUF_SIZE, lpProgramName);

	wcscpy_s(cmdline, CMDBUF_SIZE, lpProgramName);
	wcscat_s(cmdline, CMDBUF_SIZE, L" ");
	wcscat_s(cmdline, CMDBUF_SIZE, lpArguments);

	wprintf(L"Running %s as %s\n", exepath, cmdline);

	if (!CreateProcessW(exepath, cmdline, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi))
	{
		PrintWin32Error("Could not create process", GetLastError());
		return FALSE;
	}

	WaitForSingleObject(pi.hProcess, INFINITE);

	if (!GetExitCodeProcess(pi.hProcess, &exitCode))
	{
		PrintWin32Error("Could not get exit code", GetLastError());
	}
	printf("Process exited with code %d\n", exitCode);

	CloseHandle(pi.hProcess);
	CloseHandle(pi.hThread);

	return TRUE;
}
