#include "Steam.h"
#include <tlhelp32.h>
#include <string>
#include "f4se_loader_common/LoaderError.h"

std::string GetRegKey(HKEY root, const char * path, char * value)
{
	std::string	result;
	HKEY		key;

	UInt32	err = RegOpenKeyEx(root, path, 0, KEY_READ, &key);
	if(!err)
	{
		UInt32	dataLen = 0;

		// lol race condition
		err = RegQueryValueEx(key, value, NULL, NULL, NULL, &dataLen);
		if(!err)
		{
			result.resize(dataLen);

			err = RegQueryValueEx(key, value, NULL, NULL, (BYTE *)&result[0], &dataLen);
			if(!err)
			{
				// trim null terminator
				if(result.length() > 0)
					result.resize(result.length() - 1);
			}
			else
			{
				_ERROR("GetRegKey: error getting value (%08X)", err);
			}
		}
		else
		{
			_ERROR("GetRegKey: error querying value length (%08X)", err);
		}

		RegCloseKey(key);
	}
	else
	{
		_ERROR("GetRegKey: registry key not found (%08X)", err);
	}

	return result;
}

std::string GetSteamRoot(void)
{
	return GetRegKey(HKEY_LOCAL_MACHINE, "SOFTWARE\\Valve\\Steam", "InstallPath");
}

std::string GetSteamClientDLLPath(void)
{
	return GetRegKey(HKEY_CURRENT_USER, "SOFTWARE\\Valve\\Steam\\ActiveProcess", "SteamClientDll");
}

bool SteamCheckPassive(void)
{
	bool	result = false;
	HANDLE	snap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
	if(snap != INVALID_HANDLE_VALUE)
	{
		PROCESSENTRY32	proc;

		proc.dwSize = sizeof(proc);

		if(Process32First(snap, &proc))
		{
			do 
			{
				if(!_stricmp(proc.szExeFile, "steam.exe"))
				{
					_MESSAGE("found steam");
					result = true;
					break;
				}
			}
			while(Process32Next(snap, &proc));
		}
		else
		{
			_ERROR("SteamCheck: Module32First failed (%d)", GetLastError());
			result = true;
		}

		CloseHandle(snap);
	}
	else
	{
		_ERROR("SteamCheck: CreateToolhelp32Snapshot failed (%d)", GetLastError());
		result = true;	// if for whatever reason we can't scan, assume steam is running
	}

	return result;
}

static bool SteamCheckActive(void)
{
	bool	result = false;

	HMODULE	steamAPI = LoadLibrary("steam_api.dll");
	if(steamAPI)
	{
		typedef bool (* _SteamAPI_IsSteamRunning)(void);

		_SteamAPI_IsSteamRunning	SteamAPI_IsSteamRunning = NULL;

		// this just checks HKCU\Software\Valve\Steam\ActiveProcess\pid
		// to see if it's running, however process IDs can be reused and it doesn't clean the registry key
		// on exit
		SteamAPI_IsSteamRunning = (_SteamAPI_IsSteamRunning)GetProcAddress(steamAPI, "SteamAPI_IsSteamRunning");

		if(SteamAPI_IsSteamRunning)
		{
			UInt32	startTime = GetTickCount();
			const UInt32	kSteamTimeout = 10 * 1000;	// 10 seconds

			while((GetTickCount() - startTime) >= kSteamTimeout)
			{
				if(SteamAPI_IsSteamRunning())
				{
					result = true;
					break;
				}

				Sleep(100);
			}

			if(!result)
			{
				_ERROR("timed out waiting for steam boot");
			}
		}
		else
		{
			_ERROR("couldn't get steam API ptr");
		}

		FreeLibrary(steamAPI);
	}
	else
	{
		_ERROR("couldn't load steam API DLL (%08X)", GetLastError());
	}

	return result;
}

bool SteamLaunch(void)
{
	std::string	steamRoot = GetSteamRoot();
	_MESSAGE("steam root = %s", steamRoot.c_str());

	if(steamRoot.empty())
		return false;

	std::string	steamEXEPath = steamRoot + "\\Steam.exe";

	STARTUPINFO			startupInfo = { 0 };
	PROCESS_INFORMATION	procInfo = { 0 };

	startupInfo.cb = sizeof(startupInfo);

	if(!CreateProcess(
		steamEXEPath.c_str(),
		NULL,	// no args
		NULL,	// default process security
		NULL,	// default thread security
		FALSE,	// don't inherit handles
		0,		// no options
		NULL,	// no new environment block
		steamRoot.c_str(),	// new cwd
		&startupInfo, &procInfo))
	{
		PrintLoaderError("Launching Steam failed (%08X).", GetLastError());
		return false;
	}

	// this doesn't do anything useful
	//	bool result = SteamCheckActive();

	// this is an ugly hack. wait for steam to start pumping messages
	WaitForInputIdle(procInfo.hProcess, INFINITE);

	// and then you know some more just because even then it isn't ready
	Sleep(1000 * 5);

	// clean up
	CloseHandle(procInfo.hProcess);
	CloseHandle(procInfo.hThread);

	return true;
}
