#include "Common.h"
#ifdef HAVE_TOOLHELP32
#include <Tlhelp32.h>
#endif
#include "FindGTASA.h"

#define RGSA "Rockstar Games\\GTA San Andreas"
#define REG_KEY_NAME "SOFTWARE\\"##RGSA##"\\Installation"
#define REG_VALUE_NAME "ExePath"
#define ProgramFiles "ProgramFiles"
#define STR1 "gta_sa.exe"
#define STR2 "C:\\Program Files"
#define STR3 "C:\\"
#define WINDOW_NAME "GTA: San Andreas"

char gta_saFullPath[MAX_PATH];
char gta_saFolder[MAX_PATH];

static bool fileExists(const char* fileName)
{
	DWORD l;
	if (!fileName)
		return false;
	l = GetFileAttributes(fileName);
	if (l == (DWORD) -1) {
		l = GetLastError();
		if (l == ERROR_FILE_NOT_FOUND || l == ERROR_PATH_NOT_FOUND)
			return false;
	}
	return true;
}

static int findGTASA2()
{
	/*
	 * Check usual Program Files area
	 */
	DWORD l = GetEnvironmentVariable(
		ProgramFiles,
		gta_saFolder,
		MAX_PATH);
	if (l == 0 || l + sizeof(RGSA) + sizeof(STR1) >= MAX_PATH)
		strcpy(gta_saFolder, STR2 "\\" RGSA);
	else
		strcat(gta_saFolder, "\\" RGSA);
	strcpy(gta_saFullPath, gta_saFolder);
	strcat(gta_saFullPath, "\\" STR1);
	return 1;
}

int findGTASA()
{
	HKEY k;

	/*
	 * Check current directory
	 */
	if (fileExists(STR1)) {
		strcpy(gta_saFullPath, STR1);
		gta_saFolder[0] = '\0';
		return 1;
	}
	/*
	 * Check registry for path
	 */
	if (RegOpenKey(
			HKEY_LOCAL_MACHINE,
			REG_KEY_NAME,
			&k) != ERROR_SUCCESS)
		return findGTASA2();
	DWORD t, s = MAX_PATH;
	if (RegQueryValueEx(
			k,
			REG_VALUE_NAME,
			NULL,
			&t,
			(LPBYTE) gta_saFullPath,
			&s) != ERROR_SUCCESS ||
		t != REG_SZ) {
		CloseHandle(k);
		return findGTASA2();
	}
	CloseHandle(k);
	/*
	 * The registry string is surrounded by "", remove them
	 */
	if (gta_saFullPath[0] == '"') {
		strcpy(gta_saFolder, gta_saFullPath + 1);
		strcpy(gta_saFullPath, gta_saFolder);
		gta_saFullPath[strlen(gta_saFullPath) - 1] = '\0';
	} else
		strcpy(gta_saFolder, gta_saFullPath);
	if (!fileExists(gta_saFullPath))
		return findGTASA2();
	char* p = strrchr(gta_saFolder, '\\');
	if (p)
		*p = '\0';
	else
		strcpy(gta_saFolder, STR3);
	return 1;
}

#ifdef HAVE_TOOLHELP32
/*
 * This is code to locate and open a running gta_sa.exe.
 *   Use NtQuerySystemInformation (ntdll.dll)
 *       instead of ToolHelp32 (kernel32.dll) ?
 */
HANDLE findGTASAProcess(PDWORD pLastError)
{
	DWORD lastError;
	static PROCESSENTRY32 pe;

	HANDLE h = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
	if (h == INVALID_HANDLE_VALUE) {
		lastError = GetLastError();
#ifdef STDIO_SUPPORT
		fprintf(stderr, "CreateToolhelp32Snapshot error = %u\n", lastError);
#endif
		strcpy(errorText, "findGTASAProcess: can't enumerate processes");
		if (pLastError)
			*pLastError = lastError;
		return 0;
	}
	pe.dwSize = sizeof(PROCESSENTRY32);
	BOOL rc = Process32First(h, &pe);
	while (rc && stricmp(pe.szExeFile, STR1)) {
		rc = Process32Next(h, &pe);
	}
	CloseHandle(h);
	if (!rc) {
		lastError = GetLastError();
#ifdef STDIO_SUPPORT
		if (lastError != ERROR_NO_MORE_FILES) {
			fprintf(stderr, "Process32First/Next error = %u\n", lastError);
		}
#endif
		if (pLastError)
			*pLastError = lastError;
		return 0;
	}
#if defined(STDIO_SUPPORT) && defined(_DEBUG)
	fprintf(stderr, "gta_sa.exe number threads = %u\n", pe.cntThreads);
#endif
	h = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pe.th32ProcessID);
	if (!h) {
		strcpy(errorText, "findGTASAProcess: can't open remote process");
		if (pLastError)
			*pLastError = GetLastError();
		return 0;
	}
	return h;
}
#endif /* HAVE_TOOLHELP32 */

/*
 * This code courtesy of Alper Saracoglu's San Andreas Control Center
 */
#if _MSC_VER < 1300
typedef HANDLE (WINAPI *POpenThread)(DWORD dwDesiredAccess, BOOL bInheritHandle, DWORD dwThreadId);
#endif
int findGTASAWindow(HWND& hWnd, PROCESS_INFORMATION& pi, PDWORD pLastError)
{
	hWnd = FindWindow(NULL, WINDOW_NAME);
	if (!hWnd) {
		if (pLastError)
			*pLastError = GetLastError();
		return 0;
	}
	pi.dwThreadId = GetWindowThreadProcessId(hWnd, &pi.dwProcessId);
#if _MSC_VER < 1300
	POpenThread pOpenThread = (POpenThread)
		GetProcAddress(GetModuleHandle("KERNEL32.DLL"), "OpenThread");
	pi.hThread = pOpenThread(THREAD_ALL_ACCESS, FALSE, pi.dwThreadId);
#else
	pi.hThread = OpenThread(THREAD_ALL_ACCESS, FALSE, pi.dwThreadId);
#endif
	if (!pi.hThread) {
		if (pLastError)
			*pLastError = GetLastError();
		return 0;
	}
	pi.hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pi.dwProcessId);
	if (!pi.hProcess) {
		if (pLastError)
			*pLastError = GetLastError();
		CloseHandle(pi.hThread);
		return 0;
	}
	return 1;
}