#pragma newdecls required

#include <sourcemod>
#include <AdminGroups>

/* ARRAYS */
ArrayList G_hArray_AdminGroups;
ArrayList G_hArray_ClientGroups[MAXPLAYERS+1];

/* BOOLEANS */
bool G_bClientPostAdminFilter[MAXPLAYERS+1];

//----------------------------------------------------------------------------------------------------
// Purpose:
//----------------------------------------------------------------------------------------------------
public Plugin myinfo =
{
	name         = "AdminGroups",
	author       = "zaCade",
	description  = "Manage custom sourcemod admin groups with plugins",
	version      = "1.0"
};

//----------------------------------------------------------------------------------------------------
// Purpose:
//----------------------------------------------------------------------------------------------------
public APLRes AskPluginLoad2(Handle myself, bool late, char[] error, int errorSize)
{
	CreateNative("AdminGroups_CreateAdminGroup", Native_CreateAdminGroup);
	CreateNative("AdminGroups_DeleteAdminGroup", Native_DeleteAdminGroup);

	CreateNative("AdminGroups_GrantAdminGroup",  Native_GrantAdminGroup);
	CreateNative("AdminGroups_RevokeAdminGroup", Native_RevokeAdminGroup);

	RegPluginLibrary("AdminGroups");
	return APLRes_Success;
}

//----------------------------------------------------------------------------------------------------
// Purpose:
//----------------------------------------------------------------------------------------------------
public void OnPluginStart()
{
	G_hArray_AdminGroups = new ArrayList(128);

	for (int client = 1; client <= MaxClients; client++)
	{
		G_hArray_ClientGroups[client] = new ArrayList(128);
	}
}

//----------------------------------------------------------------------------------------------------
// Purpose:
//----------------------------------------------------------------------------------------------------
public void OnRebuildAdminCache(AdminCachePart part)
{
	switch(part)
	{
		case(AdminCache_Groups):
		{
			CreateAdminGroups();
		}
		case(AdminCache_Admins):
		{
			CreateTimer(1.0, OnRebuildAdminCachePost, INVALID_HANDLE, TIMER_FLAG_NO_MAPCHANGE);
		}
	}
}

//----------------------------------------------------------------------------------------------------
// Purpose:
//----------------------------------------------------------------------------------------------------
public Action OnRebuildAdminCachePost(Handle hTimer)
{
	for (int client = 1; client <= MaxClients; client++)
	{
		GrantAdminGroups(client);
	}
}

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

	G_hArray_ClientGroups[client].Clear();
}

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

	G_hArray_ClientGroups[client].Clear();
}

//----------------------------------------------------------------------------------------------------
// Purpose:
//----------------------------------------------------------------------------------------------------
public void OnClientPostAdminFilter(int client)
{
	G_bClientPostAdminFilter[client] = true;

	GrantAdminGroups(client);
}

//----------------------------------------------------------------------------------------------------
// Purpose: Create all groups (Wrapper)
//----------------------------------------------------------------------------------------------------
stock void CreateAdminGroups()
{
	if (G_hArray_AdminGroups.Length)
	{
		for (int adminGroupIndex; adminGroupIndex < G_hArray_AdminGroups.Length; adminGroupIndex++)
		{
			char group[128];
			G_hArray_AdminGroups.GetString(adminGroupIndex, group, sizeof(group));

			CreateAdminGroup(group);
		}
	}
}

//----------------------------------------------------------------------------------------------------
// Purpose: Create a specific group
//----------------------------------------------------------------------------------------------------
stock void CreateAdminGroup(const char[] group)
{
	GroupId GrpID;

	if ((GrpID = FindAdmGroup(group)) == INVALID_GROUP_ID)
	{
		LogMessage("Creating new admin group %s", group);

		GrpID = CreateAdmGroup(group);
		GrpID.ImmunityLevel = 0;
	}
}

//----------------------------------------------------------------------------------------------------
// Purpose: Grant all groups (Wrapper)
//----------------------------------------------------------------------------------------------------
stock void GrantAdminGroups(int client)
{
	if (!G_bClientPostAdminFilter[client])
		return;

	if (G_hArray_ClientGroups[client].Length)
	{
		for (int clientGroupIndex; clientGroupIndex < G_hArray_ClientGroups[client].Length; clientGroupIndex++)
		{
			char group[128];
			G_hArray_ClientGroups[client].GetString(clientGroupIndex, group, sizeof(group));

			GrantAdminGroup(client, group);
		}
	}
}

//----------------------------------------------------------------------------------------------------
// Purpose: Grant a specific group
//----------------------------------------------------------------------------------------------------
stock void GrantAdminGroup(int client, const char[] group)
{
	if (!G_bClientPostAdminFilter[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(group)) != INVALID_GROUP_ID)
	{
		if (AdminInheritGroup(AdmID, GrpID))
		{
			LogMessage("%L added to group %s", client, group);
		}
	}
	else
	{
		LogMessage("%L group not found %s", client, group);
	}
}

//----------------------------------------------------------------------------------------------------
// Purpose:
//----------------------------------------------------------------------------------------------------
public int Native_CreateAdminGroup(Handle hPlugin, int numParams)
{
	char group[128];
	GetNativeString(1, group, sizeof(group));

	int adminGroupIndex;
	if ((adminGroupIndex = G_hArray_AdminGroups.FindString(group)) != -1)
	{
		ThrowNativeError(SP_ERROR_NATIVE, "Group already exists. (%s) [%d]", group, adminGroupIndex);
		return;
	}

	G_hArray_AdminGroups.PushString(group);

	SortADTArray(G_hArray_AdminGroups, Sort_Ascending, Sort_String);

	CreateAdminGroup(group);
}

//----------------------------------------------------------------------------------------------------
// Purpose:
//----------------------------------------------------------------------------------------------------
public int Native_DeleteAdminGroup(Handle hPlugin, int numParams)
{
	char group[128];
	GetNativeString(1, group, sizeof(group));

	int adminGroupIndex;
	if ((adminGroupIndex = G_hArray_AdminGroups.FindString(group)) == -1)
	{
		ThrowNativeError(SP_ERROR_NATIVE, "Group doesnt exist. (%s) [%d]", group, adminGroupIndex);
		return;
	}

	G_hArray_AdminGroups.Erase(adminGroupIndex);

	SortADTArray(G_hArray_AdminGroups, Sort_Ascending, Sort_String);
}

//----------------------------------------------------------------------------------------------------
// Purpose:
//----------------------------------------------------------------------------------------------------
public int Native_GrantAdminGroup(Handle hPlugin, int numParams)
{
	int client = GetNativeCell(1);

	if (client < 1 || client > MaxClients)
	{
		ThrowNativeError(SP_ERROR_NATIVE, "Invalid client index. (%d)", client);
		return;
	}

	if (!IsClientConnected(client))
	{
		ThrowNativeError(SP_ERROR_NATIVE, "Client (%d) is not connected.", client);
		return;
	}

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

	int adminGroupIndex
	if ((adminGroupIndex = G_hArray_AdminGroups.FindString(group)) == -1)
	{
		ThrowNativeError(SP_ERROR_NATIVE, "Group doesnt exist. (%s) [%d]", group, adminGroupIndex);
		return;
	}

	int clientGroupIndex;
	if ((clientGroupIndex = G_hArray_ClientGroups[client].FindString(group)) != -1)
	{
		ThrowNativeError(SP_ERROR_NATIVE, "Client already has group. (%s) [%d]", group, clientGroupIndex);
		return;
	}

	G_hArray_ClientGroups[client].PushString(group);

	SortADTArray(G_hArray_ClientGroups[client], Sort_Ascending, Sort_String);

	GrantAdminGroup(client, group);
}

//----------------------------------------------------------------------------------------------------
// Purpose:
//----------------------------------------------------------------------------------------------------
public int Native_RevokeAdminGroup(Handle hPlugin, int numParams)
{
	int client = GetNativeCell(1);

	if (client < 1 || client > MaxClients)
	{
		ThrowNativeError(SP_ERROR_NATIVE, "Invalid client index. (%d)", client);
		return;
	}

	if (!IsClientConnected(client))
	{
		ThrowNativeError(SP_ERROR_NATIVE, "Client (%d) is not connected.", client);
		return;
	}

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

	int adminGroupIndex
	if ((adminGroupIndex = G_hArray_AdminGroups.FindString(group)) == -1)
	{
		ThrowNativeError(SP_ERROR_NATIVE, "Group doesnt exist. (%s) [%d]", group, adminGroupIndex);
		return;
	}

	int clientGroupIndex;
	if ((clientGroupIndex = G_hArray_ClientGroups[client].FindString(group)) == -1)
	{
		ThrowNativeError(SP_ERROR_NATIVE, "Client doesnt have group. (%s) [%d]", group, clientGroupIndex);
		return;
	}

	G_hArray_ClientGroups[client].Erase(clientGroupIndex);

	SortADTArray(G_hArray_ClientGroups[client], Sort_Ascending, Sort_String);
}