// RogueBugHook.cpp
// © 2010 Andrew Brockert (andrew@mercuric.net)

#include "RogueBugHook.h"

int __fastcall CClientExoAppInternal__MainLoopHookProc(CClientExoAppInternal* pCliExoAppInt)
{
	if(bRequestingData)
	{
		TimestampedLog("*** MainLoop with request\n");

		// tell the main loop to take a screenshot (we can't do it ourselves
		// because the main loop does a lot of necessary setup)
		pCliExoAppInt->m_bTakeScreenshot = 1;

		int mainresult = CClientExoAppInternal__MainLoop(pCliExoAppInt);

		int newlen;

		// ask the engine some questions
		CNWCCreature *pPlayerCreature = CClientExoAppInternal__GetCreatureByGameObjectID(pCliExoAppInt, NULL, pCliExoAppInt->m_pcPlayerCreOID);
		NWN2_Camera *cam = CClientExoAppInternal__GetModuleNWN2Camera(pCliExoAppInt);

		if(pCliExoAppInt->m_pcModule != NULL)
		{
			// module name
			CExoString sModName = pCliExoAppInt->m_pcModule->m_sModuleName;
			newlen = MIN(sModName.len, 255);
			strncpy(bugdata.sModName, sModName.text, newlen);
			bugdata.sModName[newlen] = '\0';
		}
		else
		{
			// clear mod name string
			bugdata.sModName[0] = '\0';
		}

		// area data
		if(pPlayerCreature != NULL && pPlayerCreature->m_pArea != NULL)
		{
			// area resref
			CResRef cAreaResRef = pPlayerCreature->m_pArea->m_cResRef;
			strncpy(bugdata.sAreaResRef, (char*)(&cAreaResRef.m_resRef), 0x20);
			bugdata.sAreaResRef[0x20] = '\0';
			// area name
			CExoString sAreaName = pPlayerCreature->m_pArea->m_sAreaName;
			newlen = MIN(sAreaName.len, 255);
			strncpy(bugdata.sAreaName, sAreaName.text, newlen);
			bugdata.sAreaName[newlen] = '\0';
		}
		else
		{
			// clear resref and name strings
			bugdata.sAreaResRef[0] = '\0';
			bugdata.sAreaName[0] = '\0';
		}

		// player location
		bugdata.playerX = pfPlayerLoc[0];
		bugdata.playerY = pfPlayerLoc[1];
		bugdata.playerZ = pfPlayerLoc[2];

		// camera state
		if(cam != NULL)
		{
			bugdata.camPosX = cam->m_Position.x;
			bugdata.camPosY = cam->m_Position.y;
			bugdata.camPosZ = cam->m_Position.z;
			bugdata.camLookX = cam->m_LookAt.x;
			bugdata.camLookY = cam->m_LookAt.y;
			bugdata.camLookZ = cam->m_LookAt.z;
		}
		else
		{
			// reset all
			bugdata.camPosX = 0.0f;
			bugdata.camPosY = 0.0f;
			bugdata.camPosZ = 0.0f;
			bugdata.camLookX = 0.0f;
			bugdata.camLookY = 0.0f;
			bugdata.camLookZ = 0.0f;
		}

		// conversation state
		strncpy(bugdata.sConvResref, (char*)(&dlgResRef.m_resRef), 0x20);
		bugdata.sConvResref[0x20] = '\0';

		// write data to shared memory
		bool writeresult = WriteBugDataToSharedObject();

		if(writeresult)
		{
			// signal readiness to app
			bool sendresult = SendMessage(hAppCommWnd, iDataReadyID, 0, 0);
			TimestampedLog("*** SendMessage returned %d\n", sendresult);
			bRequestingData = false;
		}
		else
		{
			// TODO: any better way to handle this?
			TimestampedLog("* Writing data failed; abort this request.\n");
			bRequestingData = false;
		}
		return mainresult;
	}
	else
	{
		return CClientExoAppInternal__MainLoop(pCliExoAppInt);
	}
}

void __fastcall CNWSDialog__AddPlayerHookProc(CNWSDialog *pDialog, void *, void *pDialogPlayer)
{
	strncpy((char*)dlgResRef.m_resRef, (char*)pDialog->m_cResRef.m_resRef, 0x20);
	CNWSDialog__AddPlayer(pDialog, NULL, pDialogPlayer);
}

void __fastcall CNWSDialog__CleanupHookProc(CNWSDialog* pDialog)
{
	dlgResRef.m_resRef[0] = '\0';
	CNWSDialog__Cleanup(pDialog);
}

void __fastcall NWN2_Electron__ScreenShotHookProc(NWN2_Electron *pThis, void *, char *path)
{
	if(bRequestingData)
	{
		TimestampedLog("*** ScreenShot with bRequestingData\n");
		// save screenshot settings
		char oldtype = *g_ScreenshotType;
		char **g_GraphicsOptions = (char**)0x009c58e0;
		char *oldextension = *g_GraphicsOptions;

		// overwrite settings to save as bmp
		*g_ScreenshotType = 0; // 0 for bmp; 2 for tga
		*g_GraphicsOptions = "bmp";

		// take screenshot
		NWN2_Electron__ScreenShot(pThis, NULL, docspath);

		// restore settings
		*g_ScreenshotType = oldtype;
		*g_GraphicsOptions = oldextension;
	}
	else // not a RogueBug call
	{
		NWN2_Electron__ScreenShot(pThis, NULL, path);
	}
}

TRACED_HOOK_HANDLE MainLoopHookTrace, AddPlayerHookTrace, CleanupHookTrace, ScreenShotHookTrace;

void InstallHooks()
{
	DWORD success = NULL;

	DWORD threadIDs[1];
	threadIDs[0] = 0;

	LhSetGlobalExclusiveACL(threadIDs, 1);

	success = TRUE;

	MainLoopHookTrace = new HOOK_TRACE_INFO();
	success &= LhInstallHook(CClientExoAppInternal__MainLoop,
		CClientExoAppInternal__MainLoopHookProc,
		NULL, MainLoopHookTrace) == 0;

	AddPlayerHookTrace = new HOOK_TRACE_INFO();
	success &= LhInstallHook(CNWSDialog__AddPlayer,
		CNWSDialog__AddPlayerHookProc,
		NULL, AddPlayerHookTrace) == 0;

	CleanupHookTrace = new HOOK_TRACE_INFO();
	success &= LhInstallHook(CNWSDialog__Cleanup,
		CNWSDialog__CleanupHookProc,
		NULL, CleanupHookTrace) == 0;

	ScreenShotHookTrace = new HOOK_TRACE_INFO();
	success &= LhInstallHook(NWN2_Electron__ScreenShot,
		NWN2_Electron__ScreenShotHookProc,
		NULL, ScreenShotHookTrace) == 0;

	if ( success )
	{
		TimestampedLog("* Hooks installed.\n");
		LhSetExclusiveACL(threadIDs, 1, MainLoopHookTrace);
		LhSetExclusiveACL(threadIDs, 1, AddPlayerHookTrace);
		LhSetExclusiveACL(threadIDs, 1, CleanupHookTrace);
		LhSetExclusiveACL(threadIDs, 1, ScreenShotHookTrace);
	}
	else
	{
		TimestampedLog("! Hooks failed to install!\n");
	}
}

bool WriteBugDataToSharedObject()
{
	char *objname = "Local\\RogueBugSharedObject";
	HANDLE hMap;

	hMap = OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, objname);
	if(hMap == NULL)
	{
		TimestampedLog("! failed to open file mapping\n");
		return false;
	}

	SBugData *view = (SBugData*)MapViewOfFile(hMap, FILE_MAP_ALL_ACCESS, 0, 0, sizeof(SBugData));
	if(view == NULL)
	{
		TimestampedLog("! failed to map view\n");
		return false;
	}

	*view = bugdata;

	UnmapViewOfFile(view);
	CloseHandle(hMap);

	return true;
}
