#pragma semicolon 1

#include <sourcemod>
#include <sdktools>
#include <sdkhooks>
#include <cstrike>
#include <multicolors>
#include <zombiereloaded>

char g_sBanListFilePath[256];

bool g_bKnifeBanned[MAXPLAYERS + 1];
bool g_bKnifeBan;

ArrayList g_TempBanned;

KeyValues Banlist;

int g_hActiveWeapon;

public Plugin myinfo =
{
	name = "KnifeBan",
	author = "Dogan",
	description = "Tools to handle Zombie Knifers in ZE",
	version = "2.0.0",
	url = ""
}

public void OnPluginStart()
{
	LoadTranslations("common.phrases");

	ConVar cvar;
	HookConVarChange((cvar = CreateConVar("sm_knifeban_block", "1", "1 = Knifebanned clients are blocked, 0 = Knifebanned clients are not blocked", FCVAR_NONE, true, 0.0, true, 1.0)), g_cvKnifeBan);
	g_bKnifeBan = cvar.BoolValue;
	delete cvar;

	AutoExecConfig(true, "plugin.KnifeBan");

	RegAdminCmd("sm_knifeban", Command_Knifeban, ADMFLAG_BAN, "Knifebans a client");
	RegAdminCmd("sm_knifeunban", Command_Knifeunban, ADMFLAG_BAN, "Knifeunbans a client");
	RegAdminCmd("sm_getrawtime", Command_Getrawtime, ADMFLAG_RCON, "Retreives the raw time of the server");
	RegConsoleCmd("sm_knifestatus", Command_Knifestatus, "Returns the knifestatus of a client");
	RegConsoleCmd("sm_knifebanlist", Command_Knifebanlist, "Returns the knifebanlist of all clients currently on the server");

	g_hActiveWeapon = FindSendPropInfo("CBaseCombatCharacter", "m_hActiveWeapon");
	if(g_hActiveWeapon == -1)
		SetFailState("Couldn't find CBaseCombatCharacter::m_hActiveWeapon");

	CreateTimer(5.0, CheckKnifeBans, _, TIMER_REPEAT);

	g_TempBanned = new ArrayList();

	Banlist = new KeyValues("banlist");
	BuildPath(Path_SM, g_sBanListFilePath, sizeof(g_sBanListFilePath), "configs/knifeban/banlist.cfg");
	Banlist.ImportFromFile(g_sBanListFilePath);
	if(!FileExists(g_sBanListFilePath))
		SetFailState("[KnifeBan] Config file missing!");


	for(int i = 1; i <= MaxClients; i++)
	{
		if(IsClientInGame(i))
			OnClientPostAdminCheck(i);
	}
}

public void OnPluginEnd()
{
	Banlist.Rewind();
	if(!Banlist.ExportToFile(g_sBanListFilePath))
		SetFailState("[KnifeBan] Config file missing!");
}

public void g_cvKnifeBan(ConVar convar, const char[] oldValue, const char[] newValue)
{
	g_bKnifeBan = convar.BoolValue;
}

public void OnClientPostAdminCheck(int client)
{
	SDKHook(client, SDKHook_OnTakeDamage, OnTakeDamage);

	char sAuth[32];
	GetClientAuthId(client, AuthId_Steam2, sAuth, sizeof(sAuth));

	Banlist.Rewind();

	if(Banlist.JumpToKey(sAuth))
	{
		int length = Banlist.GetNum("duration");
		int time = Banlist.GetNum("time");

		if(length == -1)
			g_bKnifeBanned[client] = true;
		else if(length > 0)
			CheckIfClientIsStillKnifeBanned(sAuth, client, time, length);
	}

	int accountid = GetSteamAccountID(client);
	if(accountid != 0)
	{
		int index = g_TempBanned.FindValue(accountid);
		if(index != -1)
			g_bKnifeBanned[client] = true;
	}
}

public void OnClientDisconnect(int client)
{
	g_bKnifeBanned[client] = false;
}

public void OnMapEnd()
{
	g_TempBanned.Clear();
}

public Action CheckKnifeBans(Handle timer)
{
	for(int i = 1; i <= MaxClients; i++)
	{
		if(!IsClientInGame(i))
			continue;

		if(g_bKnifeBanned[i])
		{
			char sAuth[32];
			GetClientAuthId(i, AuthId_Steam2, sAuth, sizeof(sAuth));

			Banlist.Rewind();

			int length;
			int time;
			if(Banlist.JumpToKey(sAuth))
			{
				length = Banlist.GetNum("duration");
				time = Banlist.GetNum("time");
			}

			if(length > 0)
				CheckIfClientIsStillKnifeBanned(sAuth, i, time, length);
		}
	}

	return Plugin_Continue;
}

public void CheckIfClientIsStillKnifeBanned(char sAuth[32], int client, int time, int length)
{
	int timesinceknifeban = GetTime() - time;

	if(timesinceknifeban < length * 60)
		g_bKnifeBanned[client] = true;
	else
	{
		g_bKnifeBanned[client] = false;
		Banlist.Rewind();

		if(Banlist.JumpToKey(sAuth))
		{
			Banlist.SetNum("time", 0);
			Banlist.SetNum("duration", 0);
			Banlist.SetString("admin", "");
			Banlist.Rewind();
			Banlist.ExportToFile(g_sBanListFilePath);
		}
	}
}

public Action OnTakeDamage(int victim, int &attacker, int &inflictor, float &damage, int &damagetype, int &weapon, float damageForce[3], float damagePosition[3], int damagecustom)
{
	if(attacker < 1 || attacker > MaxClients)
		return Plugin_Continue;

	if(!IsPlayerAlive(victim))
		return Plugin_Continue;

	if(!g_bKnifeBan)
		return Plugin_Continue;

	if(!g_bKnifeBanned[attacker])
		return Plugin_Continue;

	if(!ZR_IsClientZombie(victim))
		return Plugin_Continue;

	if(!ZR_IsClientHuman(attacker))
		return Plugin_Continue;

	char sWeapon[32];
	int iWeapon = GetEntDataEnt2(attacker, g_hActiveWeapon);
	if(iWeapon != INVALID_ENT_REFERENCE)
		GetEdictClassname(iWeapon, sWeapon, sizeof(sWeapon));
	else
		return Plugin_Continue;

	if(!StrEqual(sWeapon, "weapon_knife") || damagetype & DMG_BLAST)
        return Plugin_Continue;

	CPrintToChat(attacker, "\x07%s[KnifeBan] \x07%sYou are currently knifebanned.", "E01B5D", "F16767");
	damage = 0.0;
	return Plugin_Handled;
}

public Action Command_Knifeban(int client, int args)
{
	if(!GetCmdArgs())
	{
		CReplyToCommand(client, "\x07%s[KnifeBan] \x07%sUsage: sm_knifeban <#userid/name> [duration]", "E01B5D", "F16767");
		return Plugin_Handled;
	}

	char sArguments[2][32];
	GetCmdArg(1, sArguments[0], sizeof(sArguments[]));
	GetCmdArg(2, sArguments[1], sizeof(sArguments[]));

	int target;
	if((target = FindTarget(client, sArguments[0], true)) == -1)
		return Plugin_Handled;

	int length;
	if(GetCmdArgs() >= 2)
	{
		length = StringToInt(sArguments[1]);

		if(length < 0)
		{
			CReplyToCommand(client, "\x07%s[KnifeBan] \x07%sThe given knifeban duration is invalid.", "E01B5D", "F16767");
			return Plugin_Handled;
		}
	}

	if(g_bKnifeBanned[target])
	{
		CReplyToCommand(client, "\x07%s[KnifeBan] \x07%sThe given client is already knifebanned.", "E01B5D", "F16767");
		return Plugin_Handled;
	}

	g_bKnifeBanned[target] = true;
	int time = GetTime();

	char sAdmin[32];
	GetClientName(client, sAdmin, sizeof(sAdmin));
	if(client == 0)
		sAdmin = "Console";

	char sAuth[32];
	GetClientAuthId(target, AuthId_Steam2, sAuth, sizeof(sAuth));

	Banlist.Rewind();

	if(GetCmdArgs() >= 2)
	{
		if(length)
		{
			CPrintToChatAll("\x07%s[KnifeBan] \x07%s%N\x07%s knifebanned \x07%s%N\x07%s for \x07%s%d\x07%s minutes.", "E01B5D", "EDEDED", client, "F16767", "EDEDED", target, "F16767", "EDEDED", length, "F16767");
			LogAction(client, target, "%L knifebanned %L for %d minutes.", client, target, length);
			if(Banlist.JumpToKey(sAuth, true))
			{
				int history = Banlist.GetNum("history");
				Banlist.SetNum("history", history + 1);
				Banlist.SetNum("time", time);
				Banlist.SetNum("duration", length);
				Banlist.SetString("admin", sAdmin);
				Banlist.Rewind();
				Banlist.ExportToFile(g_sBanListFilePath);
			}
		}
		else
		{
			CPrintToChatAll("\x07%s[KnifeBan] \x07%s%N\x07%s knifebanned \x07%s%N\x07%s permanently.", "E01B5D", "EDEDED", client, "F16767", "EDEDED", target, "F16767");
			LogAction(client, target, "%L knifebanned %L permanently.", client, target);
			if(Banlist.JumpToKey(sAuth, true))
			{
				int history = Banlist.GetNum("history");
				Banlist.SetNum("history", history + 1);
				Banlist.SetNum("time", time);
				Banlist.SetNum("duration", -1);
				Banlist.SetString("admin", sAdmin);
				Banlist.Rewind();
				Banlist.ExportToFile(g_sBanListFilePath);
			}
		}
	}
	else
	{
		CPrintToChatAll("\x07%s[KnifeBan] \x07%s%N\x07%s knifebanned \x07%s%N\x07%s temporarily.", "E01B5D", "EDEDED", client, "F16767", "EDEDED", target, "F16767");
		LogAction(client, target, "%L knifebanned %L temporarily.", client, target);
		if(Banlist.JumpToKey(sAuth, true))
		{
			int history = Banlist.GetNum("history");
			Banlist.SetNum("history", history + 1);
			Banlist.SetNum("time", time);
			Banlist.SetString("admin", sAdmin);
			Banlist.Rewind();
			Banlist.ExportToFile(g_sBanListFilePath);
			int accountid = GetSteamAccountID(target);
			g_TempBanned.Push(accountid);
		}
	}

	return Plugin_Handled;
}

public Action Command_Knifeunban(int client, int args)
{
	if(!GetCmdArgs())
	{
		CReplyToCommand(client, "\x07%s[KnifeBan] \x07%sUsage: sm_knifeunban <#userid/name>", "E01B5D", "F16767");
		return Plugin_Handled;
	}

	char sArguments[1][32];
	GetCmdArg(1, sArguments[0], sizeof(sArguments[]));

	int target;
	if((target = FindTarget(client, sArguments[0], true)) == -1)
		return Plugin_Handled;

	if(!g_bKnifeBanned[target])
	{
		CReplyToCommand(client, "\x07%s[KnifeBan] \x07%sThe given client is not knifebanned.", "E01B5D", "F16767");
		return Plugin_Handled;
	}

	g_bKnifeBanned[target] = false;
	Banlist.Rewind();

	char sAuth[32];
	GetClientAuthId(target, AuthId_Steam2, sAuth, sizeof(sAuth));

	if(Banlist.JumpToKey(sAuth))
	{
		int history = Banlist.GetNum("history");
		Banlist.SetNum("history", history - 1);
		Banlist.SetNum("time", 0);
		Banlist.SetNum("duration", 0);
		Banlist.SetString("admin", "");
		Banlist.Rewind();
		Banlist.ExportToFile(g_sBanListFilePath);
	}

	int accountid = GetSteamAccountID(target);
	if(accountid != 0)
	{
		int index = g_TempBanned.FindValue(accountid);
		if(index != -1)
			g_TempBanned.Erase(index);
	}

	CPrintToChatAll("\x07%s[KnifeBan] \x07%s%N\x07%s knifeunbanned \x07%s%N\x07%s.", "E01B5D", "EDEDED", client, "F16767", "EDEDED", target, "F16767");
	LogAction(client, target, "%L knifeunbanned %L.", client, target);

	return Plugin_Handled;
}

public Action Command_Getrawtime(int client, int args)
{
	int time = GetTime();
	ReplyToCommand(client, "%d", time);

	return Plugin_Handled;
}

public Action Command_Knifestatus(int client, int args)
{
	if(CheckCommandAccess(client, "", ADMFLAG_BAN) && GetCmdArgs())
	{
		char sArguments[1][32];
		GetCmdArg(1, sArguments[0], sizeof(sArguments[]));

		int target;
		if((target = FindTarget(client, sArguments[0], true)) == -1)
			return Plugin_Handled;

		char sAuth[32];
		GetClientAuthId(target, AuthId_Steam2, sAuth, sizeof(sAuth));
		int accountid = GetSteamAccountID(target);
		int index = g_TempBanned.FindValue(accountid);

		Banlist.Rewind();
		int length;
		int time;
		int history;
		if(Banlist.JumpToKey(sAuth))
		{
			length = Banlist.GetNum("duration");
			time = Banlist.GetNum("time");
			history = Banlist.GetNum("history");
		}

		CReplyToCommand(client, "\x07%s[KnifeBan] \x07%s%N\x07%s has been knifebanned \x07%s%d\x07%s times before.", "E01B5D", "EDEDED", target, "F16767", "EDEDED", history, "F16767");

		if(g_bKnifeBanned[target] && index != -1)
		{
			CReplyToCommand(client, "\x07%s[KnifeBan] \x07%s%N\x07%s is currently temporarily knifebanned.", "E01B5D", "EDEDED", target, "F16767");
			return Plugin_Handled;
		}
		else if(g_bKnifeBanned[target] && length == -1)
		{
			CReplyToCommand(client, "\x07%s[KnifeBan] \x07%s%N\x07%s is currently permanently knifebanned.", "E01B5D", "EDEDED", target, "F16767");
			return Plugin_Handled;
		}
		else if(g_bKnifeBanned[target] && length > 0)
		{
			char sTimeRemaining[64];
			int timesinceknifeban = GetTime() - time;
			int iTimeRemaining = length * 60 - timesinceknifeban;

			int iDays    = (iTimeRemaining / 86400);
			int iHours   = (iTimeRemaining / 3600) % 24;
			int iMinutes = (iTimeRemaining / 60) % 60;
			int iSeconds = (iTimeRemaining % 60);

			if (iDays)
				Format(sTimeRemaining, sizeof(sTimeRemaining), "%d Days %d Hours %d Minutes %d Seconds", iDays, iHours, iMinutes, iSeconds);
			else if (iHours)
				Format(sTimeRemaining, sizeof(sTimeRemaining), "%d Hours %d Minutes %d Seconds", iHours, iMinutes, iSeconds);
			else if (iMinutes)
				Format(sTimeRemaining, sizeof(sTimeRemaining), "%d Minutes %d Seconds", iMinutes, iSeconds);
			else
				Format(sTimeRemaining, sizeof(sTimeRemaining), "%d Seconds", iSeconds);

			CReplyToCommand(client, "\x07%s[KnifeBan] \x07%s%N\x07%s is currently knifebanned for another \x07%s%s\x07%s.", "E01B5D", "EDEDED", target, "F16767", "EDEDED", sTimeRemaining, "F16767");
			return Plugin_Handled;
		}
		else
		{
			CReplyToCommand(client, "\x07%s[KnifeBan] \x07%s%N\x07%s is currently not knifebanned.", "E01B5D", "EDEDED", target, "F16767");
			return Plugin_Handled;
		}
	}
	else
	{
		char sAuth[32];
		GetClientAuthId(client, AuthId_Steam2, sAuth, sizeof(sAuth));
		int accountid = GetSteamAccountID(client);
		int index = g_TempBanned.FindValue(accountid);

		Banlist.Rewind();
		int length;
		int time;
		if(Banlist.JumpToKey(sAuth))
		{
			length = Banlist.GetNum("duration");
			time = Banlist.GetNum("time");
		}

		if(g_bKnifeBanned[client] && index != -1)
		{
			CReplyToCommand(client, "\x07%s[KnifeBan] \x07%sYou are currently temporarily knifebanned.", "E01B5D", "F16767");
			return Plugin_Handled;
		}
		else if(g_bKnifeBanned[client] && length == -1)
		{
			CReplyToCommand(client, "\x07%s[KnifeBan] \x07%sYou are currently permanently knifebanned.", "E01B5D", "F16767");
			return Plugin_Handled;
		}
		else if (g_bKnifeBanned[client] && length > 0)
		{
			char sTimeRemaining[64];
			int timesinceknifeban = GetTime() - time;
			int iTimeRemaining = length * 60 - timesinceknifeban;


			int iDays    = (iTimeRemaining / 86400);
			int iHours   = (iTimeRemaining / 3600) % 24;
			int iMinutes = (iTimeRemaining / 60) % 60;
			int iSeconds = (iTimeRemaining % 60);

			if (iDays)
				Format(sTimeRemaining, sizeof(sTimeRemaining), "%d Days %d Hours %d Minutes %d Seconds", iDays, iHours, iMinutes, iSeconds);
			else if (iHours)
				Format(sTimeRemaining, sizeof(sTimeRemaining), "%d Hours %d Minutes %d Seconds", iHours, iMinutes, iSeconds);
			else if (iMinutes)
				Format(sTimeRemaining, sizeof(sTimeRemaining), "%d Minutes %d Seconds", iMinutes, iSeconds);
			else
				Format(sTimeRemaining, sizeof(sTimeRemaining), "%d Seconds", iSeconds);

			CReplyToCommand(client, "\x07%s[KnifeBan] \x07%sYou are currently knifebanned for another \x07%s%s\x07%s.", "E01B5D", "F16767", "EDEDED", sTimeRemaining, "F16767");
			return Plugin_Handled;
		}
		else
		{
			CReplyToCommand(client, "\x07%s[KnifeBan] \x07%sYou are currently not knifebanned.", "E01B5D", "F16767");
			return Plugin_Handled;
		}
	}
}

public Action Command_Knifebanlist(int client, int args)
{
	char aBuf[1024];
	char aBuf2[MAX_NAME_LENGTH];

	for(int i = 1; i <= MaxClients; i++)
	{
		if(IsClientInGame(i) && !IsFakeClient(i))
		{
			if(g_bKnifeBanned[i])
			{
				GetClientName(i, aBuf2, sizeof(aBuf2));
				StrCat(aBuf, sizeof(aBuf), aBuf2);
				StrCat(aBuf, sizeof(aBuf), ", ");
			}
		}
	}

	if(strlen(aBuf))
	{
		aBuf[strlen(aBuf) - 2] = 0;
		CReplyToCommand(client, "\x07%s[KnifeBan] \x07%sCurrently knifebanned clients: \x07%s%s", "E01B5D", "F16767", "EDEDED", aBuf);
	}
	else
		CReplyToCommand(client, "\x07%s[KnifeBan] \x07%sCurrently knifebanned clients: \x07%snone", "E01B5D", "F16767", "EDEDED");

	if(CheckCommandAccess(client, "", ADMFLAG_BAN))
		OpenKnifebanlistMenu(client);

	return Plugin_Handled;
}

public void OpenKnifebanlistMenu(int client)
{
	if(client == 0)
		return;

	Menu menu = new Menu(MenuHandler_MainMenu);

	menu.SetTitle("Currently knifebanned clients:", client);

	char sBuffer[32];
	int iMenuItem;
	char sMenuItem[8];

	for(int i = 1; i <= MaxClients; i++)
	{
		if(IsClientInGame(i) && !IsFakeClient(i))
		{
			if(g_bKnifeBanned[i])
			{
				GetClientName(i, sBuffer, sizeof(sBuffer));
				IntToString(iMenuItem, sMenuItem, sizeof(sMenuItem));
				menu.AddItem(sMenuItem, sBuffer);
				iMenuItem++;
			}
		}
	}

	menu.Display(client, MENU_TIME_FOREVER);
}

public int MenuHandler_MainMenu(Menu menu, MenuAction action, int client, int selection)
{
	switch(action)
	{
		case(MenuAction_Select):
		{
			char sMenuItem[8];
			char sBuffer[32];
			int flag;
			menu.GetItem(selection, sMenuItem, sizeof(sMenuItem), flag, sBuffer, sizeof(sBuffer));
			OpenKnifebanlistOfTheClient(client, sBuffer);
		}
		case(MenuAction_End):
		{
			delete menu;
		}
	}
}

public void OpenKnifebanlistOfTheClient(int client, char sBuffer[32])
{
	Menu menuclient = new Menu(MenuHandler_ClientMenu);

	char sTitle[64] = "Knifebanned client: ";

	StrCat(sTitle, sizeof(sTitle), sBuffer);

	int target;
	char sName[32];

	for(int i = 1; i <= MaxClients; i++)
	{
		if(IsClientInGame(i) && !IsFakeClient(i) && g_bKnifeBanned[i])
		{
			GetClientName(i, sName, sizeof(sName));

			if(StrEqual(sName, sBuffer))
			{
				target = i;
				break;
			}
		}
	}

	char sAuth[32];
	GetClientAuthId(target, AuthId_Steam2, sAuth, sizeof(sAuth));

	Banlist.Rewind();

	int length;
	int time;
	int history;
	char sAdmin[32];
	if(Banlist.JumpToKey(sAuth))
	{
		length = Banlist.GetNum("duration");
		time = Banlist.GetNum("time");
		history = Banlist.GetNum("history");
		Banlist.GetString("admin", sAdmin, sizeof(sAdmin));
	}

	menuclient.SetTitle(sTitle, client);

	char sInfo[256];
	Format(sInfo, sizeof(sInfo), "Banned by: %s", sAdmin);

	menuclient.AddItem("0", sInfo);

	if(length > 0)
		Format(sInfo, sizeof(sInfo), "Duration: %d Minutes", length);
	else if(length == -1)
		Format(sInfo, sizeof(sInfo), "Duration: Permanently");
	else
		Format(sInfo, sizeof(sInfo), "Duration: Temporarly");

	menuclient.AddItem("1", sInfo);

	char sTime[32];
	FormatTime(sTime, sizeof(sTime), "%c", time);
	Format(sInfo, sizeof(sInfo), "Date: %s", sTime);

	menuclient.AddItem("2", sInfo);

	Format(sInfo, sizeof(sInfo), "Ban History: %d times", history);

	menuclient.AddItem("3", sInfo);

	menuclient.AddItem("4", "Back to List");

	menuclient.Display(client, MENU_TIME_FOREVER);
}

public int MenuHandler_ClientMenu(Menu menuclient, MenuAction action, int client, int selection)
{
	switch(action)
	{
		case(MenuAction_Select):
		{
			switch(selection)
			{
				case(4): OpenKnifebanlistMenu(client);
			}
		}
		case(MenuAction_End):
		{
			delete menuclient;
		}
	}
}