#Include "TextLib" as TL
#Include "Libs/Nadeo/ShootMania/SM.Script.txt" as SM

#Const	Version		"2012-10-12"
#Const	ScriptName	"WarmUpSimple.Script.txt"

/* ------------------------------------- */
// Globales variables
/* ------------------------------------- */
declare CSmBlockSpawn[Integer] G_Clan1Spawns;	///< Spawn points for the clan 1
declare CSmBlockSpawn[Integer] G_Clan2Spawns;	///< Spawn points for the clan 2
declare Integer	G_Clan1SpawnsIndex;			///< Current index on the Clan1Spawns array
declare Integer	G_Clan2SpawnsIndex;			///< Current index on the Clan2Spawns array
declare Integer	G_WarmUpDuration;			///< Duration of the warm up
declare Integer	G_CountdownOnPlayersReady;	///< Countdown time when all players are ready
declare Boolean G_ForceWarmUpDuration;		///< Don't reset the warm up timer based on ready players
declare Integer G_MinimumPlayersNumber;		///< Minimum number of players in each clan
declare Text	G_BigMessage;				///< Big Message shown during the warm up
declare Text	G_StatusMessage;			///< Status Message shown during the warm up
declare Integer G_CurrentBarrier;			///< Ensure synchronisation between server and player UI
declare CUILayer[] G_CustomLayers;			///< Add custom layers to the warmup interface
declare Boolean	G_IsInitialized;			///< The options have been initialized

/* ------------------------------------- */
// Functions
/* ------------------------------------- */

/* ------------------------------------- */
/** Return the version number of the script
 *
 *	@return		The version number of the script
 */
Text GetScriptVersion() {
	return Version;
}

/* ------------------------------------- */
/** Return the name of the script
 *
 *	@return		The name of the script
 */
Text GetScriptName() {
	return ScriptName;
}

/* -------------------------------------- */
/** Initialize the warm up
 *  Take all the options and initialize them to a correct value
 *
 *  @param		_Duration		Time duration in seconds of the warm up
 *  @param		_Clan1Spawns	A list of spawns for clan 1 players or all players if the mode don't use clans
 *  @param		_Clan2Spawns	A list of spawns for clan 2 players
 */
Void Initialize(Integer _Duration, CSmBlockSpawn[] _Clan1Spawns, CSmBlockSpawn[] _Clan2Spawns) {
	G_WarmUpDuration		= _Duration;
	G_ForceWarmUpDuration	= False;
	G_BigMessage			= TL::Compose("$f90%1", _("Warm up"));
	G_StatusMessage			= _("Press F6 once you're ready.");
	G_Clan1SpawnsIndex		= 0;
	G_Clan2SpawnsIndex		= 0;
	G_CurrentBarrier		= Now;
	
	G_CountdownOnPlayersReady = 5;
	
	declare I = 0;
	foreach (Spawn in _Clan1Spawns) {
		if (Spawn == Null) continue;
		G_Clan1Spawns[I] = Spawn;
		I += 1;
	}
	I = 0;
	foreach (Spawn in _Clan2Spawns) {
		if (Spawn == Null) continue;
		G_Clan2Spawns[I] = Spawn;
		I += 1;
	}
	
	G_IsInitialized = True;
}

/* -------------------------------------- */
/** Initialize the warm up (overload)
 *
 *  @param		_Duration		Time duration in seconds of the warm up
 *  @param		_Clan1Spawn		The spawn for clan 1 players or all players if the mode don't use clans
 *  @param		_Clan2Spawn		The spawn for clan 2 players
 */
Void Initialize(Integer _Duration, CSmBlockSpawn _Clan1Spawn, CSmBlockSpawn _Clan2Spawn) {
	Initialize(_Duration, [_Clan1Spawn], [_Clan2Spawn]);
}

/* -------------------------------------- */
/** Initialize the warm up (overload)
 *
 *  @param		_Duration		Time duration in seconds of the warm up
 *  @param		_Clan1Spawns	A list of spawns for clan 1 players or all players if the mode don't use clans
 *  @param		_Clan2Spawn		The spawn for clan 2 players
 */
Void Initialize(Integer _Duration, CSmBlockSpawn[] _Clan1Spawns, CSmBlockSpawn _Clan2Spawn) {
	Initialize(_Duration, _Clan1Spawns, [_Clan2Spawn]);
}

/* -------------------------------------- */
/** Initialize the warm up (overload)
 *
 *  @param		_Duration		Time duration in seconds of the warm up
 *  @param		_Clan1Spawn		The spawn for clan 1 players or all players if the mode don't use clans
 *  @param		_Clan2Spawns	A list of spawns for clan 2 players
 */
Void Initialize(Integer _Duration, CSmBlockSpawn _Clan1Spawn, CSmBlockSpawn[] _Clan2Spawns) {
	Initialize(_Duration, [_Clan1Spawn], _Clan2Spawns);
}

/* -------------------------------------- */
/** Initialize the warm up (overload)
 *  Short version for mode not using clans
 *
 *  @param		_Duration		Time duration in seconds of the warm up
 *  @param		_Spawns			List of spawns for all players
 */
Void Initialize(Integer _Duration, CSmBlockSpawn[] _Spawns) {
	Initialize(_Duration, _Spawns, CSmBlockSpawn[]);
}

/* -------------------------------------- */
/** Initialize the warm up (overload)
 *  Short version for mode not using clans
 *
 *  @param		_Duration		Time duration in seconds of the warm up
 *  @param		_Spawn			The spawn for all players
 */
Void Initialize(Integer _Duration, CSmBlockSpawn _Spawn) {
	Initialize(_Duration, [_Spawn], CSmBlockSpawn[]);
}

/* -------------------------------------- */
/** Initialize the warm up (overload)
 *
 *  @param		_Duration		Time duration in seconds of the warm up
 */
Void Initialize(Integer _Duration) {
	Initialize(_Duration, CSmBlockSpawn[], CSmBlockSpawn[]);
}

/* -------------------------------------- */
/// Initialize the warm up (overload)
Void Initialize() {
	Initialize(90, CSmBlockSpawn[], CSmBlockSpawn[]);
}

/* -------------------------------------- */
/** Return the warm up UI manialink
 *  If the mode use clans then show the join team buttons
 *
 *  @return 	The warm up manialink
 */
Text GetWarmUpUI() {
	declare ML = "";
	declare JoinTeamButtons = "";
	
	// Show the join team buttons only if the mode uses clans
	if (UseClans) {
		JoinTeamButtons = """
<label posn="-42 0 4" halign="right" style="CardButtonMedium" text="<< Join (F3)" id="BtnJoinClan1" ScriptEvents="1" />
<label posn="43 0 4" halign="left" style="CardButtonMedium" text="Join (F4) >>" id="BtnJoinClan2" ScriptEvents="1" />
		""";
	}
	
	
	ML = """
<frame posn="0 -59.6 2">
	{{{ JoinTeamButtons }}}
	<frame posn="0 0 0">
		<label posn="0 0 4" halign="center" style="CardButtonMedium" text=" Ready (F6)" id="BtnReady" ScriptEvents="1" />
		<quad posn="-16 -1.3 5" sizen="5 5" style="Icons64x64_1" id="IconReady" substyle="LvlGreen" />
		<quad posn="-16 -1.3 5" sizen="5 5" style="Icons64x64_1" id="IconNotReady" substyle="LvlRed" />
	</frame>
</frame>
<script><!--
	main () {
		declare netread Net_Barrier for UI = 0;
		declare netwrite Net_BarrierReached for UI = 0;
		declare netwrite Net_ReadyToPlay for UI = False;
		declare IconReady <=> Page.GetFirstChild("IconReady");
		declare IconNotReady <=> Page.GetFirstChild("IconNotReady");
		
		Net_ReadyToPlay = False;
		
		while(True) {
			Net_BarrierReached = Net_Barrier;
			
			// update UI
			if (Net_ReadyToPlay) {
				IconReady.Show();
				IconNotReady.Hide();
			} else {
				IconReady.Hide();
				IconNotReady.Show();
			}

			yield;

			// process events.
			foreach(Event in PendingEvents)	{
				switch(Event.Type){
					case CMlEvent::Type::MouseClick :
					{		
						if (Event.ControlId == "BtnReady")		Net_ReadyToPlay = !Net_ReadyToPlay;
						if (Event.ControlId == "BtnJoinClan1")	JoinTeam1();
						if (Event.ControlId == "BtnJoinClan2")	JoinTeam2();
					}
			
					case CMlEvent::Type::KeyPress:
					{
						if (Event.CharPressed == "2752512") Net_ReadyToPlay = !Net_ReadyToPlay;	// F6
						if (Event.CharPressed == "2555904") JoinTeam1();	// F3
						if (Event.CharPressed == "2621440") JoinTeam2();	// F4
					}
				}
			}
		}
	}
--></script>
	""";
	
	return ML;
}

/* -------------------------------------- */
/** Force the warm up to last a fixed time
 *
 *  @param		_Forced		Set the forced time warm up option to true or false
 */
Void SetForceWarmUpDuration(Boolean _Forced) {
	G_ForceWarmUpDuration = _Forced;
}

/* -------------------------------------- */
/** Require a minimum number of players before the warm up can end
 *
 *  @param		_MinPlayers		The minimum number of players required to begin
 */
Void SetMinimumPlayersNumber(Integer _MinPlayers) {
	G_MinimumPlayersNumber = _MinPlayers;
}

/* -------------------------------------- */
/** Change the warm up BigMessage 
 *
 *	@param		_NewBigMessage		The BigMessage to use
 */
Void SetBigMessage(Text _NewBigMessage) {
	G_BigMessage = _NewBigMessage;
}

/* -------------------------------------- */
/** Change the warm up StatusMessage
 *
 *	@param		_NewStatusMessage	The StatusMessage to use
 */
Void SetStatusMessage(Text _NewStatusMessage) {
	G_StatusMessage = _NewStatusMessage;
}

/**
 * Set the duration of the timer when all players are ready.
 */
Void SetCountdownOnPlayersReady(Integer _TimeInSeconds) {
	if(_TimeInSeconds > 0) G_CountdownOnPlayersReady = _TimeInSeconds;
}

/* -------------------------------------- */
/** Test if the players are ready to begin
 *
 *	@param		_RequireAllReady	If true all the players must be ready, else only one player is enough (in each clan)
 *
 *	@return		True if the players are ready, false otherwise
 */
Boolean PlayersAreReady(Boolean _RequireAllReady) {
	declare NbClan1Ready = 0;		///< Number of clan 1 ready players
	declare NbClan2Ready = 0;		///< number of clan 2 ready players
	declare NbTotalReady = 0;		///< Total number of ready players
	declare AllPlayersReady = True;	///< All players are ready
	declare PlayersReady = False;	///< The final return value
	
	foreach(Player in Players) {
		declare IsPlayerReady = False;
		
		/// Bots are ready by default
		if (Player.IsFakePlayer) {
			IsPlayerReady = True;
		} 
		/// Check if player is ready
		else {
			declare UI <=> UIManager.GetUI(Player);
			if (UI != Null) {
				declare netwrite Net_Barrier for UI = 0;
				declare netread Net_BarrierReached for UI = 0;
				declare netread Net_ReadyToPlay for UI = False;
				
				Net_Barrier = G_CurrentBarrier;
				if (Net_BarrierReached == Net_Barrier && Net_ReadyToPlay) {
					IsPlayerReady = True;
				}
			}
		}

		if (!IsPlayerReady) {
			AllPlayersReady = False;
			continue;
		}
		
		NbTotalReady += 1;
		if (Player.CurrentClan == 1) {
			NbClan1Ready += 1;
		} else if (Player.CurrentClan == 2) {
			NbClan2Ready += 1;
		}
	}
	
	// We use clans and at least one player is ready in each team
	if (!_RequireAllReady && UseClans) {
		PlayersReady = (NbClan1Ready >= 1 && ClansNbPlayers[1] >= G_MinimumPlayersNumber) 
						&& (NbClan2Ready >= 1 && ClansNbPlayers[2] >= G_MinimumPlayersNumber);
	} 
	// We don't use clans and at least one player is ready
	else if (!_RequireAllReady && !UseClans) {
		PlayersReady = NbTotalReady >= 1 && PlayersNbTotal >= G_MinimumPlayersNumber;
	} 
	// We use clans and all players are ready
	else if (_RequireAllReady && UseClans) {
		PlayersReady = (NbClan1Ready >= 1 && ClansNbPlayers[1] >= G_MinimumPlayersNumber) 
					&& (NbClan2Ready >= 1 && ClansNbPlayers[2] >= G_MinimumPlayersNumber) 
					&& AllPlayersReady;
	} 
	// We don't use clan and all players are ready
	else {
		PlayersReady = NbTotalReady >= 1 && PlayersNbTotal >= G_MinimumPlayersNumber && AllPlayersReady;
	}
			
	return PlayersReady;
}

/* -------------------------------------- */
/** Test if all players are ready to begin
 *	Short version of PlayersAreReady(True)
 *
 *	@return		True if all players are ready, false otherwise
 */
Boolean AllPlayersAreReady() {
	return PlayersAreReady(True);
}

/** Test if one player is ready (in each clan if we use clans)
 *	Short version of  PlayersAreReady(False)
 *
 *	@return		True if one player is ready, false otherwise
 */
Boolean MinimumPlayersAreReady() {
	return PlayersAreReady(False);
}

/* -------------------------------------- */
/** Get the right BlockSpawn where to spawn the player
 *
 *	@param		_RequestedClan		The clan the player requested
 *
 *	@return		The BlockSpawn where to spawn the player
 */
CSmBlockSpawn GetSpawn(Integer _RequestedClan) {
	declare CSmBlockSpawn BlockSpawn;
	
	// If player is in clan 1 or if we don't use clans
	if (_RequestedClan <= 1 || !UseClans) {
		if (G_Clan1Spawns.count >= 1) {
			if (!G_Clan1Spawns.existskey(G_Clan1SpawnsIndex)) G_Clan1SpawnsIndex = 0;
			BlockSpawn = G_Clan1Spawns[G_Clan1SpawnsIndex];
			G_Clan1SpawnsIndex += 1;
		} else {
			foreach(Spawn in BlockSpawns) {
				BlockSpawn = Spawn;
				break;
			}
		}		
	} 
	// If player is in clan 2
	else if (_RequestedClan >= 2) {
		if (G_Clan2Spawns.count >= 1) {
			if (!G_Clan2Spawns.existskey(G_Clan2SpawnsIndex)) G_Clan2SpawnsIndex = 0;
			BlockSpawn = G_Clan2Spawns[G_Clan2SpawnsIndex];
			G_Clan2SpawnsIndex += 1;
		} else {
			foreach(Spawn in BlockSpawns) {
				BlockSpawn = Spawn;
				break;
			}
		}	
	}
	
	return BlockSpawn;
}

/* -------------------------------------- */
/// Start the warm up
Void Start() {
	declare WarmUpLayers = Ident[Ident];
	declare MinReadyTime = -1;
	declare ForcedTime = Now + G_WarmUpDuration * 1000;
	
	SM::UnspawnAllPlayers();
	
	// Initialize UI
	UIManager.UIAll.UISequence = CUIConfig::EUISequence::Playing;
	declare OldBigMessage = UIManager.UIAll.BigMessage;
	declare OldStatusMessage = UIManager.UIAll.StatusMessage;
	UIManager.UIAll.BigMessage = G_BigMessage;
	UIManager.UIAll.StatusMessage = G_StatusMessage;
	G_CurrentBarrier += 1;
	
	// Initialize timer
	declare OldStartTime = StartTime;
	declare OldEndTime = EndTime;
	StartTime = Now;
	EndTime = -1;
	
	while ((EndTime == -1 || EndTime >= Now) && !MatchEndRequested && !ServerShutdownRequested) {
		yield;
		
		SM::UnspawnPlayersChangingClan();
		
		// Managing events
		foreach (Event in PendingEvents) {
			if (Event.Type == CSmModeEvent::EType::OnHit) {
				if (Event.Victim != Null && Event.Shooter != Null && Event.Victim != Event.Shooter) {
					Event.Damage = 100;
					Event.Victim.Armor = Event.Victim.ArmorMax;
					Event.ShooterPoints = 1;
					PassOn(Event);
				} else {
					Discard(Event);
				}
			} else if (Event.Type == CSmModeEvent::EType::OnArmorEmpty) {
				if (Event.Shooter != Null && Event.Victim != Null) {
					Event.Victim.Armor = Event.Victim.ArmorMax;
					Discard(Event);
				} else {
					PassOn(Event);
				}
			} else {
				PassOn(Event);
			}
		}
		
		// Spawn players and set UI
		foreach (Player in Players) {
			// Spawn
			if (Player.SpawnStatus == CSmPlayer::ESpawnStatus::NotSpawned) {
				declare Spawn <=> GetSpawn(Player.RequestedClan);
				if (Spawn != Null) {
					if (UseClans) SM::SpawnPlayer(Player, Player.RequestedClan, Player.ArmorMax, Spawn, -1);
					else SM::SpawnPlayer(Player, 0, Player.ArmorMax, Spawn, -1);
				}
			}
			// UI
			declare UI <=> UIManager.GetUI(Player);
			if (UI == Null) continue;
			if (!WarmUpLayers.existskey(Player.Id)) {
				declare WarmUpLayer <=> UIManager.UILayerCreate();
				WarmUpLayer.ManialinkPage = GetWarmUpUI();
				UI.UILayers.add(WarmUpLayer);
				WarmUpLayers[Player.Id] = WarmUpLayer.Id;
			}
		}
		
		declare LayersToRemove = Ident[];
		foreach (PlayerId => LayerId in WarmUpLayers) {
			if (!Players.existskey(PlayerId)) LayersToRemove.add(LayerId);
		}
		foreach (LayerId in LayersToRemove) {
			if (UIManager.UILayers.existskey(LayerId)) UIManager.UILayerDestroy(UIManager.UILayers[LayerId]);
			WarmUpLayers.remove(LayerId);
		}
		
		// Warm up ending conditions	
		if (AllPlayersAreReady()) {
			if (EndTime == -1 || EndTime > Now + (G_CountdownOnPlayersReady*1000)) EndTime = Now + (G_CountdownOnPlayersReady*1000);
		} else if (MinimumPlayersAreReady()) {
			if (MinReadyTime == -1) MinReadyTime = Now + (G_WarmUpDuration * 1000);			
			EndTime = MinReadyTime;
		} else {
			if (!G_ForceWarmUpDuration) {
				EndTime = -1;
				MinReadyTime = -1;
			} else {
				EndTime = ForcedTime;
				MinReadyTime = ForcedTime;
			}
		}
	}
	
	// Delete the warm up layers
	foreach (PlayerId => LayerId in WarmUpLayers) {
		if (UIManager.UILayers.existskey(LayerId)) UIManager.UILayerDestroy(UIManager.UILayers[LayerId]);
	}
	
	// Restore UI
	UIManager.UIAll.UISequence = CUIConfig::EUISequence::EndRound;
	UIManager.UIAll.BigMessage = OldBigMessage;
	UIManager.UIAll.StatusMessage = OldStatusMessage;
	
	SM::UnspawnAllPlayers();
	sleep(2000);
	
	// Restore timer
	StartTime = OldStartTime;
	EndTime = OldEndTime;
}