/*================================================================================
	
	ATTENTION! THIS IS MODIFIED BETA VERSION 1.2!!! ORIGINAL COULD BE FOUND HERE
	http://forums.supercentral.net/index.php?showtopic=125

	--------------------------------
	-*- [ZP] Dragons Portal Ammo Bank -*-
	--------------------------------
	
	~~~~~~~~~~~~~~~
	- Description -
	~~~~~~~~~~~~~~~
	
	Ammo Bank for Dragons Portal Zombie Plague Server.

	Put this in your zombieplague.cfg

	// Laser Mines
	// ---------------
	zp_bank_announce_time 300 // How fast you see text about Banking system
	zp_bank_store_max 5000 // Maximum amount of stored ammopacks per player
	zp_bank_deposit_min 50 // Minimum value to deposit single time
	zp_bank_withdraw_max 100 // Maximum value to withdraw single time
	zp_bank_withdraw_limit 500 // Max amount to withdraw per map.
	zp_bank_savedays 14 // Maximum offline time for user before he will be deleted from DB [0 - delete all, -1 - disabled]
	zp_bank_debug 3 // Logging level. 3 - log all activity, 2 - only DataBase activity, 1 - only autoprune log, 0 - disable.

	Usage:
	Say this in any chat:
	/bank - check balance
	/deposit <amount> - amount to deposit in bank
	/withdraw <amount> - amount to withdraw from bank

	Admin commands
	zp_setammo <name> <amount> - Sets player ammobank value
	zp_addammo <name> <amount> - Adds ammopacks to player ammobank
	Set to flag "a" by default, you can easily change it in code below, or in cmdaccess.ini.
	Commands should be idiot-proof, but be careful in usage. Setting too big ammovalues could ruin gameplay.

================================================================================*/

#include <amxmodx>
#include <amxmisc>
#include <zombieplague>
#include <vault>

stock const ZP_BANK_FMT[] = "^x04[ZP]^x01 %L"
stock const zplog[] = "zp_bank_act.log"

new g_iAmmoPacks[33]
new g_iSessionMovement[33]
new g_iWithdrawLimit[33]
new g_szAuth[33][32]
new cvAnnounceTime
new cvBankMax
new cvDepositMin
new cvWithdrawMax
new cvWithdrawLimit
new cvBankSaveDays
new cvDeBug

LoadClient(id, szAuth[])
{
	static szValue[8]
	new iValue = 0
	new key[38], vaultdata[53], name[34], timestamp[11]

	// Little memory cleanup
	szValue[0] = '^0'
	key[0] = '^0'
	vaultdata[0] = '^0'
	name[0] = '^0'
	timestamp[0] = '^0'

	format(key,37,"ZBANK_%s",szAuth)
	
	get_vaultdata(key,vaultdata,52)
	parse(vaultdata,name,33,timestamp,10,szValue,7)
		
	if (strlen(szValue) > 0) 
	{
		iValue = (g_iAmmoPacks[id] = str_to_num(szValue))
	}
	
	SayText(id, id, ZP_BANK_FMT, id, "ZP_BANK_LOAD", iValue, szAuth, (get_pcvar_num(cvWithdrawLimit) - g_iWithdrawLimit[id]))

	if (get_pcvar_num(cvDeBug) >= 2)
	{	
		if (iValue != 0) 
		{
			log_to_file(zplog,"[ZP] DBLoad: loaded ^"%d^" ammovalue for (Player ^"%s^") (IP ^"%s^")",iValue,name,szAuth)
		}
		else
		{
			log_to_file(zplog,"[ZP] DBLoad: nothing to load for (IP ^"%s^"). So sad.",szAuth)
		}
		
	}
}

SaveClient(id, szAuth[])
{
	static szValue[8]
	new key[38], vaultdata[53], name[32]

	// Little memory cleanup
	szValue[0] = '^0'
	key[0] = '^0'
	vaultdata[0] = '^0'
	name[0] = '^0'

	num_to_str(g_iAmmoPacks[id] + g_iSessionMovement[id], szValue, charsmax(szValue))
	get_user_name(id, name, 31)
	format(key,37,"ZBANK_%s",szAuth) 
	format(vaultdata,52,"^"%s^" %d %s",name,get_systime(),szValue)

	if (str_to_num(szValue) <= 0) 
	{
		remove_vaultdata(key)
	}
	else 
	{
		set_vaultdata(key,vaultdata)
	}

	if (get_pcvar_num(cvDeBug) >= 2)
	{
		if (str_to_num(szValue) > 0) 
		{
			log_to_file(zplog,"[ZP] DBSave: saved ^"%s^" ammovalue for (Player ^"%s^") (IP ^"%s^")",szValue,name,szAuth)
		}
		else
		{
			log_to_file(zplog,"[ZP] DBSave: nothing to save for (IP ^"%s^"). So sad.",szAuth)
		}
	}
}

MemClr(id)
{
	g_szAuth[id][0] = '^0'
	g_iAmmoPacks[id] = 0
	g_iSessionMovement[id] = 0
	g_iWithdrawLimit[id] = 0
}

cleanDB()
{
	// Open up the vault file - read line by line
	// Use vault to delete to any data over a certain age
	new gVaultFile[128]
	if ( !file_exists(gVaultFile) ) return
	get_localinfo("amxx_vault", gVaultFile, 127)

	new ammoSaveDays = get_pcvar_num(cvBankSaveDays)

	if ( ammoSaveDays < 0) return

	if (get_pcvar_num(cvDeBug) >= 1)
	{
		log_to_file(zplog,"[ZP] DBClean: starting DB cleanup...")
	}

	// Create a copy of savekeys to remove
	new temp[128], pcounter
	formatex(temp, 127, "%s~", gVaultFile)

	new vaultFile = fopen(gVaultFile, "r")
	new tempFile = fopen(temp, "w+")

	// Read through the file looking for valid entries
	// Check the epoch time to see if deletion should happen
	new data[90], vaultSaveKey[38], name[34], epoch[11], value[8]
	new maxTime = ammoSaveDays * 24 * 3600
	new curTime = get_systime()
	new bool:dataFound

	while ( !feof(vaultFile) ) {
		data[0] = '^0'

		// Read the line in vault and if zp save info check to see if we should copy vault key to temp file
		// Don't need to read whole line only enough for what we need to parse
		fgets(vaultFile, data, 89)

		if ( equal(data, "ZBANK", 5) ) {
			vaultSaveKey[0] = '^0'
			name[0] = '^0'
			epoch[0] = '^0'
			value[0] = '^0'

			// Only copy keys if older than max save days
			parse(data, vaultSaveKey, 37, name, 33, epoch, 10, value, 7)
			if ( (str_to_num(epoch) + maxTime) < curTime ) {
				fprintf(tempFile, "%s^n", vaultSaveKey)
				dataFound = true
			}
		}
	}

	// Finished reading vault close it
	fclose(vaultFile)

	if ( dataFound ) {
		// Reset to top of temp file
		fseek(tempFile, 0, SEEK_SET)

		// Remove all entries in temp file from vault.ini
		while ( !feof(tempFile) ) {
			vaultSaveKey[0] = '^0'

			fgets(tempFile, vaultSaveKey, 38)
			trim(vaultSaveKey)

			if ( vaultSaveKey[0] != '^0' )
			{
				remove_vaultdata(vaultSaveKey)
				pcounter++
				if (get_pcvar_num(cvDeBug) >= 2)
				{	
					log_to_file(zplog,"[ZP] DBClean: Deleting %s due to defined autoprune",vaultSaveKey)
				}
			}
		}
	}

	fclose(tempFile)
	delete_file(temp)

	if (get_pcvar_num(cvDeBug) >= 1)
	{
		log_to_file(zplog,"[ZP] DBClean: database prunned successfully. %d users deleted. Settings: %d days",pcounter,ammoSaveDays)
	}
}

// Admin set ammopacks for player bank
public cmd_zpbankset(id, level, cid)
{
	// Check for access flag
	if (!cmd_access(id, level, cid, 3))
		return PLUGIN_HANDLED;
	
	// Retrieve arguments
	static arg[32], ammo[8], player, amount
	read_argv(1, arg, sizeof arg - 1)
	read_argv(2, ammo, sizeof arg - 1)
	amount = str_to_num(ammo)
	player = cmd_target(id, arg, CMDTARGET_ONLY_ALIVE | CMDTARGET_ALLOW_SELF)
	
	// Invalid target
	if (!player) return PLUGIN_HANDLED;
	
	if (amount >= 0)
	{
		g_iAmmoPacks[player] = amount
	}

	if (get_pcvar_num(cvDeBug) >= 2)
	{
		new PIP[32],Pname[32]
		get_user_name(player, Pname, 31)
		get_user_ip(player, PIP, 31, 1)
		log_to_file(zplog,"[ZP] BankSet: set ^"%d^" ammovalue for (Player ^"%s^") (IP ^"%s^")",amount,Pname,PIP)
	}

	return PLUGIN_HANDLED;
}

// Admin add ammopacks to player bank
public cmd_zpbankadd(id, level, cid)
{
	// Check for access flag
	if (!cmd_access(id, level, cid, 3))
		return PLUGIN_HANDLED;
	
	// Retrieve arguments
	static arg[32], ammo[8], player, amount
	read_argv(1, arg, sizeof arg - 1)
	read_argv(2, ammo, sizeof arg - 1)
	amount = str_to_num(ammo)
	player = cmd_target(id, arg, CMDTARGET_ONLY_ALIVE | CMDTARGET_ALLOW_SELF)
	
	// Invalid target
	if (!player) return PLUGIN_HANDLED;
	
	g_iAmmoPacks[player] += amount
	
	if (get_pcvar_num(cvDeBug) >= 2)
	{
		new PIP[32],Pname[32]
		get_user_name(player, Pname, 31)
		get_user_ip(player, PIP, 31, 1)
		log_to_file(zplog,"[ZP] BankSet: added ^"%d^" ammovalue for (Player ^"%s^") (IP ^"%s^")",amount,Pname,PIP)
	}
	
	return PLUGIN_HANDLED;
}

// Admin set withdraw limit status for player
public cmd_zpbanklim(id, level, cid)
{
	// Check for access flag
	if (!cmd_access(id, level, cid, 3))
		return PLUGIN_HANDLED;
	
	// Retrieve arguments
	static arg[32], limit[8], player, amount
	read_argv(1, arg, sizeof arg - 1)
	read_argv(2, limit, sizeof arg - 1)
	amount = str_to_num(limit)
	player = cmd_target(id, arg, CMDTARGET_ONLY_ALIVE | CMDTARGET_ALLOW_SELF)
	
	// Invalid target
	if (!player) return PLUGIN_HANDLED;
	
	g_iWithdrawLimit[player] = amount

	if (get_pcvar_num(cvDeBug) >= 2)
	{
		new PIP[32],Pname[32]
		get_user_name(player, Pname, 31)
		get_user_ip(player, PIP, 31, 1)
		log_to_file(zplog,"[ZP] BankSet: set ^"%d^" withdraw limit for (Player ^"%s^") (IP ^"%s^")",amount,Pname,PIP)
	}

	return PLUGIN_HANDLED;
}

// Admin check players bank accounts
public cmd_zpbanklist(id, level, cid)
{
	if (!cmd_access(id, level, cid, 1))
		return PLUGIN_HANDLED

	new playerCount
	new players[32], name[32]

	get_players(players, playerCount)

	console_print(id,"Players Bank Accounts:^n")
	
	new pid, teamName[5]
	for ( new team = 1; team >= 0; team-- ) {
		for ( new x = 0; x < playerCount; x++ ) {
			pid = players[x]
			if ( zp_get_user_zombie(pid) != team ) continue
			get_user_name(pid, name, 31)

			teamName[0] = '^0'
			if ( zp_get_user_zombie(pid) == 1 ) copy(teamName, 4, "ZM :")
			else if ( zp_get_user_zombie(pid) == 0 ) copy(teamName, 4, "HU :")
			else copy(teamName, 4, "S :")

			console_print(id, "%s%-24s (Bank: ^"%d^") (Pocket: ^"%d^") (WLimit: ^"%d^")", teamName, name, (g_iAmmoPacks[pid] + g_iSessionMovement[pid]), zp_get_user_ammo_packs(pid),  (get_pcvar_num(cvWithdrawLimit) - g_iWithdrawLimit[pid]))
		}
	}

	console_print(id, "")

	if (get_pcvar_num(cvDeBug) >= 3)
	{
		new PIP[32],Pname[32]
		get_user_name(id, Pname, 31)
		get_user_ip(id, PIP, 31, 1)
		log_to_file(zplog,"[ZP] Bank: (Player ^"%s^") (IP ^"%s^") asked for players bank account list",Pname,PIP)
	}

	return PLUGIN_HANDLED;
}

public plugin_init()
{
	register_plugin("[ZP] Ammo Bank", "1.2", "danielkza and Lethal")
	
	register_clcmd("say", "Command_Say")
	register_dictionary("zp_bank_ammo.txt")
	
	cvAnnounceTime	= register_cvar("zp_bank_announce_time", "300")
	cvBankMax		= register_cvar("zp_bank_store_max", "5000")
	cvDepositMin	= register_cvar("zp_bank_deposit_min", "50")
	cvWithdrawMax	= register_cvar("zp_bank_withdraw_max", "100")
	cvWithdrawLimit	= register_cvar("zp_bank_withdraw_limit", "500")
	cvBankSaveDays	= register_cvar("zp_bank_savedays", "14")
	cvDeBug	= register_cvar("zp_bank_debug", "3")

	register_concmd("playerbank", "cmd_zpbanklist", ADMIN_BAN, " - Show current ammobank values for players")
	register_concmd("zp_setbank", "cmd_zpbankset", ADMIN_IMMUNITY, "<name> <amount> - Set player ammobank value")
	register_concmd("zp_limbank", "cmd_zpbanklim", ADMIN_IMMUNITY, "<name> <amount> - Set player ammobank withdraw limit status")
	register_concmd("zp_addbank", "cmd_zpbankadd", ADMIN_IMMUNITY, "<name> <amount> - Add ammopacks to player ammobank")
	
	Task_Announce()
}

public plugin_end()
{
	// Client's should have already been saved by now (client_disconnect is called before plugin_end).
	// But it costs nothing to be sure.
	new iPlayers[32], iNum
	get_players(iPlayers, iNum)
	
	new iPlayer
	for(new i=0; i < iNum;i++)
	{
		iPlayer = iPlayers[i]

		if (strlen(g_szAuth[iPlayer]) > 0)
			SaveClient(iPlayer, g_szAuth[iPlayer])

		MemClr(iPlayer)
	}
	
	cleanDB()
}

public client_putinserver(id)
{
	static szAuth[32]
	MemClr(id)
	get_user_ip(id, szAuth, 31, 1)
	copy(g_szAuth[id], charsmax(g_szAuth[]), szAuth)
	LoadClient(id, szAuth)	
}

public client_disconnect(id)
{	
	if (strlen(g_szAuth[id]) > 0)
		SaveClient(id, g_szAuth[id])
	
	MemClr(id)
}

new msgSayText = -1
stock bool:SayText(const receiver, sender, const msg[], any:...)
{
	if(msgSayText == -1)
		msgSayText = get_user_msgid("SayText")
		
	if(msgSayText)
	{	
		if(!sender)
			sender = receiver
		
		static buffer[512]
		vformat(buffer,charsmax(buffer),msg,4)
		
		if(receiver)
			message_begin(MSG_ONE_UNRELIABLE,msgSayText,_,receiver)
		else
			message_begin(MSG_BROADCAST,msgSayText)
		
		write_byte(sender)
		write_string(buffer)
		message_end()
		
		return true
	}
	
	return false
}

public Task_Announce()
{
	static iPlayers[32], iNum, iPlayer
	get_players(iPlayers, iNum)
	
	for(new i=0; i < iNum;i++)
	{
		iPlayer = iPlayers[i]
		SayText(iPlayer, iPlayer, ZP_BANK_FMT, LANG_PLAYER, "ZP_BANK_ANNOUNCE")
	}
	
	set_task(get_pcvar_float(cvAnnounceTime), "Task_Announce")
}

enum
{
	CMD_DEPOSIT = 1,
	CMD_WITHDRAW,
	CMD_INFO
}

public Command_Say(id)
{
	static szArgs[32]
	read_args(szArgs, charsmax(szArgs))
	remove_quotes(szArgs)
	
	static szArg1[32], szArg2[32]
	parse(szArgs, szArg1, charsmax(szArg1), szArg2, charsmax(szArg2))
	
	new iCommand = 0
	if(equali(szArg1, "/deposit"))
		iCommand = CMD_DEPOSIT
	else if(equali(szArg1, "/withdraw"))
		iCommand = CMD_WITHDRAW
	else if(equali(szArg1, "/bank"))
		iCommand = CMD_INFO
	
	if(iCommand)
	{
		if(iCommand == CMD_INFO)
			Command_Info(id)
		else
		{
			new iValue = str_to_num(szArg2)
			if(iValue <= 0)
				SayText(id, id, ZP_BANK_FMT, id, "ZP_BANK_INVALID_AMOUNT")
			
			else if(iCommand == CMD_DEPOSIT)
				Command_Deposit(id, iValue)
			else
				Command_Withdraw(id, iValue)
		}
		
		return PLUGIN_HANDLED
		
	}
	
	return PLUGIN_CONTINUE
}

Command_Info(id)
{
	SayText(id, id, ZP_BANK_FMT, id, "ZP_BANK_LOAD", g_iAmmoPacks[id] + g_iSessionMovement[id], g_szAuth[id], (get_pcvar_num(cvWithdrawLimit) - g_iWithdrawLimit[id]))
}
	
Command_Deposit(id, iDeposit)
{
	new iCurrentAmount = zp_get_user_ammo_packs(id)
	new iDepositMin = get_pcvar_num(cvDepositMin)
	if(!iCurrentAmount)
	{
		SayText(id, id, ZP_BANK_FMT, id, "ZP_BANK_NO_AMMO")
		return
	}
	
	if(iDeposit > iCurrentAmount)
		iDeposit = iCurrentAmount
	
	if(iCurrentAmount < iDepositMin)
	{
		SayText(id, id, ZP_BANK_FMT, id, "ZP_BANK_DMIN", iDepositMin)
		return
	}

	if(iDeposit < iDepositMin)
	{
		SayText(id, id, ZP_BANK_FMT, id, "ZP_BANK_DMIN", iDepositMin)
		return
	}

	new iCurrent = g_iAmmoPacks[id] + g_iSessionMovement[id]
	new iSum = iCurrent + iDeposit
	new iBankMax = get_pcvar_num(cvBankMax)
	
	if(iBankMax && (iSum > iBankMax))
	{
		iDeposit = iBankMax - iCurrent
		iSum = iBankMax
	
		SayText(id, id, ZP_BANK_FMT, id, "ZP_BANK_MAX", iBankMax)
	}
	
	if(iDeposit)
	{
		g_iSessionMovement[id] += iDeposit

		if (g_iWithdrawLimit[id] > abs(iDeposit))
		{
			g_iWithdrawLimit[id] -= abs(iDeposit)
		}
		else
		{
			g_iWithdrawLimit[id] = 0
		}

		SayText(id, id, ZP_BANK_FMT, id, "ZP_BANK_DEPOSIT", iDeposit, iSum, (get_pcvar_num(cvWithdrawLimit) - g_iWithdrawLimit[id]))
		zp_set_user_ammo_packs(id, iCurrentAmount - iDeposit)
		
		if (get_pcvar_num(cvDeBug) >= 3)
		{
			new PIP[32],Pname[32]
			get_user_ip(id, PIP, 31, 1)
			get_user_name(id, Pname, 31)
			log_to_file(zplog,"[ZP] Bank: (Player ^"%s^") (IP ^"%s^") deposited ^"%d^" ammovalue to vault",Pname,PIP,iDeposit)
		}
	}
}


Command_Withdraw(id, iWithdraw)
{
	new iDeposited = g_iAmmoPacks[id] + g_iSessionMovement[id]
	new iWithdrawMax = get_pcvar_num(cvWithdrawMax)
	new iWithdrawLimit = get_pcvar_num(cvWithdrawLimit)
	
	if(!iDeposited)
	{
		SayText(id, id, ZP_BANK_FMT, id, "ZP_BANK_EMPTY")
		return
	}

	if(iWithdraw > iDeposited)
		iWithdraw = iDeposited

	if(iWithdraw > iWithdrawMax)
	{
		SayText(id, id, ZP_BANK_FMT, id, "ZP_BANK_WMAX", iWithdrawMax)
		return
	}

	if((g_iWithdrawLimit[id] > iWithdrawLimit) || ((g_iWithdrawLimit[id] + iWithdraw) > iWithdrawLimit))
	{
		SayText(id, id, ZP_BANK_FMT, id, "ZP_BANK_WLIM", iWithdrawLimit, (iWithdrawLimit - g_iWithdrawLimit[id]))
		return
	}
	
	g_iWithdrawLimit[id] += iWithdraw
	g_iSessionMovement[id] -= iWithdraw
	
	SayText(id, id, ZP_BANK_FMT, id, "ZP_BANK_WITHDRAW", iWithdraw, iDeposited - iWithdraw, (get_pcvar_num(cvWithdrawLimit) - g_iWithdrawLimit[id]))
	zp_set_user_ammo_packs(id, zp_get_user_ammo_packs(id) + iWithdraw)
	if (get_pcvar_num(cvDeBug) >= 3)
	{
		new PIP[32],Pname[32]
		get_user_ip(id, PIP, 31, 1)
		get_user_name(id, Pname, 31)
		log_to_file(zplog,"[ZP] Bank: (Player ^"%s^") (IP ^"%s^") withdrawed ^"%d^" ammovalue from vault",Pname,PIP,iWithdraw)
	}
}