#pragma semicolon 1
#define PLUGIN_VERSION "1.0"

#include <sourcemod>
#include <DynamicTargeting>

#pragma newdecls required

public Plugin myinfo =
{
	name = "Dynamic Targeting",
	author = "BotoX",
	description = "",
	version = PLUGIN_VERSION,
	url = ""
}

char g_PlayerNames[MAXPLAYERS + 1][MAX_NAME_LENGTH];
Handle g_PlayerData[MAXPLAYERS + 1];

public APLRes AskPluginLoad2(Handle myself, bool late, char[] error, int err_max)
{
	CreateNative("AmbiguousMenu", Native_AmbiguousMenu);
	RegPluginLibrary("DynamicTargeting");

	return APLRes_Success;
}

public void OnClientDisconnect(int client)
{
	if(g_PlayerData[client] != INVALID_HANDLE)
	{
		CloseHandle(g_PlayerData[client]);
		g_PlayerData[client] = INVALID_HANDLE;
	}
}

int CreateAmbiguousMenu(int client, const char[] sCommand, const char[] sArgString, const char[] sPattern, int FilterFlags)
{
	Menu menu = new Menu(MenuHandler_AmbiguousMenu, MenuAction_Select|MenuAction_Cancel|MenuAction_End|MenuAction_DrawItem|MenuAction_DisplayItem);
	menu.ExitButton = true;

	char sTitle[32 + MAX_TARGET_LENGTH];
	FormatEx(sTitle, sizeof(sTitle), "Target \"%s\" is ambiguous.", sPattern);
	menu.SetTitle(sTitle);

	int Players = 0;
	int[] aClients = new int[MaxClients + 1];

	for(int i = 1; i <= MaxClients; i++)
	{
		if(!IsClientConnected(i) || i == client)
			continue;

		if(FilterFlags & COMMAND_FILTER_NO_BOTS && IsFakeClient(i))
			continue;

		if(!(FilterFlags & COMMAND_FILTER_CONNECTED) && !IsClientInGame(i))
			continue;

		if(FilterFlags & COMMAND_FILTER_ALIVE && !IsPlayerAlive(i))
			continue;

		if(FilterFlags & COMMAND_FILTER_DEAD && IsPlayerAlive(i))
			continue;

		// insert player names into g_PlayerNames array
		GetClientName(i, g_PlayerNames[i], sizeof(g_PlayerNames[]));

		if(StrContains(g_PlayerNames[i], sPattern, false) != -1)
			aClients[Players++] = i;
	}

	// sort aClients array by player name
	SortCustom1D(aClients, Players, SortByPlayerName);

	// insert players sorted
	char sUserId[12];
	char sDisp[MAX_NAME_LENGTH + 16];
	for(int i = 0; i < Players; i++)
	{
		IntToString(GetClientUserId(aClients[i]), sUserId, sizeof(sUserId));

		FormatEx(sDisp, sizeof(sDisp), "%s (%s)", g_PlayerNames[aClients[i]], sUserId);
		menu.AddItem(sUserId, sDisp);
	}

	DataPack pack = new DataPack();
	pack.WriteString(sCommand);
	pack.WriteString(sArgString);
	pack.WriteString(sPattern);
	pack.WriteCell(FilterFlags);

	if(g_PlayerData[client] != INVALID_HANDLE)
	{
		CloseHandle(g_PlayerData[client]);
		g_PlayerData[client] = INVALID_HANDLE;
	}
	CancelClientMenu(client);

	g_PlayerData[client] = pack;
	menu.Display(client, MENU_TIME_FOREVER);

	return 0;
}

public int MenuHandler_AmbiguousMenu(Menu menu, MenuAction action, int param1, int param2)
{
	switch(action)
	{
		case MenuAction_End:
		{
			CloseHandle(menu);
		}
		case MenuAction_Cancel:
		{
			if(g_PlayerData[param1] != INVALID_HANDLE)
			{
				CloseHandle(g_PlayerData[param1]);
				g_PlayerData[param1] = INVALID_HANDLE;
			}
		}
		case MenuAction_Select:
		{
			int Style;
			char sItem[32];
			char sDisp[MAX_NAME_LENGTH + 16];
			menu.GetItem(param2, sItem, sizeof(sItem), Style, sDisp, sizeof(sDisp));

			int UserId = StringToInt(sItem);
			int client = GetClientOfUserId(UserId);
			if(!client)
			{
				PrintToChat(param1, "\x04[DynamicTargeting]\x01 Player no longer available.");
				menu.DisplayAt(param1, GetMenuSelectionPosition(), MENU_TIME_FOREVER);
				return 0;
			}

			DataPack pack = view_as<DataPack>(g_PlayerData[param1]);
			pack.Reset();

			char sCommand[128];
			pack.ReadString(sCommand, sizeof(sCommand));

			char sArgString[256];
			pack.ReadString(sArgString, sizeof(sArgString));

			char sPattern[MAX_TARGET_LENGTH];
			pack.ReadString(sPattern, sizeof(sPattern));

			int Result = ReCallAmbiguous(param1, client, sCommand, sArgString, sPattern);

			return Result;
		}
		case MenuAction_DrawItem:
		{
			int Style;
			char sItem[32];
			menu.GetItem(param2, sItem, sizeof(sItem), Style);

			int UserId = StringToInt(sItem);
			int client = GetClientOfUserId(UserId);
			if(!client) // Player disconnected
				return ITEMDRAW_DISABLED;

			return Style;
		}
		case MenuAction_DisplayItem:
		{
			int Style;
			char sItem[32];
			char sDisp[MAX_NAME_LENGTH + 16];
			menu.GetItem(param2, sItem, sizeof(sItem), Style, sDisp, sizeof(sDisp));

			if(!sItem[0])
				return 0;

			char sBuffer[MAX_NAME_LENGTH + 16];
			int UserId = StringToInt(sItem);
			int client = GetClientOfUserId(UserId);
			if(!client) // Player disconnected
				return 0;

			GetClientName(client, g_PlayerNames[client], sizeof(g_PlayerNames[]));
			FormatEx(sBuffer, sizeof(sBuffer), "%s (%d)", g_PlayerNames[client], UserId);

			if(!StrEqual(sDisp, sBuffer))
				return RedrawMenuItem(sBuffer);

			return 0;
		}
	}

	return 0;
}

int ReCallAmbiguous(int client, int newClient, const char[] sCommand, const char[] sArgString, const char[] sPattern)
{
	char sTarget[16];
	FormatEx(sTarget, sizeof(sTarget), "#%d", GetClientUserId(newClient));

	char sNewArgString[256];
	strcopy(sNewArgString, sizeof(sNewArgString), sArgString);

	char sPart[256];
	int CurrentIndex = 0;
	int NextIndex = 0;

	while(NextIndex != -1 && CurrentIndex < sizeof(sNewArgString))
	{
		NextIndex = BreakString(sNewArgString[CurrentIndex], sPart, sizeof(sPart));

		if(StrEqual(sPart, sPattern))
		{
			ReplaceStringEx(sNewArgString[CurrentIndex], sizeof(sNewArgString) - CurrentIndex, sPart, sTarget);
			break;
		}

		CurrentIndex += NextIndex;
	}

	FakeClientCommandEx(client, "%s %s", sCommand, sNewArgString);

	return 0;
}

public int Native_AmbiguousMenu(Handle plugin, int numParams)
{
	int client = GetNativeCell(1);

	if(client > MaxClients || client <= 0)
	{
		ThrowNativeError(SP_ERROR_NATIVE, "Client is not valid.");
		return -1;
	}

	if(!IsClientInGame(client))
	{
		ThrowNativeError(SP_ERROR_NATIVE, "Client is not in-game.");
		return -1;
	}

	if(IsFakeClient(client))
	{
		ThrowNativeError(SP_ERROR_NATIVE, "Client is fake-client.");
		return -1;
	}

	char sCommand[128];
	GetNativeString(2, sCommand, sizeof(sCommand));

	char sArgString[256];
	GetNativeString(3, sArgString, sizeof(sArgString));

	char sPattern[MAX_TARGET_LENGTH];
	GetNativeString(4, sPattern, sizeof(sPattern));

	int FilterFlags = GetNativeCell(5);

	return CreateAmbiguousMenu(client, sCommand, sArgString, sPattern, FilterFlags);
}

public int SortByPlayerName(int elem1, int elem2, const int[] array, Handle hndl)
{
	return strcmp(g_PlayerNames[elem1], g_PlayerNames[elem2], false);
}