/**
 * Interface library
 */
#Const Version		"2013-05-28"
#Const ScriptName	"Interface.Script.txt"

// ---------------------------------- //
// Constantes
// ---------------------------------- //
#Const C_TeamNamesManialink """
<script><!--
	main() {
		declare Label_Team1Name <=> (Page.GetFirstChild("team1name") as CMlLabel);
		declare Label_Team2Name <=> (Page.GetFirstChild("team2name") as CMlLabel);
		while (True) {
			yield;
			if (!PageIsVisible) continue;
			
			Label_Team1Name.SetText(Teams[0].ColorizedName);
			Label_Team2Name.SetText(Teams[1].ColorizedName);
		}
	}
--></script>
<frame>
	<quad posn="-159.75 68" sizen="39.45 3.5" halign="left" valign="center" bgcolor="FFF8" />
	<quad posn="-159.5 70.9 1" sizen="10.8 10.5" halign="left" valign="center" style="Emblems" substyle="#1" /> 
	<label id="team1name" posn="-148.5 68.4 1" sizen="39.5 3.5" halign="left" valign="center" textsize="2.3" textprefix="$s" text="Blue" />
</frame>
<frame>
	<quad posn="159.75 68" sizen="39.45 3.5" halign="right" valign="center" bgcolor="FFF8" />
	<quad posn="159.5 70.9 1" sizen="10.8 10.5" halign="right" valign="center" style="Emblems" substyle="#2" /> 
	<label id="team2name" posn="148.5 68.4 1" sizen="39.5 3.5" halign="right" valign="center" textsize="2.3" textprefix="$s" text="Red" />
</frame>
"""

#Const C_LibUI_VisibilityAll		1
#Const C_LibUI_VisibilityPlayers	2
#Const C_LibUI_VisibilitySpectators	3

#Const C_LibUI_ImgSrc				"file://Media/Manialinks/Shootmania/Common/"
#Const C_LibUI_SlidePause			5000	///< Time spent on each slide
#Const C_LibUI_SlideType_Top		1
#Const C_LibUI_SlideType_Text		2
#Const C_LibUI_SlideType_Img		3
#Const C_LibUI_SlideBufferDuration	1000

// ---------------------------------- //
// Globales
// ---------------------------------- //
declare Ident G_LibUI_LayerPlayersListsId;	///< Id of the players lists layer
declare Ident G_LibUI_LayerRankId;			///< Id of the rank layer
declare Ident G_LibUI_LayerSliderId;		///< Id the slider layer
declare Boolean G_LibUI_SliderIsVisible;	///< Save the slider visibility
declare Integer[Integer] G_LibUI_SlideType;	///< Type of each slide in the slider
declare Text[Integer] G_LibUI_SlideTitle;	///< Title of each slide in tyhe slider
declare Text[Integer] G_LibUI_SlideContent;	///< Content of each slide of the slider

// ---------------------------------- //
// Funtions
// ---------------------------------- //

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

// ---------------------------------- //
Void SetLayerTeams(CUILayer _LayerTeams) {
	_LayerTeams.ManialinkPage = 
	C_TeamNamesManialink^"""
<playerlist posn="-160 66" halign="left"  style="" substyle="Small" lines="10" columns="1" team="1" status="Playing"/>
<playerlist posn=" 160 66" halign="right" style="" substyle="Small" lines="10" columns="1" team="2" status="Playing"/>
""";
}

// ---------------------------------- //
Void SetLayerTeamsForSpectators(CUILayer _LayerTeams) {
	_LayerTeams.ManialinkPage = 
	C_TeamNamesManialink^"""
<playerlist posn="-160 66" halign="left"  style="" substyle="Medium" lines="5" columns="1" team="1" status="Playing"/>
<playerlist posn=" 160 66" halign="right" style="" substyle="Medium" lines="5" columns="1" team="2" status="Playing"/>
""";
}

// ---------------------------------- //
/** Create the PlayersLists manialink
 *
 *	@return		The manialink string
 */
Text Private_CreateLayerPlayersLists(Text _Style, Real _Scale, Integer _Lines, Integer _Columns) {
return """
<frame id="Frame_PlayersLists">
	<frame posn="-160 68">	
		<quad posn="0.25 0" sizen="39.45 3.5" halign="left" valign="center" bgcolor="fff8" />
		<quad posn="0.5 2.9 1" sizen="10.8 10.5" halign="left" valign="center" style="Emblems" substyle="#1" /> 
		<label posn="12.5 0.4 1" sizen="39.5 3.5" halign="left" valign="center" textsize="2.3" textemboss="1" text="Blue" id="Label_Team1Name" />
		<playerlist posn="0 -2" scale="{{{_Scale}}}" halign="left" substyle="{{{_Style}}}" lines="{{{_Lines}}}" columns="{{{_Columns}}}" team="1" status="Playing"/>
	</frame>
	<frame posn="160 68">
		<quad posn="-0.25 0" sizen="39.45 3.5" halign="right" valign="center" bgcolor="fff8" />
		<quad posn="-0.5 2.9 1" sizen="10.8 10.5" halign="right" valign="center" style="Emblems" substyle="#2" /> 
		<label posn="-12.5 0.4 1" sizen="39.5 3.5" halign="right" valign="center" textsize="2.3" textemboss="1" text="Red" id="Label_Team2Name" />
		<playerlist posn="0 -2" scale="{{{_Scale}}}" halign="right" substyle="{{{_Style}}}" lines="{{{_Lines}}}" columns="{{{_Columns}}}" team="2" status="Playing"/>
	</frame>
</frame>
<script><!--
#Const C_LibUI_VisibilityAll		{{{C_LibUI_VisibilityAll}}}
#Const C_LibUI_VisibilityPlayers	{{{C_LibUI_VisibilityPlayers}}}
#Const C_LibUI_VisibilitySpectators	{{{C_LibUI_VisibilitySpectators}}}

declare CMlFrame Frame_PlayersLists;

Void UpdateVisibility() {
	declare netread Integer Net_LibUI_PlayersListVisibility for Teams[0];
	
	switch (Net_LibUI_PlayersListVisibility) {
		case C_LibUI_VisibilityAll: {
			Frame_PlayersLists.Show();
		}
		case C_LibUI_VisibilityPlayers: {
			if (IsSpectatorMode) Frame_PlayersLists.Hide();
			else Frame_PlayersLists.Show();
		}
		case C_LibUI_VisibilitySpectators: {
			if (IsSpectatorMode) Frame_PlayersLists.Show();
			else Frame_PlayersLists.Hide();
		}
	}
}

main() {
	Frame_PlayersLists <=> (Page.GetFirstChild("Frame_PlayersLists") as CMlFrame);
	declare Label_Team1Name <=> (Frame_PlayersLists.GetFirstChild("Label_Team1Name") as CMlLabel);
	declare Label_Team2Name <=> (Frame_PlayersLists.GetFirstChild("Label_Team2Name") as CMlLabel);
	
	declare netread Integer Net_LibUI_PlayersListVisibility for Teams[0];
	
	declare PrevTeam1Name = "";
	declare PrevTeam2Name = "";
	declare PrevVisibility = 0;
	declare PrevIsSpectator = False;
	
	UpdateVisibility();
	
	while (True) {
		sleep(200);
		
		if (InputPlayer == Null) continue;
		if (!PageIsVisible) continue;
		
		if (PrevTeam1Name != Teams[0].ColorizedName) {
			PrevTeam1Name = Teams[0].ColorizedName;
			Label_Team1Name.SetText(Teams[0].ColorizedName);
		}
		if (PrevTeam2Name != Teams[1].ColorizedName) {
			PrevTeam2Name = Teams[1].ColorizedName;
			Label_Team2Name.SetText(Teams[1].ColorizedName);
		}
		
		if (PrevVisibility != Net_LibUI_PlayersListVisibility) {
			PrevVisibility = Net_LibUI_PlayersListVisibility;
			UpdateVisibility();
		}
		
		if (PrevIsSpectator != IsSpectatorMode) {
			PrevIsSpectator = IsSpectatorMode;
			UpdateVisibility();
		}
	}
}
--></script>
""";
}

// ---------------------------------- //
/// Destroy the PlayersLists UI
Void DestroyPlayersLists() {
	if (G_LibUI_LayerPlayersListsId != NullId && UIManager.UILayers.existskey(G_LibUI_LayerPlayersListsId)) {
		UIManager.UILayerDestroy(UIManager.UILayers[G_LibUI_LayerPlayersListsId]);
		G_LibUI_LayerPlayersListsId = NullId;
	}
}

// ---------------------------------- //
/// Create the PlayersLists UI
Void CreatePlayersLists(Text _Style, Real _Scale, Integer _Lines, Integer _Columns) {
	// Check if a PlayersLists layer was already created and destroy it if it's the case
	DestroyPlayersLists();
	
	// Create and assign the layer
	declare LayerPlayersLists <=> UIManager.UILayerCreate();
	LayerPlayersLists.ManialinkPage = Private_CreateLayerPlayersLists(_Style, _Scale, _Lines, _Columns);
	G_LibUI_LayerPlayersListsId = LayerPlayersLists.Id;
	UIManager.UIAll.UILayers.add(LayerPlayersLists);
	
	// Ste the default visibility
	declare netwrite Integer Net_LibUI_PlayersListVisibility for Teams[0];
	Net_LibUI_PlayersListVisibility = C_LibUI_VisibilityAll;
}

// ---------------------------------- //
/// Attach the PlayersLists layer to all players
Void AttachPlayersLists() {
	if (UIManager.UIAll.UILayers.existskey(G_LibUI_LayerPlayersListsId)) return;
	if (!UIManager.UILayers.existskey(G_LibUI_LayerPlayersListsId)) return;
	
	UIManager.UIAll.UILayers.add(UIManager.UILayers[G_LibUI_LayerPlayersListsId]);
}

// ---------------------------------- //
/// Detach the PlayersLists layer from all players
Void DetachPlayersLists() {
	if (!UIManager.UIAll.UILayers.existskey(G_LibUI_LayerPlayersListsId)) return;
	if (!UIManager.UILayers.existskey(G_LibUI_LayerPlayersListsId)) return;
	
	declare Detached = UIManager.UIAll.UILayers.removekey(G_LibUI_LayerPlayersListsId);
}

// ---------------------------------- //
/** Set the players lists visibility
 *
 *	@param	_Visibility		The new visibility: 1 -> All, 2 -> Only players, 3 Only spectators
 */
Void SetPlayersListsVisibility(Integer _Visibility) {
	declare netwrite Integer Net_LibUI_PlayersListVisibility for Teams[0];
	if (_Visibility < 1 || _Visibility > 3) Net_LibUI_PlayersListVisibility = 1;
	else Net_LibUI_PlayersListVisibility = _Visibility;
}

// ---------------------------------- //
// Ranking
// ---------------------------------- //
// ---------------------------------- //
/** Deprecated - Use the rank functions instead
 *	Set the content of the Position layer
 *
 *	@param	_LayerPosition	The layer who'll receive the manialink
 */
Void SetLayerPosition(CUILayer _LayerPosition) {
	_LayerPosition.ManialinkPage = """
<script><!--
	main() {
		declare Label_PosTotal <=> (Page.GetFirstChild("PosTotal") as CMlLabel);
		declare Label_PosCurrent <=> (Page.GetFirstChild("PosCurrent") as CMlLabel);
		declare netread Integer Net_LibInterface_LayerInfoUpdate for UI;
		declare netread Integer Net_LibInterface_PosTotal for UI;
		declare netread Integer Net_LibInterface_PosCurrent for UI;
		declare LastUpdate = 0;
		declare LastPosCurrent = 0;
		declare PosCurrentString = "";
		declare NewPosIsBetter = False;
		declare AnimStartTime = -1;
		declare AnimDuration = 500;
		declare AnimScalePower = 2.5;
		
		while(True) {
			yield;
			
			if (LastUpdate != Net_LibInterface_LayerInfoUpdate) {
				LastUpdate = Net_LibInterface_LayerInfoUpdate;
				
				if (Net_LibInterface_PosCurrent <= 0) PosCurrentString = "-";
				else PosCurrentString = ""^Net_LibInterface_PosCurrent;
				
				Label_PosTotal.SetText("/"^Net_LibInterface_PosTotal);
				Label_PosCurrent.SetText(PosCurrentString);
				
				if (LastPosCurrent != Net_LibInterface_PosCurrent) {
					if (LastPosCurrent > Net_LibInterface_PosCurrent) NewPosIsBetter = True;
					else NewPosIsBetter = False;
					LastPosCurrent = Net_LibInterface_PosCurrent;
					AnimStartTime = Now;
				}
			}
			
			if (AnimStartTime >= 0 && AnimStartTime + AnimDuration > Now) {
				declare X = (Now - AnimStartTime) / (AnimDuration * 1.);
				Label_PosCurrent.Scale = (((X * X) * -AnimScalePower) + 1) + (AnimScalePower * X);
				if (NewPosIsBetter) Label_PosCurrent.SetText("$0f0"^PosCurrentString);
				else Label_PosCurrent.SetText("$f00"^PosCurrentString);
			} else if (AnimStartTime >= 0 && AnimStartTime + AnimDuration <= Now) {
				Label_PosCurrent.Scale = 1.;
				AnimStartTime = -1;
				Label_PosCurrent.SetText(PosCurrentString);
			}
		}
	}
--></script>
<frame posn="150 -88">
	<label posn="0 1" halign="left" valign="bottom" id="PosTotal" />
	<label posn="0 0" halign="right" valign="bottom" style="TextRaceChrono" id="PosCurrent" />
</frame>
""";
}

// ---------------------------------- //
/** Deprecated - Use the rank functions instead
 *	Update a player position UI
 *
 *	@param	_Player		The player to update
 */
Void UpdatePosition(CSmPlayer _Player) {
	declare UI <=> UIManager.GetUI(_Player);
	if (UI == Null) return;
	
	declare LibInterface_LastPosCurrent for _Player = -1;
	declare netwrite Integer Net_LibInterface_LayerInfoUpdate for UI;
	declare netwrite Integer Net_LibInterface_PosTotal for UI;
	declare netwrite Integer Net_LibInterface_PosCurrent for UI;
	
	Net_LibInterface_LayerInfoUpdate = Now;
	Net_LibInterface_PosTotal = Scores.count;
	Net_LibInterface_PosCurrent = Scores.keyof(_Player.Score) + 1;
		
	// Play a sound if the player ranking changes while he's in top 5
	if (
		LibInterface_LastPosCurrent != Net_LibInterface_PosCurrent
		&& (LibInterface_LastPosCurrent <= 5 || Net_LibInterface_PosCurrent <= 5)
	) {	
		declare SoundVariant = 0;
		// Go down
		if (LibInterface_LastPosCurrent > Net_LibInterface_PosCurrent) {
			SoundVariant = 0;
		} 
		// Go up
		else if (LibInterface_LastPosCurrent < Net_LibInterface_PosCurrent) {
			SoundVariant = 1;
		}
		LibInterface_LastPosCurrent = Net_LibInterface_PosCurrent;
		UI.SendNotice(
			"", 
			CUIConfig::ENoticeLevel::PlayerInfo, 
			Null, CUIConfig::EAvatarVariant::Default, 
			CUIConfig::EUISound::RankChange, SoundVariant
		);
	}
}

// ---------------------------------- //
// Ranking
// ---------------------------------- //
// ---------------------------------- //
/** Create the Rank manialink
 *
 *	@return		The manialink string
 */
Text Private_CreateLayerRank() {
	return """
<frame posn="150 -88">
	<label posn="0 1" halign="left" valign="bottom" style="TextRaceMessage" id="Label_PosTotal" />
	<label posn="0 0" halign="right" valign="bottom" style="TextRaceChrono" id="Label_PosCurrent" />
</frame>
<script><!--
#Include "TextLib" as TL

#Const C_RefreshInterval	250
#Const C_AnimDuration		500
#Const C_AnimScalePower		2.5

main() {
	declare Label_PosTotal		<=> (Page.GetFirstChild("Label_PosTotal")	as CMlLabel);
	declare Label_PosCurrent	<=> (Page.GetFirstChild("Label_PosCurrent")	as CMlLabel);
	declare PrevPosTotal	= 0;
	declare PrevPosCurrent	= 0;
	declare PrevRefresh		= 0;
	declare NewPosIsBetter	= False;
	declare AnimStartTime	= -1;
	
	while (True) {
		yield;
		
		if (!PageIsVisible) continue;
		
		if (PrevRefresh + C_RefreshInterval < Now) {
			PrevRefresh = Now;
			
			if (Scores.count != PrevPosTotal) {
				PrevPosTotal = Scores.count;
				Label_PosTotal.SetText("/"^PrevPosTotal);
			}
			
			if (
				InputPlayer != Null 
				&& InputPlayer.Score != Null
				&& Scores.keyof(InputPlayer.Score) + 1 != PrevPosCurrent
			) {
				declare PosCurrent = Scores.keyof(InputPlayer.Score) + 1;
				if (PosCurrent < PrevPosCurrent) NewPosIsBetter = True;
				else NewPosIsBetter = False;
				PrevPosCurrent = PosCurrent;
				Label_PosCurrent.SetText(TL::ToText(PrevPosCurrent));
				AnimStartTime = Now;
			}
		}
			
		if (AnimStartTime >= 0 && AnimStartTime + C_AnimDuration > Now) {
			declare X = (Now - AnimStartTime) / (C_AnimDuration * 1.);
			Label_PosCurrent.Scale = (((X * X) * -C_AnimScalePower) + 1) + (C_AnimScalePower * X);
			if (NewPosIsBetter) Label_PosCurrent.SetText("$0f0"^PrevPosCurrent);
			else Label_PosCurrent.SetText("$f00"^PrevPosCurrent);
		} else if (AnimStartTime >= 0 && AnimStartTime + C_AnimDuration <= Now) {
			Label_PosCurrent.Scale = 1.;
			AnimStartTime = -1;
			Label_PosCurrent.SetText(TL::ToText(PrevPosCurrent));
		}
	}
}
--></script>
""";
}

// ---------------------------------- //
/// Destroy the rank UI
Void DestroyRank() {
	if (G_LibUI_LayerRankId != NullId && UIManager.UILayers.existskey(G_LibUI_LayerRankId)) {
		UIManager.UILayerDestroy(UIManager.UILayers[G_LibUI_LayerRankId]);
		G_LibUI_LayerRankId = NullId;
	}
}

// ---------------------------------- //
/// Create the rank UI
Void CreateRank() {
	// Check if a rank layer was already created and destroy it if it's the case
	DestroyRank();
	
	// Create and assign the layer
	declare LayerRank <=> UIManager.UILayerCreate();
	LayerRank.ManialinkPage = Private_CreateLayerRank();
	G_LibUI_LayerRankId = LayerRank.Id;
	UIManager.UIAll.UILayers.add(LayerRank);
}

// ---------------------------------- //
/// Attach the rank layer to all players
Void AttachRank() {
	if (UIManager.UIAll.UILayers.existskey(G_LibUI_LayerRankId)) return;
	if (!UIManager.UILayers.existskey(G_LibUI_LayerRankId)) return;
	
	UIManager.UIAll.UILayers.add(UIManager.UILayers[G_LibUI_LayerRankId]);
}

// ---------------------------------- //
/// Detach the rank layer from all players
Void DetachRank() {
	if (!UIManager.UIAll.UILayers.existskey(G_LibUI_LayerRankId)) return;
	if (!UIManager.UILayers.existskey(G_LibUI_LayerRankId)) return;
	
	declare Detached = UIManager.UIAll.UILayers.removekey(G_LibUI_LayerRankId);
}

// ---------------------------------- //
// Slider display
// ---------------------------------- //

// ---------------------------------- //
/** Create the different models of slides
 *
 *	@return		The slides models
 */
Text Private_CreateSlideModel() {
	return """
<framemodel id="Slide_TopEntry">
	<label posn="0 0" sizen="5 6" textemboss="1" id="Label_Rank" />
	<label posn="6 0" sizen="28 6" textemboss="1"  id="Label_Name" />
	<label posn="35 0" sizen="9 6" textemboss="1" id="Label_Value" />
</framemodel>
<framemodel id="Slide_Top">
	<frame id="Frame_Global">
		<quad posn="0 17.5" sizen="100 50" halign="center" image="{{{C_LibUI_ImgSrc}}}topsBg.dds" />
		<label posn="0 0" sizen="90 5" halign="center" textemboss="1" id="Label_Title" />
		<frame id="Frame_Top">
			<frameinstance posn="-45 -6" modelid="Slide_TopEntry" id="Frame_TopEntry_1" />
			<frameinstance posn="-45 -12" modelid="Slide_TopEntry" id="Frame_TopEntry_2" />
			<frameinstance posn="-45 -18" modelid="Slide_TopEntry" id="Frame_TopEntry_3" />
			<frameinstance posn="1 -6" modelid="Slide_TopEntry" id="Frame_TopEntry_4" />
			<frameinstance posn="1 -12" modelid="Slide_TopEntry" id="Frame_TopEntry_5" />
			<frameinstance posn="1 -18" modelid="Slide_TopEntry" id="Frame_TopEntry_6" />
		</frame>
	</frame>
</framemodel>
<framemodel id="Slide_Text">
	<frame id="Frame_Global">
		<quad posn="0 17.5" sizen="100 50" halign="center" image="{{{C_LibUI_ImgSrc}}}topsBg.dds" />
		<label posn="0 0" sizen="90 5" halign="center" textemboss="1" id="Label_Title" />
		<label posn="0 -15" sizen="90 40" halign="center" valign="center" textemboss="1" autonewline="1" id="Label_Content" />
	</frame>
</framemodel>
<framemodel id="Slide_Img">
	<frame id="Frame_Global">
		<quad posn="0 17.5" sizen="100 50" halign="center" image="{{{C_LibUI_ImgSrc}}}topsBg.dds" />
		<label posn="0 0" sizen="90 5" halign="center" textemboss="1" id="Label_Title" />
		<quad posn="0 -15" sizen="90 15" halign="center" valign="center" id="Quad_Img" />
	</frame>
</framemodel>
""";
}

// ---------------------------------- //
/** Create the maniascript for the slider ML
 *
 *	@return		The maniascript of the slider ML
 */
Text Private_CreateSliderScript() {
	return """
<script><!--
#Include "TextLib" as TL

#Const C_SlidePause		{{{C_LibUI_SlidePause}}}
#Const C_SlideType_Top	{{{C_LibUI_SlideType_Top}}}
#Const C_SlideType_Text	{{{C_LibUI_SlideType_Text}}}
#Const C_SlideType_Img	{{{C_LibUI_SlideType_Img}}}
#Const C_AnimDuration	1000
#Const C_AnimHidePos	115.
#Const C_AnimShowPos	86.

declare CMlFrame[Integer] G_Slides;

Void UpdateSlideTop(Integer _SlideId, Text _Title, Text _TopId) {	
	declare Label_Title		<=> (G_Slides[_SlideId].GetFirstChild("Label_Title") as CMlLabel);
	Label_Title.SetText(_Title);
	
	// Values shared from the Top2 library
	declare Text[Integer][Text]	LibUI_TopValue		for UI;
	declare Text[Integer][Text]	LibUI_TopName		for UI;
	if (!LibUI_TopValue.existskey(_TopId) || !LibUI_TopName.existskey(_TopId)) return;
	
	declare Frame_Top		<=> (G_Slides[_SlideId].GetFirstChild("Frame_Top") as CMlFrame);
	declare Entry = 1;
	foreach (Frame_TopEntry in Frame_Top.Controls) {		
		declare Label_Rank	<=> ((Frame_TopEntry as CMlFrame).GetFirstChild("Label_Rank") as CMlLabel);
		declare Label_Name	<=> ((Frame_TopEntry as CMlFrame).GetFirstChild("Label_Name") as CMlLabel);
		declare Label_Value	<=> ((Frame_TopEntry as CMlFrame).GetFirstChild("Label_Value") as CMlLabel);
		
		Label_Rank.SetText(TL::ToText(Entry));
		if (!LibUI_TopName[_TopId].existskey(Entry) || !LibUI_TopValue[_TopId].existskey(Entry)) {
			Label_Name.SetText("---");
			Label_Value.SetText("---");
		} else {
			Label_Name.SetText(LibUI_TopName[_TopId][Entry]);
			Label_Value.SetText(LibUI_TopValue[_TopId][Entry]);
		}
		Entry += 1;
	}
}

Void UpdateSlideText(Integer _SlideId, Text _Title, Text _Content) {
	declare Label_Title		<=> (G_Slides[_SlideId].GetFirstChild("Label_Title") as CMlLabel);
	declare Label_Content	<=> (G_Slides[_SlideId].GetFirstChild("Label_Content") as CMlLabel);
	
	Label_Title.SetText(_Title);
	Label_Content.SetText(_Content);
}

Void UpdateSlideImg(Integer _SlideId, Text _Title, Text _ImgUrl) {
	declare Label_Title		<=> (G_Slides[_SlideId].GetFirstChild("Label_Title") as CMlLabel);
	declare Quad_Img		<=> (G_Slides[_SlideId].GetFirstChild("Quad_Img") as CMlQuad);
	
	Label_Title.SetText(_Title);
	if (Http.IsValidUrl(_ImgUrl)) Quad_Img.ChangeImageUrl(_ImgUrl);
}

Real EaseInOutBack(Integer _Time, Real _StartPos, Real _EndPos, Integer _Duration, Real _Damping) {
	declare Distance = (_EndPos - _StartPos) / 2.;
	declare Progression = (_Time * 1.) / (_Duration / 2.);
	declare Damping = _Damping * 1.525;
	
	// In
	if (Progression < 1.) {
		return Distance * Progression * Progression * ((Damping + 1.) * Progression - Damping) + _StartPos;
	} 
	// Out
	else {
		Progression -= 2.;
		return Distance * (Progression * Progression * ((Damping + 1.) * Progression + Damping) + 2.) + _StartPos;
	}
	
	return 0.;
}

main() {
	declare netread Integer Net_LibUI_VisibilityUpdate for UI;
	declare netread Boolean[Integer] Net_LibUI_SliderVisibility for UI;
	declare netread Integer Net_LibUI_SlidePauseDuration for UI;
	declare netread Integer Net_LibUI_SlideUpdate for Teams[0];
	declare netread Integer[Integer] Net_LibUI_SlideType for Teams[0];
	declare netread Text[Integer] Net_LibUI_SlideTitle for Teams[0];
	declare netread Text[Integer] Net_LibUI_SlideContent for Teams[0];
	
	// Values shared from the Top2 library
	declare Integer				LibUI_TopUpdate		for UI;
	declare Text[Integer][Text]	LibUI_TopValue		for UI;
	declare Text[Integer][Text]	LibUI_TopName		for UI;
	
	declare PrevVisibilityUpdate = -1;
	declare PrevSliderVisibility = Boolean[Integer];
	declare PrevSlideUpdate = -1;
	declare PrevTitle = Text[Integer];
	declare PrevContent = Text[Integer];
	declare PrevTopUpdate = -1;
	
	declare PauseDuration = C_SlidePause;
	declare NextRotation = Now + PauseDuration;
	declare Slides = Integer[];
	declare CurrentSlide = 0;
	declare AnimStartTime = 0;
	declare AnimEndTime = 0;
	declare AnimStartPos = 0.;
	declare AnimEndPos = 0.;
	declare SliderIsVisible = False;
	
	declare Frame_Slider <=> (Page.GetFirstChild("Frame_Slider") as CMlFrame);
	Frame_Slider.Hide();
	declare FirstSlide = True;
	foreach (Control in Frame_Slider.Controls) {
		declare SlideId = TL::ToInteger(TL::SubString(Control.ControlId, 6, 30));
		G_Slides[SlideId] = (Control as CMlFrame);
		PrevTitle[SlideId] = "";
		PrevContent[SlideId] = "";
		if (!FirstSlide) Control.Hide();
		Slides.add(SlideId);
		FirstSlide = False;
	}
	
	while (True) {
		yield;
		
		if (InputPlayer == Null) continue;
		if (!PageIsVisible) continue;
		
		if (PrevVisibilityUpdate != Net_LibUI_VisibilityUpdate) {
			PrevVisibilityUpdate = Net_LibUI_VisibilityUpdate;
			PrevSliderVisibility = Net_LibUI_SliderVisibility;
		}
		
		declare ToRemove = Integer[];
		foreach (Time => IsVisible in PrevSliderVisibility) {
			if (Time <= ArenaNow) {
				ToRemove.add(Time);
				if (IsVisible && !SliderIsVisible) {
					AnimStartTime	= Now;
					AnimEndTime		= AnimStartTime + C_AnimDuration;
					AnimStartPos	= C_AnimHidePos;
					AnimEndPos		= C_AnimShowPos;
					SliderIsVisible	= True;
					Frame_Slider.Show();
				} else if (!IsVisible && SliderIsVisible) {
					AnimStartTime	= Now;
					AnimEndTime		= AnimStartTime + C_AnimDuration;
					AnimStartPos	= C_AnimShowPos;
					AnimEndPos		= C_AnimHidePos;
					SliderIsVisible	= False;
				}
			}
		}
		foreach (Item in ToRemove) {
			declare Removed = PrevSliderVisibility.removekey(Item);
		}
		
		if (!Frame_Slider.Visible) continue;
		
		if (PrevSlideUpdate != Net_LibUI_SlideUpdate) {
			PrevSlideUpdate = Net_LibUI_SlideUpdate;
			
			foreach (SlideId => SlideFrame in G_Slides) {
				if (!Net_LibUI_SlideType.existskey(SlideId)) continue;
				
				if (Net_LibUI_SlideTitle[SlideId] != PrevTitle[SlideId] || Net_LibUI_SlideContent[SlideId] != PrevContent[SlideId]) {
					PrevTitle[SlideId]		= Net_LibUI_SlideTitle[SlideId];
					PrevContent[SlideId]	= Net_LibUI_SlideContent[SlideId];
					
					switch (Net_LibUI_SlideType[SlideId]) {
						case C_SlideType_Top	: UpdateSlideTop(SlideId, PrevTitle[SlideId], PrevContent[SlideId]);
						case C_SlideType_Text	: UpdateSlideText(SlideId, PrevTitle[SlideId], PrevContent[SlideId]);
						case C_SlideType_Img	: UpdateSlideImg(SlideId, PrevTitle[SlideId], PrevContent[SlideId]);
					}
				}
			}
		}
		
		if (PrevTopUpdate != LibUI_TopUpdate) {
			PrevTopUpdate = LibUI_TopUpdate;
			
			foreach (SlideId => SlideType in Net_LibUI_SlideType) {
				if (SlideType == C_SlideType_Top) UpdateSlideTop(SlideId, PrevTitle[SlideId], PrevContent[SlideId]);
			}
		}
		
		if (AnimEndTime > 0 && AnimEndTime > Now) {
			Frame_Slider.PosnY = EaseInOutBack(Now - AnimStartTime, AnimStartPos, AnimEndPos, C_AnimDuration, 1.1);
		} else if (AnimEndTime > 0) {
			Frame_Slider.PosnY = AnimEndPos;
			AnimStartTime = 0;
			AnimEndTime = 0;
			if (Frame_Slider.PosnY == C_AnimHidePos) Frame_Slider.Hide();
		}
		
		if (Slides.count > 1) {
			if (PauseDuration != Net_LibUI_SlidePauseDuration && Net_LibUI_SlidePauseDuration > 0) {
				PauseDuration = Net_LibUI_SlidePauseDuration;
			}
			
			if (NextRotation <= Now) {
				NextRotation = Now + PauseDuration;
				G_Slides[Slides[CurrentSlide]].Hide();
				CurrentSlide += 1;
				if (CurrentSlide >= Slides.count) CurrentSlide = 0;
				G_Slides[Slides[CurrentSlide]].Show();
			}
		}
	}
}
--></script>
""";
}

// ---------------------------------- //
/// Update the slider manialink
Void Private_UpdateSliderML() {
	if (G_LibUI_LayerSliderId == NullId || !UIManager.UILayers.existskey(G_LibUI_LayerSliderId)) return;
	
	declare ML = "";
	ML ^= """<frame posn="-70 86" id="Frame_Slider">""";
	ML ^= Private_CreateSlideModel();
	foreach (SlideKey => SlideType in G_LibUI_SlideType) {
		switch (SlideType) {
			case C_LibUI_SlideType_Top	: ML ^= """<frameinstance modelid="Slide_Top"  id="Slide_{{{SlideKey}}}" />""";
			case C_LibUI_SlideType_Text	: ML ^= """<frameinstance modelid="Slide_Text" id="Slide_{{{SlideKey}}}" />""";
			case C_LibUI_SlideType_Img	: ML ^= """<frameinstance modelid="Slide_Img"  id="Slide_{{{SlideKey}}}" />""";
		}
	}
	ML ^= Private_CreateSliderScript();
	ML ^= """</frame>""";
	
	UIManager.UILayers[G_LibUI_LayerSliderId].ManialinkPage = ML;
}

// ---------------------------------- //
/** Update the slider visibility
 *
 *	@param	_Player		The player who'll see the slider
 *	@param	_Delay		The delay before the display of the slider
 *	@param	_Visibility	True if the slider is visible, false otherwise
 */
Void Private_UpdateSliderVisibity(CSmPlayer _Player, Integer _Delay, Boolean _Visibility) {
	if (_Player == Null) return;
	declare UI <=> UIManager.GetUI(_Player);
	if (UI == Null) return;
	
	declare netwrite Integer Net_LibUI_VisibilityUpdate for UI;
	declare netwrite Boolean[Integer] Net_LibUI_SliderVisibility for UI;
	declare ToRemove = Integer[];
	foreach (Time => Visibility in Net_LibUI_SliderVisibility) {
		if (Time + C_LibUI_SlideBufferDuration < Now) ToRemove.add(Time);
	}
	foreach (Item in ToRemove) {
		declare Removed = Net_LibUI_SliderVisibility.removekey(Item);
	}
	Net_LibUI_VisibilityUpdate = Now;
	Net_LibUI_SliderVisibility[Now + _Delay] = _Visibility;
}

// ---------------------------------- //
/// Destroy the slider
Void DestroySlider() {
	// Destroy the slider layer
	if (G_LibUI_LayerSliderId != NullId && UIManager.UILayers.existskey(G_LibUI_LayerSliderId)) {
		declare Removed = UIManager.UIAll.UILayers.removekey(G_LibUI_LayerSliderId);
		UIManager.UILayerDestroy(UIManager.UILayers[G_LibUI_LayerSliderId]);
		G_LibUI_LayerSliderId = NullId;
	}
	G_LibUI_SliderIsVisible = False;
	G_LibUI_SlideType.clear();
	G_LibUI_SlideTitle.clear();
	G_LibUI_SlideContent.clear();
}

// ---------------------------------- //
/// Create the slider
Void CreateSlider() {
	// Destroy any existing slider
	DestroySlider();
	
	// Create the slider layer
	declare LayerSlider <=> UIManager.UILayerCreate();
	G_LibUI_LayerSliderId = LayerSlider.Id;
	UIManager.UIAll.UILayers.add(LayerSlider);
}

// ---------------------------------- //
/// Attach the slider to UIAll
Void AttachSlider() {
	if (UIManager.UIAll.UILayers.existskey(G_LibUI_LayerSliderId)) return;
	if (!UIManager.UILayers.existskey(G_LibUI_LayerSliderId)) return;
	
	UIManager.UIAll.UILayers.add(UIManager.UILayers[G_LibUI_LayerSliderId]);
}

// ---------------------------------- //
/// Detach the slider from UIAll
Void DetachSlider() {
	if (!UIManager.UIAll.UILayers.existskey(G_LibUI_LayerSliderId)) return;
	if (!UIManager.UILayers.existskey(G_LibUI_LayerSliderId)) return;
	
	declare Removed = UIManager.UIAll.UILayers.removekey(G_LibUI_LayerSliderId);
}

// ---------------------------------- //
/** Add a slide to the slider
 *	Three types of slide are allowed:
 *	- Top  -> you have to use the Top2 library for this type to work
 *			 when creating a top slide you have to give the top id in the _Content parameter
 *	- Text -> A simple text, give the text to display in the _Content parameter
 *	- Img  -> An image, give the URL of the image to display in the _Content parameter
 *
 *	@param	_Id			An integer to use as identifier for the slide
 *	@param	_Type		Three types allowed: Top, Text, Img
 *	@param	_Title		The title of the slide
 *	@param	_Content	The content of the slide
 */
Void AddSlide(Integer _Id, Text _Type, Text _Title, Text _Content) {
	declare New = True;
	if (G_LibUI_SlideType.existskey(_Id)) New = False;
	
	// You can't change the type of an existing slide
	if (New) {
		declare Type = C_LibUI_SlideType_Text;
		switch (_Type) {
			case "Top"	: Type = C_LibUI_SlideType_Top;
			case "Text"	: Type = C_LibUI_SlideType_Text;
			case "Img"	: Type = C_LibUI_SlideType_Img;
			default		: Type = C_LibUI_SlideType_Text;
		}
		G_LibUI_SlideType[_Id] = Type;
	}
	G_LibUI_SlideTitle[_Id] = _Title;
	G_LibUI_SlideContent[_Id] = _Content;
	
	// If this is a new slide we need to update the slider manialink
	if (New) Private_UpdateSliderML();
	
	declare netwrite Integer Net_LibUI_SlideUpdate for Teams[0];
	declare netwrite Integer[Integer] Net_LibUI_SlideType for Teams[0];
	declare netwrite Text[Integer] Net_LibUI_SlideTitle for Teams[0];
	declare netwrite Text[Integer] Net_LibUI_SlideContent for Teams[0];
	Net_LibUI_SlideUpdate	= Now;
	Net_LibUI_SlideType		= G_LibUI_SlideType;
	Net_LibUI_SlideTitle	= G_LibUI_SlideTitle;
	Net_LibUI_SlideContent	= G_LibUI_SlideContent;
}

// ---------------------------------- //
/** Remove a slide
 *
 *	@param	_Id		The id of the slide to remove
 */
Void RemoveSlide(Integer _Id) {
	if (!G_LibUI_SlideType.existskey(_Id)) return;
	
	declare Removed = G_LibUI_SlideType.removekey(_Id);
	Removed = G_LibUI_SlideTitle.removekey(_Id);
	Removed = G_LibUI_SlideContent.removekey(_Id);
	Private_UpdateSliderML();
}

// ---------------------------------- //
/** Update a slide
 *	The add slide function overwrite a slide with the same id that the one passed in parameter
 *
 *	See the AddSlide() documentation for more info
 */
Void UpdateSlide(Integer _Id, Text _Title, Text _Content) {
	if (!G_LibUI_SlideType.existskey(_Id)) return;
	
	AddSlide(_Id, "", _Title, _Content);
}

// ---------------------------------- //
/** Display the slider for a player
 *
 *	@param	_Player		The player who'll see the slider
 *	@param	_Delay		The delay before the display of the slider
 */
Void ShowSlider(CSmPlayer _Player, Integer _Delay) {
	Private_UpdateSliderVisibity(_Player, _Delay, True);
}

// ---------------------------------- //
/** Hide the slider for a player
 *
 *	@param	_Player		The player who'll see the slider
 *	@param	_Delay		The delay before the display of the slider
 */
Void HideSlider(CSmPlayer _Player, Integer _Delay) {
	Private_UpdateSliderVisibity(_Player, _Delay, False);
}

// ---------------------------------- //
/** Change the pause duration on slides
 *
 *	@param	_Player		The affected player
 *	@param	_Duration	The new duration of the pause
 */
Void SetSliderPause(CSmPlayer _Player, Integer _Duration) {
	if (_Player == Null) return;
	declare UI <=> UIManager.GetUI(_Player);
	if (UI == Null) return;
	
	declare netwrite Integer Net_LibUI_SlidePauseDuration for UI;
	Net_LibUI_SlidePauseDuration = _Duration;
}