#pragma semicolon 1
#pragma newdecls required

#include <sdktools>
#include <sdkhooks>
#include <sourcemod>

#include <tf2>
#include <tf2_stocks>
#include <multicolors>

#define PLUGIN_VERSION "1.4.8.9"
#define FCVAR FCVAR_NOTIFY|FCVAR_DONTRECORD|FCVAR_CHEAT

#define ACHIEVEMENT_START 1
#define ACHIEVEMENT_END 3000
#define BATCH_SIZE 100
#define BATCH_DELAY 0.1
#define REPEAT_COUNT 30

new g_Steps[][2] = {
	{1, 100}, // General
	{1000, 1099}, // Scout
	{1100, 1199}, // Sniper
	{1200, 1299}, // Soldier 
	{1300, 1399}, // Demoman
	{1400, 1499}, // Medic
	{1500, 1599}, // Heavy
	{1600, 1699}, // Pyro
	{1700, 1799}, // Spy
	{1800, 1899}, // Engineer
	{1900, 1999}, // Halloween
	{2000, 2099}, // Replay
	{2100, 2199}, // Christmas
	{2200, 2299}, // Foundry
	{2300, 2399}, // MvM
	{2400, 2499} // Doomsday
};

new TFClassType:g_RequiredClass[] = {
	TFClass_Unknown,
	TFClass_Scout,
	TFClass_Sniper,
	TFClass_Soldier,
	TFClass_DemoMan,
	TFClass_Medic,
	TFClass_Heavy,
	TFClass_Pyro,
	TFClass_Spy,
	TFClass_Engineer,
	TFClass_Unknown,
	TFClass_Unknown,
	TFClass_Unknown,
	TFClass_Unknown,
	TFClass_Unknown,
	TFClass_Unknown
};

new g_Step[MAXPLAYERS + 1];
new g_CurrentBatch[MAXPLAYERS + 1];
new g_RepeatIteration[MAXPLAYERS + 1];
Handle g_HudSync;

public APLRes:AskPluginLoad2(Handle:myself, bool:late, String:error[], err_max) {
	
	new String:Game[32];
	GetGameFolderName(Game, sizeof(Game));
	if(!StrEqual(Game, "tf")) {
		Format(error, err_max, "[Instant Achivements] This plugin works only in Team Fortress 2.");
		return APLRes_Failure;
	}
	return APLRes_Success;
}

public Plugin:myinfo = {
	name = "[TF2] Instant Achievement Mod",
	author = "Kuroiwa, WOLFA22",
	description = "Provides /givemeall and /giveitems commands that rewards clients with achievements.",
	version = PLUGIN_VERSION,
	url = "http://sibnet-software.ru"
};

public OnPluginStart() {
	
	CreateConVar("sm_achmod_version", PLUGIN_VERSION, "Instant Achievement Mod Plugin Version", FCVAR);
	
	RegConsoleCmd("sm_givemeall", Command_GiveMeAll, "Unlocks all achievements for you.");
	RegConsoleCmd("sm_giveitems", Command_GiveItems, "Unlocks items achievements for you.");
	RegConsoleCmd("sm_items", Command_GiveItems, "Unlocks items achievements for you.");
	
	g_HudSync = CreateHudSynchronizer();

	for(int i = 1; i <= MaxClients; i++) {
		if(IsClientInGame(i)) {
			g_Step[i] = -1;
			g_CurrentBatch[i] = 0;
		}
	}
}

public void OnClientPutInServer(int client) {
	g_Step[client] = -1;
	g_CurrentBatch[client] = 0;
}

public Action:Command_GiveItems(client, args) {
	
	if(!IsClientInGame(client)) {
		return Plugin_Handled;
	}
	
	if(g_CurrentBatch[client] > 0) {
		CPrintToChat(client, "{CRIMSON}[{RED}Instant Achievements{CRIMSON}]{DEFAULT} Разблокировка уже выполняется, подождите...");
		return Plugin_Handled;
	}
	
	if(g_Step[client] != -1) {
		CPrintToChat(client, "{CRIMSON}[{RED}Instant Achievements{CRIMSON}]{DEFAULT} Разблокировка всех достижений уже выполняется, подождите...");
		return Plugin_Handled;
	}
	
	CPrintToChat(client, "{CRIMSON}[{RED}Instant Achievements{CRIMSON}]{DEFAULT} Выполняется разблокировка достижений...");
	
	g_CurrentBatch[client] = ACHIEVEMENT_START;
	CreateTimer(BATCH_DELAY, Timer_ProcessBatch, GetClientUserId(client), TIMER_REPEAT);
	
	return Plugin_Handled;
}

public Action:Timer_ProcessBatch(Handle:timer, any:userid) {
	
	new client = GetClientOfUserId(userid);
	if(!client || !IsClientInGame(client)) {
		if(client) {
			g_CurrentBatch[client] = 0;
		}
		return Plugin_Stop;
	}
	
	new batchEnd = g_CurrentBatch[client] + BATCH_SIZE - 1;
	if(batchEnd > ACHIEVEMENT_END) {
		batchEnd = ACHIEVEMENT_END;
	}
	
	// Обрабатываем текущий пакет
	for(new i = g_CurrentBatch[client]; i <= batchEnd; i++) {
		Unlock(client, i);
	}
	
	// Вычисляем прогресс
	new progress = ((g_CurrentBatch[client] - ACHIEVEMENT_START + BATCH_SIZE) * 100) / (ACHIEVEMENT_END - ACHIEVEMENT_START + 1);
	if(progress > 100) progress = 100;
	
	// Отображаем прогресс через HUD
	SetHudTextParams(-1.0, 0.4, 5.0, 255, 100, 100, 255, 0, 0.0, 0.0, 0.0);
	ShowSyncHudText(client, g_HudSync, "РАЗБЛОКИРОВКА ДОСТИЖЕНИЙ\nПрогресс: %d%%\n\n[%s%s]", 
		progress,
		GetProgressBar(progress),
		GetEmptyBar(progress));
	
	// Проверяем, закончили ли мы
	if(batchEnd >= ACHIEVEMENT_END) {
		SetHudTextParams(-1.0, 0.4, 3.0, 100, 255, 100, 255, 0, 0.0, 0.0, 0.0);
		ShowSyncHudText(client, g_HudSync, "ГОТОВО!\nВсе ачивки разблокированы!\n");
		CPrintToChat(client, "{CRIMSON}[{RED}Instant Achievements{CRIMSON}]{DEFAULT} Все возможные {RED}ачивки{DEFAULT} были разблокированы.");
		g_CurrentBatch[client] = 0;
		return Plugin_Stop;
	}
	
	// Переходим к следующему пакету
	g_CurrentBatch[client] += BATCH_SIZE;
	return Plugin_Continue;
}

public Action:Command_GiveMeAll(client, args) {
	
	if(g_Step[client] != -1) 
	{
		CPrintToChat(client, "{GREEN}[{LIGHTGREEN}Achievements{GREEN}]{DEFAULT} Разблокировка уже выполняется, подождите...");
		return Plugin_Handled;
	}
	
	if(g_CurrentBatch[client] > 0) {
		CPrintToChat(client, "{GREEN}[{LIGHTGREEN}Achievements{GREEN}]{DEFAULT} Разблокировка предметов уже выполняется, подождите...");
		return Plugin_Handled;
	}
	
	if(!IsPlayerAlive(client))
	{
		CPrintToChat(client, "{GREEN}[{LIGHTGREEN}Achievements{GREEN}]{DEFAULT} Вы должны быть живы для использования команды");
		return Plugin_Handled;
	}
	
	TF2_RespawnPlayer(client);
	CPrintToChat(client, "{GREEN}[{LIGHTGREEN}Achievements{GREEN}]{DEFAULT} Выполняется разблокировка достижений...");
	CPrintToChat(client, "{GREEN}[{LIGHTGREEN}Achievements{GREEN}]{DEFAULT} Каждый класс будет обработан {LIGHTGREEN}30 раз{DEFAULT} для накопительных достижений.");
	
	g_Step[client] = 0;
	g_RepeatIteration[client] = 0;
	CreateTimer(0.1, Timer_PrepNextStep, GetClientUserId(client));
	return Plugin_Handled;
}

public Action:Timer_PrepNextStep(Handle:timer, any:userid) {
	
	new client = GetClientOfUserId(userid);
	if(!client) {
		return;
	}
	
	TF2_RemoveAllWeapons(client);
	SetEntityMoveType(client, MOVETYPE_NONE);
	
	if(g_RequiredClass[g_Step[client]] != TFClass_Unknown) {
		TF2_SetPlayerClass(client, g_RequiredClass[g_Step[client]]);
	}
	
	// Вычисляем общий прогресс с учетом повторов
	new totalSteps = sizeof(g_Steps) * REPEAT_COUNT;
	new currentProgress = (g_Step[client] * REPEAT_COUNT + g_RepeatIteration[client]);
	new progressPercent = (currentProgress * 100) / totalSteps;
	
	char className[32];
	GetClassName(g_Step[client], className, sizeof(className));
	
	// Красивое HUD сообщение
	SetHudTextParams(-1.0, 0.35, 5.0, 100, 200, 255, 255, 0, 0.0, 0.0, 0.0);
	ShowSyncHudText(client, g_HudSync, 
		"РАЗБЛОКИРОВКА ВСЕХ ДОСТИЖЕНИЙ\nОбщий прогресс: %d%%\n\nКласс: %s (%d/%d)\nИтерация: %d/%d\n%s%s",
		progressPercent,
		className,
		g_Step[client] + 1, sizeof(g_Steps),
		g_RepeatIteration[client] + 1, REPEAT_COUNT,
		GetProgressBar(progressPercent),
		GetEmptyBar(progressPercent));
	
	CreateTimer(0.1, Timer_DoNextStep, userid);
}

public Action:Timer_DoNextStep(Handle:timer, any:userid) {
	
	new client = GetClientOfUserId(userid);
	if(!client) {
		return;
	}
	
	// Разблокируем ачивки текущего класса
	for(new i = g_Steps[g_Step[client]][0]; i <= g_Steps[g_Step[client]][1]; i++) {
		Unlock(client, i);
	}
	
	// Увеличиваем счетчик повторов
	g_RepeatIteration[client]++;
	
	// Если достигли 30 повторов для текущего класса
	if(g_RepeatIteration[client] >= REPEAT_COUNT) {
		g_RepeatIteration[client] = 0;
		g_Step[client]++;
		
		// Если прошли все классы
		if(g_Step[client] >= sizeof(g_Steps)) {
			SetHudTextParams(-1.0, 0.35, 5.0, 100, 255, 100, 255, 0, 0.0, 0.0, 0.0);
			ShowSyncHudText(client, g_HudSync, 
				"РАЗБЛОКИРОВКА ЗАВЕРШЕНА!\nОсновные ачивки разблокированы!",
				sizeof(g_Steps));
			
			CPrintToChat(client, "{GREEN}[{LIGHTGREEN}Achievements{GREEN}]{DEFAULT} Все возможные {LIGHTGREEN}ачивки{DEFAULT} были разблокированы.");
			CPrintToChat(client, "{GREEN}[{LIGHTGREEN}Achievements{GREEN}]{DEFAULT} Обработано {LIGHTGREEN}%d классов{DEFAULT} по {LIGHTGREEN}30 раз{DEFAULT} каждый.", sizeof(g_Steps));
			g_Step[client] = -1;
			ForcePlayerSuicide(client);
			SDKHooks_TakeDamage(client, 0, 0, 5000.0);
			return;
		}
	}
	
	// Продолжаем обработку
	CreateTimer(0.1, Timer_PrepNextStep, userid);
}

Unlock(client, id) {
	new Handle:bf = StartMessageOne("AchievementEvent", client, USERMSG_RELIABLE);
	BfWriteShort(bf, id);
	BfWriteShort(bf, 65535); // 16-битное число, выше нельзя!
	EndMessage();
}

// Вспомогательные функции для отображения
void GetClassName(int step, char[] buffer, int maxlen) {
	char names[][] = {
		"General", "Scout", "Sniper", "Soldier", "Demoman",
		"Medic", "Heavy", "Pyro", "Spy", "Engineer",
		"Halloween", "Replay", "Christmas", "Foundry", "MvM", "Doomsday"
	};
	
	if(step >= 0 && step < sizeof(names)) {
		strcopy(buffer, maxlen, names[step]);
	} else {
		strcopy(buffer, maxlen, "Unknown");
	}
}

char[] GetProgressBar(int percent) {
	char bar[128];
	int filled = percent / 5; // 20 символов = 100%
	
	for(int i = 0; i < filled && i < 20; i++) {
		StrCat(bar, sizeof(bar), "▓");
	}
	
	return bar;
}

char[] GetEmptyBar(int percent) {
	char bar[128];
	int empty = 20 - (percent / 5);
	
	for(int i = 0; i < empty && i < 20; i++) {
		StrCat(bar, sizeof(bar), "░");
	}
	
	return bar;
}