#pragma semicolon 1

#include <sourcemod>

#define TIMER_DELAY 5
#define ENTITY_FULL_UPDATE_TIME 60.0
#define MAXENTITIES 2048

static Handle:g_hTimer;
static String:g_sReloadMapName[PLATFORM_MAX_PATH];

enum EntityDebuggerAction
{
	Action_Update,
	Action_Remove,
}

public Plugin:myinfo =
{
	name = "BasicPawn Debugger Module Runner",
	author = "Timocop",
	description = "BasicPawn debugger module which communicates with the debugger.",
	version = "1.0",
	url = ""
};

public OnPluginStart()
{
	CreateTimer(1.0, SendPingTimer, INVALID_HANDLE, TIMER_REPEAT);
}

public OnGameFrame()
{
	for(; /* Execute debugger commands */ ;)
	{
		if(!FileExists("{IndentifierGUID}.cmd.bpdebug"))
			break;
		
		new Handle:hFile = OpenFile("{IndentifierGUID}.cmd.bpdebug", "r");
		if(hFile == INVALID_HANDLE)
			break;
		
		static String:sCmd[1024];
		while(ReadFileLine(hFile, sCmd, sizeof(sCmd))) {
			if(StrContains(sCmd, "@reloadmap") == 0) {
				GetCurrentMap(g_sReloadMapName, sizeof(g_sReloadMapName));
				
				PrintToChatAll("[BPD] Server reloads map in %d seconds...", TIMER_DELAY);
				
				g_hTimer = CreateTimer(float(TIMER_DELAY), ReloadMapTimer);
				continue;
			}
			
			if(StrContains(sCmd, "@refreshplugins") == 0) {
				PrintToChatAll("[BPD] Server refreshes plugins in %d seconds...", TIMER_DELAY);
				
				g_hTimer = CreateTimer(float(TIMER_DELAY), RefreshPluginsTimer);
				continue;
			}
			
			ServerCommand(sCmd);
		}
		
		CloseHandle(hFile);
		
		DeleteFile("{IndentifierGUID}.cmd.bpdebug");
		break;
	}
	
	for(; /* Tell the debugger all entities */ ;)
	{
		static iEntityRef[MAXENTITIES+1];
		static Float:fLastNow;
		static String:sClassname[PLATFORM_MAX_PATH];
		static String:sBuffer[2048];
		
		new Float:fNow = GetEngineTime();
		new bool:bForceUpdate = ((fLastNow + ENTITY_FULL_UPDATE_TIME) < fNow);
		new Handle:hEntStack = CreateStack(sizeof(sBuffer));
		
		if(bForceUpdate)
			fLastNow = fNow;
		
		for(new i = 0; i < MAXENTITIES; i++)
		{
			if(!bForceUpdate
					&& iEntityRef[i] != INVALID_ENT_REFERENCE
					&& IsValidEntity(iEntityRef[i]))
			{
				continue;
			}
			else if(IsValidEntity(i))
			{
				iEntityRef[i] = EntIndexToEntRef(i);
				
				GetEntityClassname(i, sClassname, sizeof(sClassname));
				
				FormatEx(sBuffer, sizeof(sBuffer), "%d:%d:%d:%s", i, iEntityRef[i], Action_Update, sClassname);
				PushStackString(hEntStack, sBuffer);
			}
			else if(iEntityRef[i] != INVALID_ENT_REFERENCE)
			{
				iEntityRef[i] = INVALID_ENT_REFERENCE;
				
				FormatEx(sBuffer, sizeof(sBuffer), "%d:%d:%d:%s", i, iEntityRef[i], Action_Remove, "");
				PushStackString(hEntStack, sBuffer);
			}
		}
		
		if(!IsStackEmpty(hEntStack)) {
			new Handle:hEntFile;
			while((hEntFile = OpenFile("{IndentifierGUID}.entities.bpdebug", "w")) == INVALID_HANDLE) {}
			
			while(!IsStackEmpty(hEntStack)) {
				PopStackString(hEntStack, sBuffer, sizeof(sBuffer));
				WriteFileLine(hEntFile, sBuffer);
			}
			
			CloseHandle(hEntFile);
		}
		
		CloseHandle(hEntStack);
		
		break;
	}
}

public Action:SendPingTimer(Handle:hTimer)
{
	for(; /* Ping to BasicPawn Debugger */ ;)
	{
		new Handle:hFile;
		while((hFile = OpenFile("{IndentifierGUID}.ping.bpdebug", "w")) == INVALID_HANDLE) {}
		
		WriteFileString(hFile, "1", false);
		
		CloseHandle(hFile);
		break;
	}
	
	return Plugin_Continue;
}

public Action:ReloadMapTimer(Handle:hTimer)
{
	if(g_hTimer != hTimer)
		return Plugin_Stop;
	
	ForceChangeLevel(g_sReloadMapName, "BasicPawn Debugger Command");
	return Plugin_Stop;
}

public Action:RefreshPluginsTimer(Handle:hTimer)
{
	if(g_hTimer != hTimer)
		return Plugin_Stop;
	
	ServerCommand("sm plugins refresh");
	PrintToChatAll("[BPD] All plugins refreshed!");
	return Plugin_Stop;
}
