//====================================================================================================
//
// Name: UNLOZE Forum Integration.
// Author: .George & zaCade (Original by Botox)
// Description: Handles forum access ingame.
//
//====================================================================================================
#include <sourcemod>
#include <SteamWorks>
#include <unloze>
#include <UNLOZE.secret> //#define UNLOZE_APIKEY here
#include <cstrike>

#pragma newdecls required

/* STRINGS */
char G_sGroup[MAXPLAYERS+1][64];
char G_sName[MAXPLAYERS+1][32];

/* BOOLS */
bool G_bPreAdminChecked[MAXPLAYERS+1];
bool G_bResponseFailed[MAXPLAYERS+1];
bool G_bResponsePassed[MAXPLAYERS+1];

//----------------------------------------------------------------------------------------------------
// Purpose:
//----------------------------------------------------------------------------------------------------
public Plugin myinfo =
{
	name		 = "UNLOZE Forum Integration",
	author	   = ".George & zaCade (Original by Botox)",
	description  = "Handles forum access ingame",
	version	  = "1.2.1"
};

//----------------------------------------------------------------------------------------------------
// Purpose: Late load
//----------------------------------------------------------------------------------------------------
public void OnPluginStart()
{

	/* Late load */
	for(int client = 1; client <= MaxClients; client++)
	{
		if(IsClientConnected(client))
			OnClientConnected(client);

		if(IsClientAuthorized(client) && !IsFakeClient(client))
		{
			char sSteamID32[32];
			if(GetClientAuthId(client, AuthId_Steam2, sSteamID32, sizeof(sSteamID32)))
				OnClientAuthorized(client, sSteamID32);
		}
	}
}


//----------------------------------------------------------------------------------------------------
// Purpose:
//----------------------------------------------------------------------------------------------------
public APLRes AskPluginLoad2(Handle myself, bool late, char[] error, int err_max)
{
	CreateNative("AsyncHasSteamIDReservedSlot", Native_AsyncHasSteamIDReservedSlot);
	CreateNative("GetClientForumName", Native_GetClientForumName);

	RegPluginLibrary("UNLOZE_ForumIntegration");

	return APLRes_Success;
}

//----------------------------------------------------------------------------------------------------
// Purpose:
//----------------------------------------------------------------------------------------------------
public void OnRebuildAdminCache(AdminCachePart part)
{
	if (part != AdminCache_Admins)
		return;

	CreateTimer(1.0, OnRebuildAdminCachePost, INVALID_HANDLE, TIMER_FLAG_NO_MAPCHANGE);
}

//----------------------------------------------------------------------------------------------------
// Purpose:
//----------------------------------------------------------------------------------------------------
public Action OnRebuildAdminCachePost(Handle hTimer)
{
	for (int client = 1; client <= MaxClients; client++)
	{
		if(G_bResponsePassed[client] && G_bPreAdminChecked[client])
			ApplyGroupFlags(client);
	}
    return Plugin_Handled;
}

//----------------------------------------------------------------------------------------------------
// Purpose:
//----------------------------------------------------------------------------------------------------
public void OnClientConnected(int client)
{
	G_bPreAdminChecked[client] = false;
	G_bResponseFailed[client] = false;
	G_bResponsePassed[client] = false;

	G_sGroup[client][0] = 0;
}

//----------------------------------------------------------------------------------------------------
// Purpose:
//----------------------------------------------------------------------------------------------------
public void OnClientDisconnect(int client)
{
	G_bPreAdminChecked[client] = false;
	G_bResponseFailed[client] = false;
	G_bResponsePassed[client] = false;

	G_sGroup[client][0] = 0;
	G_sName[client][0] = 0;
}

//----------------------------------------------------------------------------------------------------
// Purpose:
//----------------------------------------------------------------------------------------------------
public void OnClientAuthorized(int client, const char[] sSteamID32)
{
	if (IsFakeClient(client))
		return;
	char sSteamID64[32];
	SteamID32toSteamID64(sSteamID32, sSteamID64, sizeof(sSteamID64));

	char sRequest[256];
	FormatEx(sRequest, sizeof(sRequest), "https://unloze.com/api/private_api.php?api_key=%s&steam_id=%s", UNLOZE_APIKEY, sSteamID64);

	int iSerial = GetClientSerial(client);
	Handle hRequest = SteamWorks_CreateHTTPRequest(k_EHTTPMethodGET, sRequest);
	if (!hRequest ||
		!SteamWorks_SetHTTPCallbacks(hRequest, OnClientAuthorized_OnTransferComplete) ||
		!SteamWorks_SetHTTPRequestContextValue(hRequest, iSerial) ||
		!SteamWorks_SendHTTPRequest(hRequest))
	{
		delete hRequest;
	}
}

//----------------------------------------------------------------------------------------------------
// Purpose:
//----------------------------------------------------------------------------------------------------
public int OnClientAuthorized_OnTransferComplete(Handle hRequest, bool bFailure, bool bSuccessful, EHTTPStatusCode eStatusCode, int iSerial)
{
	int client = GetClientFromSerial(iSerial);

	if (!client) //Player disconnected.
	{
		delete hRequest;
		return 0;
	}
	
	if (bFailure || !bSuccessful || eStatusCode != k_EHTTPStatusCode200OK)
	{
		G_bResponseFailed[client] = true;

		if (G_bPreAdminChecked[client])
			NotifyPostAdminCheck(client);

		delete hRequest;
		return 0;
	}
	
	SteamWorks_GetHTTPResponseBodyCallback(hRequest, OnClientAuthorized_OnTransferResponse, iSerial);
    return 0;
}

//----------------------------------------------------------------------------------------------------
// Purpose:
//----------------------------------------------------------------------------------------------------
public int OnClientAuthorized_OnTransferResponse(char[] sData, int iSerial)
{
	char splitData[2][32];

	int client = GetClientFromSerial(iSerial);

	if (!client) //Player disconnected.
		return 0;

	TrimString(sData);
	StripQuotes(sData);

	LogMessage("reached sData with status 200: %s", sData);
	ExplodeString(sData, "\r\n", splitData, 2, sizeof(splitData[]));

	if(strlen(splitData[1]) > 0)
		strcopy(G_sName[client], sizeof(G_sName[]), splitData[1]);

	if(!StrEqual(splitData[0], "NOGROUP"))
	{
		strcopy(G_sGroup[client], sizeof(G_sGroup[]), splitData[0]);

		G_bResponsePassed[client] = true;

		if (G_bPreAdminChecked[client])
			NotifyPostAdminCheck(client);
	}
	else
		G_bResponseFailed[client] = true; //users with just a forum name did not pass the VIP check! so the response "failed" (but we store their forum name for later!)
    return 0;
}

//----------------------------------------------------------------------------------------------------
// Purpose:
//----------------------------------------------------------------------------------------------------
public Action OnClientPreAdminCheck(int client)
{
	G_bPreAdminChecked[client] = true;

	if (G_bResponsePassed[client] || G_bResponseFailed[client])
		return Plugin_Continue;

	RunAdminCacheChecks(client);
	return Plugin_Handled;
}

//----------------------------------------------------------------------------------------------------
// Purpose:
//----------------------------------------------------------------------------------------------------
public void OnClientPostAdminFilter(int client)
{
	ApplyGroupFlags(client);
}

//----------------------------------------------------------------------------------------------------
// Purpose:
//----------------------------------------------------------------------------------------------------
stock void ApplyGroupFlags(int client)
{
	if (!G_bResponsePassed[client])
		return;

	AdminId AdmID;
	GroupId GrpID;

	if ((AdmID = GetUserAdmin(client)) == INVALID_ADMIN_ID)
	{
		LogMessage("Creating new admin for %L", client);

		AdmID = CreateAdmin();
		SetUserAdmin(client, AdmID, true);
	}


    if ((GrpID = FindAdmGroup(G_sGroup[client])) != INVALID_GROUP_ID)
	{
		if (AdminInheritGroup(AdmID, GrpID))
		{
			LogMessage("%L added to group %s", client, G_sGroup[client]);
		}
	}
	else
	{
		LogMessage("%L group not found %s", client, G_sGroup[client]);
	}
}

public int Native_GetClientForumName(Handle plugin, int numParams)
{
	int len = GetNativeCell(2);
	int client = GetNativeCell(1);
	
	SetNativeString(2, G_sName[client], len+1);
    return 0;
}


//----------------------------------------------------------------------------------------------------
// Purpose:
//----------------------------------------------------------------------------------------------------
public int Native_AsyncHasSteamIDReservedSlot(Handle plugin, int numParams)
{
	char sSteamID32[32];
	GetNativeString(1, sSteamID32, sizeof(sSteamID32));

	Function callback; //https://github.com/alliedmodders/sourcepawn/issues/778
	callback = GetNativeFunction(2);

	any data;
	data = GetNativeCell(3);

	char sSteamID64[32];
	SteamID32toSteamID64(sSteamID32, sSteamID64, sizeof(sSteamID64));

	char sRequest[256];
	FormatEx(sRequest, sizeof(sRequest), "https://unloze.com/api/private_api.php?api_key=%s&steam_id=%s", UNLOZE_APIKEY, sSteamID64);

	DataPack hDataPack = new DataPack();
	hDataPack.WriteString(sSteamID32);
	hDataPack.WriteFunction(callback);
	hDataPack.WriteCell(plugin);
	hDataPack.WriteCell(data);

	Handle hRequest = SteamWorks_CreateHTTPRequest(k_EHTTPMethodGET, sRequest);
	if (!hRequest ||
		!SteamWorks_SetHTTPCallbacks(hRequest, Native_AsyncHasSteamIDReservedSlot_OnTransferComplete) ||
		!SteamWorks_SetHTTPRequestContextValue(hRequest, hDataPack) ||
		!SteamWorks_SendHTTPRequest(hRequest))
	{
		delete hRequest;
	}
    return 0;
}

//----------------------------------------------------------------------------------------------------
// Purpose:
//----------------------------------------------------------------------------------------------------
public int Native_AsyncHasSteamIDReservedSlot_OnTransferComplete(Handle hRequest, bool bFailure, bool bSuccessful, EHTTPStatusCode eStatusCode, DataPack hDataPack)
{
	if (bFailure || !bSuccessful || eStatusCode != k_EHTTPStatusCode200OK)
	{
		char sData[32] = "NOGROUP";
		Native_AsyncHasSteamIDReservedSlot_OnTransferResponse(sData, hDataPack);

		delete hRequest;
		return 0;
	}

	SteamWorks_GetHTTPResponseBodyCallback(hRequest, Native_AsyncHasSteamIDReservedSlot_OnTransferResponse, hDataPack);
    return 0;
}

//----------------------------------------------------------------------------------------------------
// Purpose:
//----------------------------------------------------------------------------------------------------
public int Native_AsyncHasSteamIDReservedSlot_OnTransferResponse(char[] sData, DataPack hDataPack)
{
	hDataPack.Reset();

	char sSteamID32[32];
	hDataPack.ReadString(sSteamID32, sizeof(sSteamID32));

	Function callback;
	callback = hDataPack.ReadFunction();

	Handle plugin;
	plugin = hDataPack.ReadCell();

	any data;
	data = hDataPack.ReadCell();

	static char splitData[32];

	TrimString(sData);
	StripQuotes(sData);
	
	SplitString(sData, "\r\n", splitData, sizeof(splitData));

	int result;
	if (StrEqual(splitData, "Game-Donator", false))
		result = 1;
	else
		result = 0;

	Call_StartFunction(plugin, callback);
	Call_PushString(sSteamID32);
	Call_PushCell(result);
	Call_PushCell(data);
	Call_Finish();

	delete hDataPack;
	return 0;
}

//----------------------------------------------------------------------------------------------------
// Purpose:
//----------------------------------------------------------------------------------------------------
stock bool SteamID32toSteamID64(const char[] sSteamID32, char[] sSteamID64, int iSize)
{
	if (strlen(sSteamID32) < 11 || strncmp(sSteamID32[0], "STEAM_", 6))
	{
		sSteamID64[0] = 0;
		return false;
	}

	int iUpper = 765611979;
	int isSteam64ID = StringToInt(sSteamID32[10]) * 2 + 60265728 + sSteamID32[8] - 48;

	int iDiv = isSteam64ID / 100000000;
	int iIdx = 9 - (iDiv ? (iDiv / 10 + 1) : 0);

	iUpper += iDiv;

	IntToString(isSteam64ID, sSteamID64[iIdx], iSize - iIdx);
	iIdx = sSteamID64[9];

	IntToString(iUpper, sSteamID64, iSize);
	sSteamID64[9] = iIdx;

	return true;
}

stock bool IsValidClient(int client)
{
    if (client > 0 && client <= MaxClients && IsClientConnected(client) && IsClientInGame(client))
        return true;
    return false;
}

