// this script assigns bot roles as defined in the Map table
this.Name = "RoleManager";
this.Parent = "LowLevel";
this.Debug = false;
this.AlwaysRecieveEvents = true;

this.Initialize = function()
{
	this.justStarted = true;
};

this.FindFreeRoleSlot = function()
{
	// return if map doesn't have Roles table
	// skip bots that already have a role
	if (!Map.Roles || this.Bot.HasRole()) {
		return;
	}

	team = this.Bot.GetTeam();
	currentClass = this.Bot.GetClass();
	teamRoles = Map.Roles[Util.TeamName(team)];
	if (!teamRoles){ return; }

	botCount = 0;
	evalNum = -9999;
	newClass = null;
	foreach (id and role in teamRoles) {
		if (!IsTable(role) || !role.numbots) { continue; }

		if (role.MinBotsForRole) {
			if (Server.Team[team].NumBots < role.MinBotsForRole) {
				continue;
			}
		}

		if (!role.bots) { role.bots = table(); }

		//remove disconnected bots
		deleted = null;
		foreach (bot and val in role.bots) {
			if (!bot.Name) {
				if (!deleted){ deleted = table(); }
				deleted[tableCount(deleted)] = bot;
			}
		}
		if (deleted) {
			foreach (bot in deleted) {
				role.bots[bot] = null;
			}
		}

		currentBots = tableCount(role.bots);
		botCount += currentBots;
		numFree =  role.numbots - currentBots;
		if (numFree <= 0 && !role.AllBots) { continue; }
		eval = numFree * 100;

		// prioritize roles that have no players
		if (currentBots == 0) {
			eval += 10000;
		}
		
		crucialClass = role.crucialClass;
		if (IsTable(crucialClass)) {
			crucialClass = crucialClass[currentBots];
		} else if (currentBots > 0) {
			crucialClass = null;
		}
		// prioritize roles that have correct class
		if (crucialClass == currentClass) {
			eval += 50;
		}

		// sort by spawnpt
		if (IsInt(role.spawnpt)) { eval += role.spawnpt; }

		if (this.Debug) {
			print(Util.TeamName(team), id, "current:", currentBots, "free:", numFree, "eval:", eval);
		}

		// see which one needs a bot the most
		if (eval > evalNum) {
			evalNum = eval;
			newRole = id;
			newClass = crucialClass;
			spawnPoint = role.spawnpt;
			voiceMacro = role.voice;
		}
	}

	//team counter is used only immediately after map restart
	if (!teamRoles.teamCount){ teamRoles.teamCount = 0; }
	++teamRoles.teamCount;

	// store it in the table and set the role
	if (!IsNull(newRole)) {

		// every other bot for the team unless specified
		if (!teamRoles.AllBots) {
			teamCount = 0;
			foreach (bot in BotTable)
			{
				if (bot.GetTeam() == team)
					{ ++teamCount; }
			}
			if (teamCount > teamRoles.teamCount) {
				//do not count bots that have not been initialized yet
				teamCount = teamRoles.teamCount;
			}
			if (2 * botCount >= teamCount - 1) {
				return;
			}
		}

		if (this.Debug) { print(this.Bot.Name, "role set to", newRole); }
		teamRoles[newRole].bots[this.Bot] = true;
		this.Bot.SetRoles(ROLE[newRole]);

		// and change class if necessary
		if (newClass) {
			if (this.Debug) { print(this.Bot.Name, "crucial class set to", Util.ClassName(newClass)); }
			this.Bot.crucialClass = newClass;
			this.Bot.ChangeClass(newClass);
		}

		// spawnpoint too
		if (spawnPoint) {
			if (this.Debug) { print(this.Bot.Name, "spawn point set to", spawnPoint); }
			this.Bot.ChangeSpawnPoint(spawnPoint);
		}

		// signal a voice macro
		if (voiceMacro) {
			signal(format("%s_%s", this.Bot.Name, voiceMacro));
		}
	}
};

this.Events[EVENT.DISCONNECTED] = function()
{
	// wait a frame so the disconnecting bot is gone
	yield();
	// see if any bots without roles can take the spot
	// this is executed for all bots except the bot which has been disconnected
	if (this.FindFreeRoleSlot){ this.FindFreeRoleSlot(); }
};

this.Events[EVENT.CHANGETEAM] = function(team)
{
	if (!Map.Roles) {
		return;
	}
	if (this.justStarted) {
		this.justStarted = null;
	} else {
		// clear role in old team
		if (team==TEAM.AXIS){ team=TEAM.ALLIES; } else { team=TEAM.AXIS; }
		teamRoles = Map.Roles[Util.TeamName(team)];
		if (teamRoles) {
			bot = this.Bot;
			foreach (name and role in teamRoles) {
				if (IsTable(role) && role.bots && role.bots[bot]) {
					if (this.Debug){ print(bot.Name, "role", name, "cleared"); }
					role.bots[bot] = null;
					bot.ClearRoles(ROLE[name]);
					bot.crucialClass = null;
				}
			}
		}
	}
	// set role in new team
	this.FindFreeRoleSlot();
};
