/** 
 *	TM library
 */

#Const Version		"2013-12-16"
#Const ScriptName	"TM2.Script.txt"

#Include "TextLib" as TL

// ---------------------------------- //
// Constant
// ---------------------------------- //
#Const C_SpawnDuration			3000	///< Time before respawn (3,2,1,Go!)
#Const C_OutroDuration			8000	///< Outro sequence duration
#Const C_OutroScoresTableTime	3000	///< Time before the display of the scores table in the outro sequence

#Const C_SpawnStatus_Racing		1	///< The player is currently racing
#Const C_SpawnStatus_Waiting	2	///< The player is winting to be spawned
#Const C_SpawnStatus_Outro		3	///< The player is watching the outro sequence

// ---------------------------------- //
// Functions
// ---------------------------------- //

// ---------------------------------- //
// Private
// ---------------------------------- //
// ---------------------------------- //
/** Start the outro sequence for a player
 *
 *	@param	_Player		The player to set in outro sequence
 */
Void Private_OutroStart(CTmPlayer _Player) {
	if (_Player == Null) return;
	
	declare LibTM2_OutroEndTime for _Player = -1;
	declare LibTM2_OutroScoresTableTime for _Player = -1;
	LibTM2_OutroEndTime = Now + C_OutroDuration;
	LibTM2_OutroScoresTableTime = Now + C_OutroScoresTableTime;
}

// ---------------------------------- //
/** Display the scores table for a player during the outro sequence
 *
 *	@param	_Player		The player who will see the scores table
 */
Void Private_OutroScoresTable(CTmPlayer _Player) {
	if (_Player == Null) return;
	
	declare LibTM2_OutroScoresTableTime for _Player = -1;
	LibTM2_OutroScoresTableTime = -1;
	
	declare UI <=> UIManager.GetUI(_Player);
	if (UI != Null) {
		UI.ScoreTableVisibility = CUIConfig::EVisibility::ForcedVisible;
	}
}

// ---------------------------------- //
/** Stop the outro sequence for a player
 *
 *	@param	_Player		The player to get out of the outro sequence
 */
Void Private_OutroStop(CTmPlayer _Player) {
	if (_Player == Null) return;
	
	declare LibTM2_OutroEndTime for _Player = -1;
	LibTM2_OutroEndTime = -1;
	
	declare UI <=> UIManager.GetUI(_Player);
	if (UI != Null) {
		UI.ScoreTableVisibility = CUIConfig::EVisibility::None;
	}
}

// ---------------------------------- //
// Public
// ---------------------------------- //

// ---------------------------------- //
/** 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;
}

// ---------------------------------- //
/// Unload the library
Void Unload() {
	foreach (Player in ConnectedPlayers) {
		declare LibTM2_SpawnStatus for Player = C_SpawnStatus_Waiting;
		declare LibTM2_OutroEndTime for Player = -1;
		declare LibTM2_OutroScoresTableTime for Player = -1;
		LibTM2_SpawnStatus = C_SpawnStatus_Waiting;
		LibTM2_OutroEndTime = -1;
		LibTM2_OutroScoresTableTime = -1;
	}
}

// ---------------------------------- //
/// Load the library
Void Load() {
	Unload();
}

// ---------------------------------- //
/** Spawn a player for a race
 *	If this player was already spawned, he will be respawned
 *
 *	@param	_Player				The player to spawn
 *	@param	_StartTime			Server time of the beginning of the race
 *	@param	_RegisterOnLadder	Try to register the player on the ladder if he wasn't
 */
Void StartRace(CTmPlayer _Player, Integer _StartTime, Boolean _RegisterOnLadder) {
	if (_Player == Null) return;
	
	declare LibTM2_SpawnStatus for _Player = C_SpawnStatus_Waiting;
	
	_Player.IsSpawned = True;
	_Player.RaceStartTime = _StartTime;
	LibTM2_SpawnStatus = C_SpawnStatus_Racing;
	
	if(_RegisterOnLadder && _Player.Score != Null && !_Player.Score.IsRegisteredForLadderMatch) {
		Ladder_AddPlayer(_Player.Score);
	}
}

// ---------------------------------- //
/** Spawn a player for a race
 *	If this player was already spawned, he will be respawned
 *
 *	@param	_Player			The player to spawn
 *	@param	_StartTime		Server time of the beginning of the race
 */
Void StartRace(CTmPlayer _Player, Integer _StartTime) {
	StartRace(_Player, _StartTime, True);
}

// ---------------------------------- //
/** Spawn a player for a race
 *	If this player was already spawned, he will be respawned
 *
 *	@param	_Player		The player to spawn
 */
Void StartRace(CTmPlayer _Player) {
	StartRace(_Player, Now + C_SpawnDuration, True);
}

// ---------------------------------- //
/** Unspawn a racing player and send him in the outro sequence
 *
 *	@param	_Player		The player to unspawn
 */
Void EndRace(CTmPlayer _Player) {
	if (_Player == Null) return;
	
	declare LibTM2_SpawnStatus for _Player = C_SpawnStatus_Waiting;
	LibTM2_SpawnStatus = C_SpawnStatus_Outro;
	
	Private_OutroStart(_Player);
}

// ---------------------------------- //
/** Unspawn a racing player and skip the outro sequence
 *
 *	@param	_Player		The player to unspawn
 */
Void WaitRace(CTmPlayer _Player) {
	if (_Player == Null) return;
	
	declare LibTM2_SpawnStatus for _Player = C_SpawnStatus_Waiting;
	
	declare UI <=> UIManager.GetUI(_Player);
	if (UI != Null) UI.UISequence = CUIConfig::EUISequence::None;
	if (LibTM2_SpawnStatus == C_SpawnStatus_Outro) Private_OutroStop(_Player);
	
	_Player.IsSpawned = False;
	_Player.RaceStartTime = -1;
	LibTM2_SpawnStatus = C_SpawnStatus_Waiting;
}

// ---------------------------------- //
/// Unspawn all the players
Void WaitRaceAll() {
	foreach (Player in ConnectedPlayers) {
		WaitRace(Player);
	}
}

// ---------------------------------- //
/** Check if a player is racing
 *
 *	@param	_Player		The player to check
 *
 *	@return		True if the player is racing, false otherwise
 */
Boolean IsRacing(CTmPlayer _Player) {
	if (_Player == Null) return False;
	
	declare LibTM2_SpawnStatus for _Player = C_SpawnStatus_Waiting;
	
	if (LibTM2_SpawnStatus == C_SpawnStatus_Racing) return True;
	return False;
}

// ---------------------------------- //
/** Check if a player is waiting to be spawned
 *
 *	@param	_Player		The player to check
 *
 *	@return		True if the player is waiting to be spawned, false otherwise
 */
Boolean IsWaiting(CTmPlayer _Player) {
	if (_Player == Null) return False;
	
	declare LibTM2_SpawnStatus for _Player = C_SpawnStatus_Waiting;
	
	if (LibTM2_SpawnStatus == C_SpawnStatus_Waiting) return True;
	return False;
}

// ---------------------------------- //
/** Check if a player is watching the outro sequence
 *
 *	@param	_Player		The player to check
 *
 *	@return		True if the player is watching the outro, false otherwise
 */
Boolean IsWatchingOutro(CTmPlayer _Player) {
	if (_Player == Null) return False;
	
	declare LibTM2_SpawnStatus for _Player = C_SpawnStatus_Waiting;
	
	if (LibTM2_SpawnStatus == C_SpawnStatus_Outro) return True;
	return False;
}

// ---------------------------------- //
/** Get the current spawn status of a player
 *
 *	@param	_Player		The player to check
 *
 *	@return		The current status of the player or -1 if the player doesn't exist
 */
Integer GetPlayerStatus(CTmPlayer _Player) {
	if (_Player == Null) return -1;
	
	declare LibTM2_SpawnStatus for _Player = C_SpawnStatus_Waiting;
	return LibTM2_SpawnStatus;
}

// ---------------------------------- //
/// Update the library
Void Loop() {
	foreach (Player in ConnectedPlayers) {
		declare LibTM2_SpawnStatus for Player = C_SpawnStatus_Waiting;
		
		// ---------------------------------- //
		// Sequence outro
		if (LibTM2_SpawnStatus == C_SpawnStatus_Outro) {
			declare LibTM2_OutroEndTime for Player = -1;
			declare LibTM2_OutroScoresTableTime for Player = -1;
			
			if (LibTM2_OutroScoresTableTime > 0 && Now >= LibTM2_OutroScoresTableTime) {
				Private_OutroScoresTable(Player);
			}
			if (Now >= LibTM2_OutroEndTime) {
				WaitRace(Player);
			}
		}
		// ---------------------------------- //
		// Others
		else if (LibTM2_SpawnStatus != C_SpawnStatus_Waiting) {
			if (Player.RequestsSpectate || Player.RaceStartTime <= 0) {
				WaitRace(Player);
			}
		} else if (LibTM2_SpawnStatus == C_SpawnStatus_Waiting) {
			if (Player.RaceStartTime > 0) WaitRace(Player);
		}
	}
}

// ---------------------------------- //
/**	Get a player from its login
 *
 *	@param	_Login		Login of the player to get
 *	
 *	@return				The player if found, Null otherwise
 */
CTmPlayer GetPlayer(Text _Login) {
	foreach (Player in AllPlayers) {
		if (Player.Login == _Login) return Player;
	}
	
	return Null;
}

// ---------------------------------- //
/**	Convert a time (Integer) to a Text
 *
 *	@param	_Time		The time to convert
 *	
 *	@return				The time converted in Text
 */
Text TimeToText(Integer _Time) {
	if (_Time < 0) {
		return "???";
	}
	
	declare MilliSeconds = _Time % 1000;
	declare Seconds = (_Time / 1000) % 60;
	declare Minutes = (_Time / 60000) % 60;
	declare Hours = (_Time / 3600000) % 24;
	
	declare Time = TL::FormatInteger(Minutes, 2)^":"^TL::FormatInteger(Seconds, 2)^"."^TL::FormatInteger(MilliSeconds, 3);
	if (Hours > 0) Time = Hours^":"^Time;
	return Time;
}

// ---------------------------------- //
/**	Convert a Text to a time (Integer)
 *
 *	@param	_Value		The Text to convert
 *	
 *	@return				The Text converted in time
 */
Integer TextToTime(Text _Value) {
	declare Time = 0;
	declare Split = TL::Split(":", _Value);
	
	// mm:ss.xxx
	if (Split.count == 2) { 
		Time += TL::ToInteger(Split[0]) * 60000;
		
		// ss.xxx
		declare Split2 = TL::Split(".", Split[1]);
		if (Split2.count > 1) {
			Time += TL::ToInteger(Split2[0]) * 1000;
			Time += TL::ToInteger(Split2[1]);
		} else {
			Time += TL::ToInteger(Split2[0]) * 1000;
		}
	} 
	// h:mm:ss.xxx
	else if (Split.count == 3) { 
		Time += TL::ToInteger(Split[0]) * 3600000;
		Time += TL::ToInteger(Split[1]) * 60000;
		
		// ss.xxx
		declare Split2 = TL::Split(".", Split[2]);
		if (Split2.count > 1) {
			Time += TL::ToInteger(Split2[0]) * 1000;
			Time += TL::ToInteger(Split2[1]);
		} else {
			Time += TL::ToInteger(Split2[0]) * 1000;
		}
	} 
	// ss.xxx
	else {
		declare Split2 = TL::Split(".", Split[0]);
		if (Split2.count > 1) {
			Time += TL::ToInteger(Split2[0]) * 1000;
			Time += TL::ToInteger(Split2[1]);
		} else {
			Time += TL::ToInteger(Split2[0]) * 1000;
		}
	}
	
	if (Time < 0) Time = 0;
	return Time;
}

// ---------------------------------- //
/**	Inject TimeToText into a ManiaLink
 *	
 *	@return		TimeToText() function
 */
Text InjectMLTimeToText() {
	return """
Text TimeToText(Integer _Time) {
	if (_Time < 0) {
		return "???";
	}
	
	declare MilliSeconds = _Time % 1000;
	declare Seconds = (_Time / 1000) % 60;
	declare Minutes = (_Time / 60000) % 60;
	declare Hours = (_Time / 3600000) % 24;
	
	declare Time = TL::FormatInteger(Minutes, 2)^":"^TL::FormatInteger(Seconds, 2)^"."^TL::FormatInteger(MilliSeconds, 3);
	if (Hours > 0) Time = Hours^":"^Time;
	return Time;
}""";
}

// ---------------------------------- //
/**	Inject TextToTime into a ManiaLink
 *	
 *	@return			TextToTime() function
 */
Text InjectMLTextToTime() {
	return """
Integer TextToTime(Text _Value) {
	declare Time = 0;
	declare Split = TL::Split(":", _Value);
	
	// mm:ss.xxx
	if (Split.count == 2) { 
		Time += TL::ToInteger(Split[0]) * 60000;
		
		// ss.xxx
		declare Split2 = TL::Split(".", Split[1]);
		if (Split2.count > 1) {
			Time += TL::ToInteger(Split2[0]) * 1000;
			Time += TL::ToInteger(Split2[1]);
		} else {
			Time += TL::ToInteger(Split2[0]) * 1000;
		}
	} 
	// h:mm:ss.xxx
	else if (Split.count == 3) { 
		Time += TL::ToInteger(Split[0]) * 3600000;
		Time += TL::ToInteger(Split[1]) * 60000;
		
		// ss.xxx
		declare Split2 = TL::Split(".", Split[2]);
		if (Split2.count > 1) {
			Time += TL::ToInteger(Split2[0]) * 1000;
			Time += TL::ToInteger(Split2[1]);
		} else {
			Time += TL::ToInteger(Split2[0]) * 1000;
		}
	} 
	// ss.xxx
	else {
		declare Split2 = TL::Split(".", Split[0]);
		if (Split2.count > 1) {
			Time += TL::ToInteger(Split2[0]) * 1000;
			Time += TL::ToInteger(Split2[1]);
		} else {
			Time += TL::ToInteger(Split2[0]) * 1000;
		}
	}
	
	if (Time < 0) Time = 0;
	return Time;
}""";
}