#include <windows.h>
#include <detours.h>
#include <math.h>
#include <stdio.h>

#include "Core.h"
#include "Engine.h"
#include "UnLinker.h"

extern "C" void *_ReturnAddress(void);
#pragma intrinsic(_ReturnAddress)

typedef void(WINAPI *t_void)(struct FFrame& Stack, void* Result);

// Core hooks

t_void orgAllObjects			= NULL;
t_void orgGetReferencers		= NULL;

void WINAPI AllObjects					(struct FFrame& Stack, void* Result);
void WINAPI GetReferencers				(struct FFrame& Stack, void* Result);

// Engine hooks
t_void orgActorCommand			= NULL;
t_void orgConsoleCommand		= NULL;
t_void orgPlayerCommand			= NULL;
t_void orgGetDirectoryContents	= NULL;
t_void orgLocalPerform			= NULL;
t_void orgNativePerform			= NULL;

void WINAPI ActorCommand				(struct FFrame& Stack, void* Result);
void WINAPI ConsoleCommand				(struct FFrame& Stack, void* Result);
void WINAPI PlayerCommand				(struct FFrame& Stack, void* Result);
void WINAPI GetDirectoryContents		(struct FFrame& Stack, void* Result);
void WINAPI LocalPerform				(struct FFrame& Stack, void* Result);
void WINAPI NativePerform				(struct FFrame& Stack, void* Result);	

// UWeb hooks
t_void orgIncludeBinaryFile		= NULL;

void WINAPI IncludeBinaryFile			(struct FFrame& Stack, void* Result);

void* HookFunction (HMODULE hDll, char* Procname, DWORD dwOffset)
{
	GLog->Logf(L"[INFO] Attempting to hook %ls", ANSI_TO_TCHAR(Procname));

	if (hDll)
	{
		void* pAddress = (void*)GetProcAddress(hDll, Procname);

		if (pAddress)
		{
			void* pResult = (void*)DetourFunction((BYTE*)pAddress, (BYTE*)dwOffset);

			if (pResult)
			{
				GLog->Logf(L" - Done : Function was detoured");

				return pResult;
			}
			else
			{
				GLog->Logf(L" - Failed : Function was not detoured");
			}
		}
		else
		{
			GLog->Logf(L" - Failed : Function was not found");
		}
	}
	else
	{
		GLog->Logf(L" - Failed : Module was not found");
	}

	return 0;
}

class UEngine* CachedEngine = NULL;

UEngine* FindEngine ()
{
	if (CachedEngine)
	{
		return CachedEngine;
	}
	else
	{
		for (TObjectIterator<UEngine> pEngine; pEngine; ++pEngine)
		{
			if (pEngine)
			{
				CachedEngine = *pEngine;

				return CachedEngine;
			}
		}
	}

	return NULL;
}
