#include <sourcemod>
#include <BossHP>
#include <zombiereloaded>

#include "loghelper.inc"

#include <basic>
#include "CConfig.inc"
#include "CBoss.inc"

ArrayList g_hStats[MAXPLAYERS+1];

bool g_bZRLoaded;

//----------------------------------------------------------------------------------------------------
// Purpose:
//----------------------------------------------------------------------------------------------------
public Plugin myinfo =
{
	name         = "BossHP - Ranking",
	author       = "Neon & zaCade",
	description  = "",
	version      = "1.0.0",
};

//----------------------------------------------------------------------------------------------------
// Purpose:
//----------------------------------------------------------------------------------------------------
public void OnPluginStart()
{
	HookEvent("player_death", EventHook_PlayerDeath, EventHookMode_Pre);

	for (int client = 1; client <= MaxClients; client++)
	{
		if (!IsClientInGame(client))
			continue;

		OnClientPutInServer(client);
	}
}

//----------------------------------------------------------------------------------------------------
// Purpose:
//----------------------------------------------------------------------------------------------------
public void OnAllPluginsLoaded()
{
	g_bZRLoaded = LibraryExists("zombiereloaded");
}

//----------------------------------------------------------------------------------------------------
// Purpose:
//----------------------------------------------------------------------------------------------------
public void OnLibraryAdded(const char[] sName)
{
	if (strcmp(sName, "zombiereloaded", false) == 0)
		g_bZRLoaded = true;
}

//----------------------------------------------------------------------------------------------------
// Purpose:
//----------------------------------------------------------------------------------------------------
public void OnLibraryRemoved(const char[] sName)
{
	if (strcmp(sName, "zombiereloaded", false) == 0)
		g_bZRLoaded = false;
}

//----------------------------------------------------------------------------------------------------
// Purpose:
//----------------------------------------------------------------------------------------------------
public void OnClientPutInServer(int client)
{
	g_hStats[client] = new ArrayList(64);
}

//----------------------------------------------------------------------------------------------------
// Purpose:
//----------------------------------------------------------------------------------------------------
public void OnClientDisconnect(int client)
{
	delete g_hStats[client];
}

//----------------------------------------------------------------------------------------------------
// Purpose:
//----------------------------------------------------------------------------------------------------
public void OnBossDamaged(any Boss, any Config, int client, float damage)
{
	if (!IsValidClient(client))
		return;

	SetEntProp(client, Prop_Send, "m_iAccount", GetEntProp(client, Prop_Send, "m_iAccount") + 1);

	bool bBreakable;
	CConfig _Config = view_as<CConfig>(Config);

	if (_Config.IsBreakable)
		bBreakable = true;
	else
		bBreakable = false;

	delete _Config;

	for (int index = 0; index < g_hStats[client].Length; index++)
	{
		any BossDamage[2];
		g_hStats[client].GetArray(index, BossDamage, sizeof(BossDamage));

		if (BossDamage[0] == Boss)
		{
			if (bBreakable)
				BossDamage[1] += RoundToNearest(damage);
			else
				BossDamage[1] += 1;

			g_hStats[client].SetArray(index, BossDamage, sizeof(BossDamage));
			return;
		}
	}

	any BossDamage[2];
	BossDamage[0] = Boss;

	if (bBreakable)
		BossDamage[1] = RoundToNearest(damage);
	else
		BossDamage[1] = 1;

	g_hStats[client].PushArray(BossDamage, sizeof(BossDamage));
}

//----------------------------------------------------------------------------------------------------
// Purpose:
//----------------------------------------------------------------------------------------------------
public void OnBossKilled(any Boss, any Config, int reason)
{
	if (reason == 0)
	{
		for (int client = 1; client <= MaxClients; client++)
		{
			if (!IsClientInGame(client))
				continue;

			for (int index = 0; index < g_hStats[client].Length; index++)
			{
				any BossDamage[2];
				g_hStats[client].GetArray(index, BossDamage, sizeof(BossDamage));

				if (BossDamage[0] == Boss)
				{
					g_hStats[client].Erase(index);
					break;
				}
			}
		}
	}
	else
	{
		int iSortedList[MAXPLAYERS+1][2];
		int iSortedCount;

		for (int client = 1; client <= MaxClients; client++)
		{
			if (!IsClientInGame(client))
				continue;

			for (int index = 0; index < g_hStats[client].Length; index++)
			{
				any BossDamage[2];
				g_hStats[client].GetArray(index, BossDamage, sizeof(BossDamage));

				if (BossDamage[0] == Boss)
				{
					iSortedList[iSortedCount][0] = client;
					iSortedList[iSortedCount][1] = BossDamage[1];
					iSortedCount++;

					g_hStats[client].Erase(index);
					break;
				}
			}
		}

		SortCustom2D(iSortedList, iSortedCount, SortBossHitsList);

		if (iSortedCount)
		{
			CConfig _Config = view_as<CConfig>(Config);

			char sBossName[64];
			_Config.GetName(sBossName, sizeof(sBossName));

			char sBuffer[512];
			char sType[16];
			if (_Config.IsBreakable)
			{
				sType = "damage"
				Format(sBuffer, sizeof(sBuffer), "BOSS DAMAGE [%s]:", sBossName);
			}
			else
			{
				sType = "hits"
				Format(sBuffer, sizeof(sBuffer), "BOSS HITS [%s]:", sBossName);
			}
			delete _Config;

			Format(sBuffer, sizeof(sBuffer), "%s\n*************************", sBuffer);

			if (iSortedList[0][0])
			{
				Format(sBuffer, sizeof(sBuffer), "%s\n1. %N - %d %s", sBuffer, iSortedList[0][0], iSortedList[0][1], sType);

				LogPlayerEvent(iSortedList[0][0], "triggered", "ze_boss_damage_first");
			}
			if (iSortedList[1][0])
			{
				Format(sBuffer, sizeof(sBuffer), "%s\n2. %N - %d %s", sBuffer, iSortedList[1][0], iSortedList[1][1], sType);

				LogPlayerEvent(iSortedList[1][0], "triggered", "ze_boss_damage_second");
			}
			if (iSortedList[2][0])
			{
				Format(sBuffer, sizeof(sBuffer), "%s\n3. %N - %d %s", sBuffer, iSortedList[2][0], iSortedList[2][1], sType);

				LogPlayerEvent(iSortedList[2][0], "triggered", "ze_boss_damage_third");
			}

			Format(sBuffer, sizeof(sBuffer), "%s\n*************************", sBuffer);

			Handle hMessage = StartMessageAll("HudMsg");
			if (hMessage)
			{
				if (GetUserMessageType() == UM_Protobuf)
				{
					PbSetInt(hMessage, "channel", 50);
					PbSetInt(hMessage, "effect", 0);
					PbSetColor(hMessage, "clr1", {255, 255, 255, 255});
					PbSetColor(hMessage, "clr2", {255, 255, 255, 255});
					PbSetVector2D(hMessage, "pos", Float:{0.02, 0.45});
					PbSetFloat(hMessage, "fade_in_time", 0.1);
					PbSetFloat(hMessage, "fade_out_time", 0.1);
					PbSetFloat(hMessage, "hold_time", 7.0);
					PbSetFloat(hMessage, "fx_time", 0.0);
					PbSetString(hMessage, "text", sBuffer);
					EndMessage();
				}
				else
				{
					BfWriteByte(hMessage, 50);
					BfWriteFloat(hMessage, 0.02);
					BfWriteFloat(hMessage, 0.25);
					BfWriteByte(hMessage, 255);
					BfWriteByte(hMessage, 255);
					BfWriteByte(hMessage, 0);
					BfWriteByte(hMessage, 255);
					BfWriteByte(hMessage, 255);
					BfWriteByte(hMessage, 255);
					BfWriteByte(hMessage, 255);
					BfWriteByte(hMessage, 255);
					BfWriteByte(hMessage, 0);
					BfWriteFloat(hMessage, 0.1);
					BfWriteFloat(hMessage, 0.1);
					BfWriteFloat(hMessage, 7.0);
					BfWriteFloat(hMessage, 0.0);
					BfWriteString(hMessage, sBuffer);
					EndMessage();
				}
			}

			if(GetEngineVersion() == Engine_CSGO)
			{
				int iSplits
				char sSplits[16][512];

				if((iSplits = ExplodeString(sBuffer, "\n", sSplits, sizeof(sSplits), sizeof(sSplits[]))) != 0)
				{
					for (int iSplit; iSplit < iSplits; iSplit++)
					{
						PrintToChatAll(sSplits[iSplit]);
					}
				}
			}
			else
				PrintToChatAll(sBuffer);
		}
	}
}

//----------------------------------------------------------------------------------------------------
// Purpose:
//----------------------------------------------------------------------------------------------------
public Action EventHook_PlayerDeath(Event hEvent, const char[] sEventName, bool bDontBroadcast)
{
	if (!g_bZRLoaded)
		return Plugin_Continue;

	int iClient = GetClientOfUserId(hEvent.GetInt("userid"));

	if (!IsValidClient(iClient))
		return Plugin_Continue;

	if (IsPlayerAlive(iClient) && !ZR_IsClientHuman(iClient))
		return Plugin_Continue;

	int iPacked = (iClient<<16) | (GetEntProp(iClient, Prop_Send, "m_iAccount")&0xFFFF);

	RequestFrame(RequestFrame_Callback, iPacked);

	return Plugin_Continue;
}

//----------------------------------------------------------------------------------------------------
// Purpose:
//----------------------------------------------------------------------------------------------------
void RequestFrame_Callback(int iPacked)
{
	int iOldCash = iPacked&0xFFFF;
	int iClient = iPacked>>16;

	if (!IsValidEntity(iClient) || !IsValidClient(iClient))
		return;

	SetEntProp(iClient, Prop_Send, "m_iAccount", iOldCash);
}

//----------------------------------------------------------------------------------------------------
// Purpose:
//----------------------------------------------------------------------------------------------------
public int SortBossHitsList(int[] elem1, int[] elem2, const int[][] array, Handle hndl)
{
	if (elem1[1] > elem2[1]) return -1;
	if (elem1[1] < elem2[1]) return 1;

	return 0;
}

//----------------------------------------------------------------------------------------------------
// Purpose:
//----------------------------------------------------------------------------------------------------
stock int IsValidClient(int client, bool nobots = true)
{
	if (client <= 0 || client > MaxClients || !IsClientConnected(client) || (nobots && IsFakeClient(client)))
		return false;

	return IsClientInGame(client);
}
