#pragma semicolon 1

#include <amxmodx>
#include <reapi>
#include <nvault>

native csd_chat_rutbe(oyuncu, const rutbe[]);

enum _:szRank {
	Rank_Name,
	Rank_MaxXp,
	Rank_TxtSprFileName
}

/* {"RankName", Maximum XP to level up, "txt_spr_name"} */
new const szRankNames[][][] = {
	{"Unranked", 20, ""},
	{"Silver I", 20, "silver1"},
	{"Silver II", 24, "silver2"},
	{"Silver III", 28, "silver3"},
	{"Silver IV", 32, "silver4"},
	{"Silver Elite", 36, "silverelite"},
	{"Silver Elite Master", 40, "silverelitemaster"},
	{"Gold Nova I", 45, "goldnova1"},
	{"Gold Nova II", 50, "goldnova2"},
	{"Gold Nova III", 55, "goldnova3"},
	{"Gold Nova Master", 60, "goldnovamaster"},
	{"Master Guardian I", 66, "masterguardian1"},
	{"Master Guardian II", 72, "masterguardian2"},
	{"Master Guardian Elite", 78, "masterguardianelite"},
	{"Distinguished Master Guardian", 86, "distinguishedmasterguardian"},
	{"Legendary Eagle", 95, "legendaryeagle"},
	{"Legendary Eagle Master", 105, "legendaryeaglemaster"},
	{"Supreme Master First Class", 110, "suprememasterfirstclass"},
	{"The Global Elite", 120, "theglobalelite"}
};

new bool:g_PlayerRankedUp[MAX_CLIENTS + 1],
	g_rank[MAX_CLIENTS + 1],
	g_xp[MAX_CLIENTS + 1],
	pCvars[6],
	pMsgIds[4],
	pForward[2],
	hud_sync;

public plugin_init() {
	register_plugin("Advanced Rank System", "0.0.2", "PurposeLess");

	register_event("CurWeapon", "@Event_CurWeapon", "be", "1=1");

	RegisterHookChain(RG_CBasePlayer_Killed, "@CBasePlayer_Killed", .post = true);

	if(get_member_game(m_bMapHasBombZone)) {
		RegisterHookChain(RG_CGrenade_DefuseBombEnd, "@CGrenade_DefuseBombEnd", .post = true);
		RegisterHookChain(RG_PlantBomb, "@PlantBomb", .post = true);
	}

	bind_pcvar_num(create_cvar("ars_dead_xp", "-2"), pCvars[0]);
	bind_pcvar_num(create_cvar("ars_kill_xp", "3"), pCvars[1]);
	bind_pcvar_num(create_cvar("ars_kill_hp_xp", "2"), pCvars[2]);
	bind_pcvar_num(create_cvar("ars_defuse_xp", "3"), pCvars[3]);
	bind_pcvar_num(create_cvar("ars_plant_xp", "3"), pCvars[4]);
	bind_pcvar_num(create_cvar("ars_spr_appear_time", "5"), pCvars[5]);

	pMsgIds[0] = get_user_msgid("WeaponList");
	pMsgIds[1] = get_user_msgid("SetFOV");
	pMsgIds[2] = get_user_msgid("CurWeapon");
	pMsgIds[3] = get_user_msgid("HideWeapon");

	pForward[0] = CreateMultiForward("ars_rank_up", ET_IGNORE, FP_CELL);
	pForward[1] = CreateMultiForward("ars_rank_down", ET_IGNORE, FP_CELL);

	hud_sync = CreateHudSyncObj();
}

public plugin_precache() {
	for(new i = 1; i < sizeof(szRankNames); i++) {
		precache_generic(fmt("sprites/ars/%s.txt", szRankNames[i][Rank_TxtSprFileName][0]));
		precache_generic(fmt("sprites/ars/%s.spr", szRankNames[i][Rank_TxtSprFileName][0]));
	}
}

public plugin_natives() {
	register_native("ars_get_user_xp", "@ars_get_user_xp");
	register_native("ars_get_user_rank", "@ars_get_user_rank");
	register_native("ars_get_user_rankname", "@ars_get_user_rankname");
}

@ars_get_user_xp() {
	new pPlayer = get_param(1);

	return g_xp[pPlayer];
}

@ars_get_user_rank() {
	new pPlayer = get_param(1);

	return g_rank[pPlayer];
}

@ars_get_user_rankname() {
	new pPlayer = get_param(1);

	set_array(2, szRankNames[g_rank[pPlayer]][Rank_Name], get_param(3));
}

public client_putinserver(pPlayer) {
	if(is_user_bot(pPlayer)) {
		return;
	}

	set_task(1.0, "@ShowHudmessage", pPlayer, .flags = "b");
}

@ShowHudmessage(const pPlayer) {
	set_hudmessage(210, 105, 30, 0.01, 0.15, 0, _, 1.0, 0.1, 0.1);
	ShowSyncHudMsg(pPlayer, hud_sync, "Rank: %s^nRank Xp: %i/%i", szRankNames[g_rank[pPlayer]][Rank_Name], g_xp[pPlayer], szRankNames[g_rank[pPlayer]][Rank_MaxXp]);
}

@Event_CurWeapon(const pPlayer) {
	if(!g_PlayerRankedUp[pPlayer] || get_member(pPlayer, m_iFOV) != 90) {
		return;
	}

	Show_Rank_Event(pPlayer);
}

@CBasePlayer_Killed(const pVictim, pAttacker, iGib) {
	if(pVictim == pAttacker || !is_user_connected(pAttacker)) {
		return;
	}

	g_xp[pVictim] += pCvars[0];
	g_xp[pAttacker] += pCvars[1];

	if(get_member(pVictim, m_bHeadshotKilled)) {
		g_xp[pAttacker] += pCvars[2];
	}

	RankCheck(pAttacker);
	RankCheck(pVictim);
}

@CGrenade_DefuseBombEnd(const pBomb, const pPlayer, bool:bDefused) {
	if(bDefused && pCvars[3]) {
		g_xp[pPlayer] += pCvars[3];
		RankCheck(pPlayer);
	}
}

@PlantBomb(const pPlayer, Float:vecStart[3], Float:vecVelocity[3]) {
	if(pCvars[4]) {
		g_xp[pPlayer] += pCvars[4];
		RankCheck(pPlayer);
	}
}

const TASKID_ARS = 1337;

RankCheck(const pPlayer) {
	if(g_xp[pPlayer] >= szRankNames[g_rank[pPlayer]][Rank_MaxXp][0]) {
		if(g_rank[pPlayer] == sizeof(szRankNames) - 1) {
			g_xp[pPlayer] = szRankNames[g_rank[pPlayer]][Rank_MaxXp][0];
			return;
		}
		g_xp[pPlayer] -= szRankNames[g_rank[pPlayer]][Rank_MaxXp][0];
		g_rank[pPlayer]++;

		csd_chat_rutbe(pPlayer, fmt("%s", szRankNames[g_rank[pPlayer]][Rank_Name]));

		g_PlayerRankedUp[pPlayer] = true;
		Show_Rank_Event(pPlayer);
		remove_task(pPlayer + TASKID_ARS);
		set_task(float(pCvars[5]), "@Clear_Rank_Event", pPlayer + TASKID_ARS);

		ExecuteForward(pForward[0], _, pPlayer);
	}
	else if(g_xp[pPlayer] <= 0) {
		if(g_rank[pPlayer] < 2) {
			g_xp[pPlayer] = 0;
			return;
		}

		g_rank[pPlayer]--;
		g_xp[pPlayer] = szRankNames[g_rank[pPlayer]][Rank_MaxXp][0] - g_xp[pPlayer];

		csd_chat_rutbe(pPlayer, fmt("%s", szRankNames[g_rank[pPlayer]][Rank_Name]));
		ExecuteForward(pForward[1], _, pPlayer);
	}
}

Show_Rank_Event(const pPlayer) {
	new ammo, weapon = get_user_weapon(pPlayer, ammo);

	switch(weapon) {
		case CSW_P228: SetMessage_WeaponList(pPlayer, 9, 52);
		case CSW_HEGRENADE: SetMessage_WeaponList(pPlayer, 12, 1);
		case CSW_XM1014: SetMessage_WeaponList(pPlayer, 5, 32);
		case CSW_C4: SetMessage_WeaponList(pPlayer, 14, 1);
		case CSW_MAC10: SetMessage_WeaponList(pPlayer, 6, 100);
		case CSW_AUG: SetMessage_WeaponList(pPlayer, 4, 90);
		case CSW_SMOKEGRENADE: SetMessage_WeaponList(pPlayer, 13, 1);
		case CSW_ELITE: SetMessage_WeaponList(pPlayer, 10, 120);
		case CSW_FIVESEVEN: SetMessage_WeaponList(pPlayer, 7, 100);
		case CSW_UMP45: SetMessage_WeaponList(pPlayer, 6, 100);
		case CSW_GALIL: SetMessage_WeaponList(pPlayer, 4, 90);
		case CSW_FAMAS: SetMessage_WeaponList(pPlayer, 4, 90);
		case CSW_USP: SetMessage_WeaponList(pPlayer, 6, 100);
		case CSW_GLOCK18: SetMessage_WeaponList(pPlayer, 10, 120);
		case CSW_MP5NAVY: SetMessage_WeaponList(pPlayer, 10, 120);
		case CSW_M249: SetMessage_WeaponList(pPlayer, 3, 200);
		case CSW_M3: SetMessage_WeaponList(pPlayer, 5, 32);
		case CSW_M4A1: SetMessage_WeaponList(pPlayer, 4, 90);
		case CSW_TMP: SetMessage_WeaponList(pPlayer, 10, 120);
		case CSW_FLASHBANG: SetMessage_WeaponList(pPlayer, 11, 2);
		case CSW_DEAGLE: SetMessage_WeaponList(pPlayer, 8, 35);
		case CSW_SG552: SetMessage_WeaponList(pPlayer, 4, 90);
		case CSW_AK47: SetMessage_WeaponList(pPlayer, 2, 90);
		case CSW_KNIFE: SetMessage_WeaponList(pPlayer, -1, -1);
		case CSW_P90: SetMessage_WeaponList(pPlayer, 7, 100);
		case CSW_SCOUT: SetMessage_WeaponList(pPlayer, 2, 90);
		case CSW_SG550: SetMessage_WeaponList(pPlayer, 4, 90);
		case CSW_AWP: SetMessage_WeaponList(pPlayer, 1, 30);
		case CSW_G3SG1: SetMessage_WeaponList(pPlayer, 2, 90);
		default: return;
	}

	SetMessage_SetFOV(pPlayer, 89);
	SetMessage_CurWeapon(pPlayer, ammo);
	SetMessage_SetFOV(pPlayer, 90);
}

@Clear_Rank_Event(TaskId) {
	new pPlayer = TaskId - TASKID_ARS;

	g_PlayerRankedUp[pPlayer] = false;
	SetMessage_HideWeapon(pPlayer);
}

SetMessage_WeaponList(const pPlayer, const pAmmoId, const pAmmoMaxAmount) {
	message_begin(MSG_ONE, pMsgIds[0], .player = pPlayer); {
		write_string(fmt("ars/%s", szRankNames[g_rank[pPlayer]][Rank_TxtSprFileName]));
		write_byte(pAmmoId);
		write_byte(pAmmoMaxAmount);
		write_byte(-1);
		write_byte(-1);
		write_byte(0);
		write_byte(11);
		write_byte(2);
		write_byte(0);
	}
	message_end();
}

SetMessage_SetFOV(const pPlayer, const FOV) {
	message_begin(MSG_ONE, pMsgIds[1], .player = pPlayer); {
		write_byte(FOV);
	}
	message_end();
}

SetMessage_CurWeapon(const pPlayer, const ammo) {
	message_begin(MSG_ONE, pMsgIds[2], .player = pPlayer); {
		write_byte(1);
		write_byte(2);
		write_byte(ammo);
	}
	message_end();
}

SetMessage_HideWeapon(const pPlayer) {
	message_begin(MSG_ONE, pMsgIds[3], .player = pPlayer);
	write_byte(0);
	message_end();
}

/* nvault */

new g_vault;

public plugin_cfg() {
	g_vault = nvault_open("AdvancedRankSystem");

	if(g_vault == INVALID_HANDLE) {
		set_fail_state("Unknown nvault for AdvancedRankSystem");
	}
}

public plugin_end() {
	nvault_close(g_vault);
}

public client_authorized(pPlayer, const authid[]) {
	g_xp[pPlayer] = nvault_get(g_vault, fmt("%s_xp", authid));
	g_rank[pPlayer] = nvault_get(g_vault, fmt("%s_rank", authid));

	csd_chat_rutbe(pPlayer, fmt("%s", szRankNames[g_rank[pPlayer]][Rank_Name]));
}

public client_disconnected(pPlayer) {
	new authid[MAX_AUTHID_LENGTH], data[MAX_AUTHID_LENGTH+10];
	get_user_authid(pPlayer, authid, charsmax(authid));

	num_to_str(g_xp[pPlayer], data, charsmax(data));
	nvault_pset(g_vault, fmt("%s_xp", authid), data);

	num_to_str(g_rank[pPlayer], data, charsmax(data));
	nvault_pset(g_vault, fmt("%s_rank", authid), data);

	remove_task(pPlayer);
	remove_task(pPlayer + TASKID_ARS);
}