/**
 *	Channel progression library
 */
#Const	Version			"2017-04-24"
#Const	ScriptName	"Libs/Nadeo/ChannelProgression.Script.txt"

// ---------------------------------- //
// Libraries
// ---------------------------------- //
#Include "TextLib" as TL
#Include "Libs/Nadeo/Layers2.Script.txt" as Layers
#Include "Libs/Nadeo/Manialink/Grid.Script.txt" as Grid

// ---------------------------------- //
// Constants
// ---------------------------------- //
#Const C_LayerName	"LibChannelProgression"

// ---------------------------------- //
// Functions
// ---------------------------------- //
// ---------------------------------- //
// Private
// ---------------------------------- //
// ---------------------------------- //
/** Generate the manialink of a header
 *
 *	@param	_Size											The size of the header
 *	@param	_Ratio										The ratio between the small and big
 *																		part of the header
 *
 *	@return														The header manialink
 */
Text Private_GenerateHeaderML(Vec2 _Size, Real _Ratio) {
	return """
	<quad z-index="0" size="{{{_Size.X*_Ratio}}} {{{_Size.Y}}}" class="header-small" />
	<quad z-index="1" pos="{{{_Size.X*_Ratio}}} 0" size="{{{_Size.X-(_Size.X*_Ratio)}}} {{{_Size.Y}}}" class="header-big" />
""";
}

// ---------------------------------- //
/** Get the channel progression manialink
 *
 *	@return														The manialink
 */
Text Private_GetChannelProgressionML() {
	// Title
	declare SizeX_Title = 200.;
	declare SizeY_Title = 20.;
	//L16N [Channel progression] Title displayed above the channel progression window
	declare Title = TL::ToUpperCase(_("Program progression"));
	
	declare GridX_Title = Grid::CreateConfig(SizeX_Title, 64, 1., False, Grid::C_Direction_Right);
	declare GridY_Title = Grid::CreateConfig(SizeY_Title, 1, 4., True, Grid::C_Direction_Top);
	
	declare SizeX_Bar = Grid::GetSize(GridX_Title, 1);
	declare SizeX_TitleValue = Grid::GetSize(GridX_Title, 63);
	declare PosX_Bar = Grid::Push(GridX_Title, 0);
	declare PosX_TitleValue = Grid::Push(GridX_Title, 1);
	
	declare SizeY_Bar = Grid::GetSize(GridY_Title, 1);
	declare PosY_Bar = Grid::Push(GridY_Title, 0, Grid::C_Align_Center, SizeY_Bar);
	
	// Content
	declare Columns = 16;
	declare Lines = 16;
	declare Margin = 1.;
	declare Padding= False;
	declare SizeX_Content = 200.;
	declare SizeY_Content = 100.;
	
	declare GridX_Content = Grid::CreateConfig(SizeX_Content, Columns, Margin, Padding, Grid::C_Direction_Right);
	declare GridY_Content = Grid::CreateConfig(SizeY_Content, Lines, Margin, Padding, Grid::C_Direction_Bottom);
	
	declare SizeX_LeftColumn = Grid::GetSize(GridX_Content, 4);
	declare SizeX_RightColumn = Grid::GetSize(GridX_Content, 12);
	declare PosX_LeftColumn = Grid::Push(GridX_Content, 0);
	declare PosX_RightColumn = Grid::Push(GridX_Content, 4);
	
	declare SizeY_Ranking = Grid::GetSize(GridY_Content, 8);
	declare SizeY_Score = Grid::GetSize(GridY_Content, 8);
	declare PosY_Ranking = Grid::Push(GridY_Content, 0);
	declare PosY_Score = Grid::Push(GridY_Content, 8);
	
	declare SizeY_Emblem = Grid::GetSize(GridY_Content, 4);
	declare SizeY_BestScores = Grid::GetSize(GridY_Content, 4);
	declare SizeY_Reward = Grid::GetSize(GridY_Content, 8);
	declare PosY_Emblem = Grid::Push(GridY_Content, 0);
	declare PosY_BestScores = Grid::Push(GridY_Content, 4);
	declare PosY_Reward = Grid::Push(GridY_Content, 8);
	
	// Ranking
	declare RankingPadding = True;
	declare RankingsNb = 8;
	
	declare GridX_Ranking = Grid::CreateConfig(SizeX_LeftColumn, Columns, Margin, RankingPadding, Grid::C_Direction_Right);
	declare GridY_Ranking = Grid::CreateConfig(SizeY_Ranking, RankingsNb, Margin, RankingPadding, Grid::C_Direction_Bottom);
	
	declare SizeX_RankingPosition = Grid::GetSize(GridX_Ranking, 2);
	declare SizeX_RankingName = Grid::GetSize(GridX_Ranking, 10);
	declare SizeX_RankingScore = Grid::GetSize(GridX_Ranking, 4);
	declare SizeY_RankingItem = Grid::GetSize(GridY_Ranking, 1);
	
	declare PosX_RankingPosition = Grid::Push(GridX_Ranking, 0, Grid::C_Align_Center, SizeX_RankingPosition);
	declare PosX_RankingName = Grid::Push(GridX_Ranking, 2);
	declare PosX_RankingScore = Grid::Push(GridX_Ranking, 12, Grid::C_Align_Right, SizeX_RankingScore);
	
	declare RankingsML = "";
	for (I, 1, RankingsNb) {
		declare PosY = Grid::Push(GridY_Ranking, (I - 1), Grid::C_Align_Center, SizeY_RankingItem);
		RankingsML ^= """<frameinstance pos="0 {{{PosY}}}" modelid="framemodel-player-ranking" />""";
	}
	
	// Score
	declare ScorePadding = True;
	declare ScoreColumns = 1;
	declare ScoreLines = 10;
	//L16N [Channel progression] Legend displayed above the score of the player in the channel progression window
	declare ScoreLegend = _("Score");
	
	declare GridX_Score = Grid::CreateConfig(SizeX_LeftColumn, ScoreColumns, Margin, ScorePadding, Grid::C_Direction_Right);
	declare GridY_Score = Grid::CreateConfig(SizeY_Score, ScoreLines, Margin, ScorePadding, Grid::C_Direction_Bottom);
	
	declare SizeX_ScoreLegend = Grid::GetSize(GridX_Score, 1);
	declare SizeX_ScoreValue = Grid::GetSize(GridX_Score, 1);
	declare SizeY_ScoreLegend = Grid::GetSize(GridY_Score, 4);
	declare SizeY_ScoreValue = Grid::GetSize(GridY_Score, 6);
	
	declare PosX_ScoreLegend = Grid::Push(GridX_Score, 0, Grid::C_Align_Center, SizeX_ScoreLegend);
	declare PosX_ScoreValue = Grid::Push(GridX_Score, 0, Grid::C_Align_Center, SizeX_ScoreValue);
	declare PosY_ScoreLegend = Grid::Push(GridY_Score, 0, Grid::C_Align_Bottom, SizeY_ScoreLegend);
	declare PosY_ScoreValue = Grid::Push(GridY_Score, 4);
	
	// Emblem
	declare EmblemPadding = True;
	declare EmblemColumns = 16;
	declare EmblemLines = 16;
	
	declare GridX_Emblem = Grid::CreateConfig(SizeX_RightColumn, EmblemColumns, Margin, EmblemPadding, Grid::C_Direction_Right);
	declare GridY_Emblem = Grid::CreateConfig(SizeY_Emblem, EmblemLines, Margin, EmblemPadding, Grid::C_Direction_Bottom);
	
	declare SizeX_CurrentEmblem = Grid::GetSize(GridX_Emblem, 3);
	declare SizeX_EmblemProgressBar = Grid::GetSize(GridX_Emblem, 10);
	declare SizeX_EmblemGain = Grid::GetSize(GridX_Emblem, 10);
	declare SizeX_EmblemPoints = Grid::GetSize(GridX_Emblem, 10);
	declare SizeX_NextEmblem = Grid::GetSize(GridX_Emblem, 3);
	declare PosX_CurrentEmblem = Grid::Push(GridX_Emblem, 0, Grid::C_Align_Center, SizeX_CurrentEmblem);
	declare PosX_EmblemProgressBar = Grid::Push(GridX_Emblem, 3);
	declare PosX_EmblemGain = Grid::Push(GridX_Emblem, 3, Grid::C_Align_Right, SizeX_EmblemGain);
	declare PosX_EmblemPoints = Grid::Push(GridX_Emblem, 3, Grid::C_Align_Center, SizeX_EmblemPoints);
	declare PosX_NextEmblem = Grid::Push(GridX_Emblem, 13, Grid::C_Align_Center, SizeX_NextEmblem);
	
	declare SizeY_EmblemProgress = Grid::GetSize(GridY_Emblem, 16);
	declare SizeY_EmblemProgressBar = Grid::GetSize(GridY_Emblem, 4);
	declare SizeY_EmblemGain = Grid::GetSize(GridY_Emblem, 4);
	declare SizeY_EmblemPoints = Grid::GetSize(GridY_Emblem, 6);
	declare PosY_EmblemProgress = Grid::Push(GridY_Emblem, 0, Grid::C_Align_Center, SizeY_EmblemProgress);
	declare PosY_EmblemProgressBar = Grid::Push(GridY_Emblem, 6);
	declare PosY_EmblemGain = Grid::Push(GridY_Emblem, 6, Grid::C_Align_Center, SizeY_EmblemGain);
	declare PosY_EmblemPoints = Grid::Push(GridY_Emblem, 10, Grid::C_Align_Center, SizeY_EmblemPoints);
	
	// Best scores
	declare BestScoresPadding = True;
	declare BestScoresNb = 8;
	declare BestScoresLines = 10;
	//L16N [Channel progression] Legend displayed above the best scores of the player in the channel progression window
	declare BestScoresLegend = _("Best scores");
	
	declare GridX_BestScores = Grid::CreateConfig(SizeX_RightColumn, BestScoresNb, Margin, BestScoresPadding, Grid::C_Direction_Right);
	declare GridY_BestScores = Grid::CreateConfig(SizeY_BestScores, BestScoresLines, Margin, BestScoresPadding, Grid::C_Direction_Bottom);
	
	declare SizeX_BestScoresLegend = Grid::GetSize(GridX_BestScores, BestScoresNb);
	declare SizeX_BestScore = Grid::GetSize(GridX_BestScores, 1);
	declare PosX_BestScoresLegend = Grid::Push(GridX_BestScores, 0, Grid::C_Align_Center, SizeX_BestScoresLegend);
	
	declare SizeY_BestScoresLegend = Grid::GetSize(GridY_BestScores, 4);
	declare SizeY_BestScoreRank = Grid::GetSize(GridY_BestScores, 3);
	declare SizeY_BestScoreValue = Grid::GetSize(GridY_BestScores, 3);
	declare PosY_BestScoresLegend = Grid::Push(GridY_BestScores, 0, Grid::C_Align_Center, SizeY_BestScoresLegend);
	declare PosY_BestScoreRank = Grid::Push(GridY_BestScores, 4, Grid::C_Align_Center, SizeY_BestScoreRank);
	declare PosY_BestScoreValue = Grid::Push(GridY_BestScores, 7, Grid::C_Align_Center, SizeY_BestScoreValue);
	
	// Reward
	declare RewardPadding = True;
	declare RewardColumns = 16;
	declare RewardLines = 1;
	
	declare GridX_Reward = Grid::CreateConfig(SizeX_RightColumn, RewardColumns, Margin, RewardPadding, Grid::C_Direction_Right);
	declare GridY_Reward = Grid::CreateConfig(SizeY_Reward, RewardLines, Margin, RewardPadding, Grid::C_Direction_Bottom);
	
	declare SizeX_RewardRoulette = Grid::GetSize(GridX_Reward, 7);
	declare SizeX_RewardArrow = Grid::GetSize(GridX_Reward, 2);
	declare SizeX_RewardResult = Grid::GetSize(GridX_Reward, 7);
	declare PosX_RewardRoulette = Grid::Push(GridX_Reward, 0, Grid::C_Align_Center, SizeX_RewardRoulette);
	declare PosX_RewardArrow = Grid::Push(GridX_Reward, 7, Grid::C_Align_Center, SizeX_RewardArrow);
	declare PosX_RewardResult = Grid::Push(GridX_Reward, 9, Grid::C_Align_Center, SizeX_RewardResult);
	
	declare SizeY_RewardRoulette = Grid::GetSize(GridY_Reward, 1);
	declare SizeY_RewardArrow = Grid::GetSize(GridY_Reward, 1);
	declare SizeY_RewardResult = Grid::GetSize(GridY_Reward, 1);
	declare PosY_RewardRoulette = Grid::Push(GridY_Reward, 0, Grid::C_Align_Center, SizeY_RewardRoulette);
	declare PosY_RewardArrow = Grid::Push(GridY_Reward, 0, Grid::C_Align_Center, SizeY_RewardArrow);
	declare PosY_RewardResult = Grid::Push(GridY_Reward, 0, Grid::C_Align_Center, SizeY_RewardResult);
	
	declare BestScoresML = "";
	for (I, 1, BestScoresNb) {
		declare PosX = Grid::Push(GridX_BestScores, (I-1), Grid::C_Align_Center, SizeX_BestScore);
		BestScoresML ^= """<frameinstance pos="{{{PosX}}} 0" modelid="framemodel-best-score" />""";
	}
	
	return """
<manialink version="3" name="ChannelProgression">
<stylesheet>
	<style class="bg-dark" opacity="0.5" bgcolor="0b081b" />
	<style class="bg-blur" style="Bgs1" substyle="BgDialogBlur" opacity="0.1" />
	<style class="header-small" valign="bottom" colorize="1ec8c2" image="file://Media/Manialinks/Nadeo/Common/ScoresTable/HeaderSmall.dds" />
	<style class="header-big" valign="bottom" colorize="1ec8c2" image="file://Media/Manialinks/Nadeo/Common/ScoresTable/HeaderBig.dds" />
	<style class="text-default" textsize="2" textfont="OswaldMono" textcolor="fff" textemboss="1" />
	<style class="text-medium" textsize="6" textfont="OswaldMono" textcolor="fff" textemboss="1" />
	<style class="text-big" textsize="12" textfont="OswaldMono" textcolor="fff" textemboss="1" />
</stylesheet>
<framemodel id="framemodel-player-ranking">
	<label pos="{{{PosX_RankingPosition}}} 0" size="{{{SizeX_RankingPosition}}} {{{SizeY_RankingItem}}}" halign="center" valign="center2" class="text-default" id="label-rank" />
	<label pos="{{{PosX_RankingName}}} 0" size="{{{SizeX_RankingName}}} {{{SizeY_RankingItem}}}" valign="center2" class="text-default" id="label-name" />
	<label pos="{{{PosX_RankingScore}}} 0" size="{{{SizeX_RankingScore}}} {{{SizeY_RankingItem}}}" halign="right" valign="center2" class="text-default" id="label-score" />
</framemodel>
<framemodel id="framemodel-best-score">
	<label pos="0 {{{PosY_BestScoreRank}}}" size="{{{SizeX_BestScore}}} {{{SizeY_BestScoreRank}}}" halign="center" valign="center2" class="text-default" id="label-rank" />
	<label pos="0 {{{PosY_BestScoreValue}}}" size="{{{SizeX_BestScore}}} {{{SizeY_BestScoreValue}}}" halign="center" valign="center2" class="text-default" id="label-score" />
</framemodel>
<frame z-index="25" id="frame-global">
	<frame pos="0 50" id="frame-pos">
		<frame pos="{{{SizeX_Content*-0.5}}} 0">
			<!-- Title -->
			<frame pos="0 {{{PosY_Bar}}}" z-index="0">
				<quad pos="{{{PosX_Bar}}} 0" z-index="0" size="{{{SizeX_Bar}}} {{{SizeY_Bar}}}" valign="center" bgcolor="fff" />
				<label pos="{{{PosX_TitleValue}}} 0" z-index="1" size="{{{SizeX_TitleValue}}} {{{SizeY_Bar}}}" valign="center2" text="{{{Title}}}" class="text-big" />
			</frame>
			<!-- Left column -->
			<frame pos="{{{PosX_LeftColumn}}} 0" z-index="1">
	      <frame pos="0 1" z-index="0">
					{{{Private_GenerateHeaderML(<SizeX_LeftColumn, 0.6>, 0.95)}}}
				</frame>
				<!-- Ranking -->
				<frame pos="0 {{{PosY_Ranking}}}" z-index="1">
					<frame z-index="0">
						<quad z-index="1" size="{{{SizeX_LeftColumn}}} {{{SizeY_Ranking}}}" class="bg-dark" />
						<quad z-index="0" size="{{{SizeX_LeftColumn}}} {{{SizeY_Ranking}}}" class="bg-blur" />
					</frame>
					<frame z-index="1" id="frame-ranking">
						{{{RankingsML}}}
					</frame>
				</frame>
				<!-- Score -->
				<frame pos="0 {{{PosY_Score}}}" z-index="2" id="frame-score">
					<frame z-index="0">
						<quad size="{{{SizeX_LeftColumn}}} {{{SizeY_Score}}}" class="bg-dark" />
						<quad size="{{{SizeX_LeftColumn}}} {{{SizeY_Score}}}" class="bg-blur" />
					</frame>
					<frame z-index="1">
						<label pos="{{{PosX_ScoreLegend}}} {{{PosY_ScoreLegend}}}" size="{{{SizeX_ScoreLegend}}} {{{SizeY_ScoreLegend}}}" halign="center" valign="bottom" text="{{{ScoreLegend}}}" class="text-medium" />
						<label pos="{{{PosX_ScoreValue}}} {{{PosY_ScoreValue}}}" size="{{{SizeX_ScoreValue}}} {{{SizeY_ScoreValue}}}" halign="center" text="0" class="text-big" id="label-score" />
					</frame>
				</frame>
			</frame>
			<!-- Right column -->
			<frame pos="{{{PosX_RightColumn}}} 0" z-index="2">
	      <frame pos="0 1" z-index="0">
					{{{Private_GenerateHeaderML(<SizeX_RightColumn, 0.6>, 0.7)}}}
				</frame>
				<!-- Emblem -->
				<frame pos="0 {{{PosY_Emblem}}}" z-index="1">
					<frame z-index="0">
						<quad size="{{{SizeX_RightColumn}}} {{{SizeY_Emblem}}}" class="bg-dark" />
						<quad size="{{{SizeX_RightColumn}}} {{{SizeY_Emblem}}}" class="bg-blur" />
					</frame>
					<frame z-index="1" id="frame-emblem">
						<quad pos="{{{PosX_CurrentEmblem}}} {{{PosY_EmblemProgress}}}" z-index="1" size="{{{SizeX_CurrentEmblem}}} {{{SizeY_EmblemProgress}}}" halign="center" valign="center" keepratio="fit" id="quad-current-emblem" />
						<frame pos="{{{PosX_EmblemProgressBar}}} {{{PosY_EmblemProgressBar}}}" z-index="0">
							<quad size="{{{SizeX_EmblemProgressBar}}} {{{SizeY_EmblemProgressBar}}}" z-index="0" bgcolor="777" />
							<quad size="{{{SizeX_EmblemProgressBar*0.5}}} {{{SizeY_EmblemProgressBar}}}" z-index="1" bgcolor="f00" id="quad-progress-bar" />
						</frame>
						<label pos="{{{PosX_EmblemGain-1}}} {{{PosY_EmblemGain-0.25}}}" z-index="4" size="{{{SizeX_EmblemGain}}} {{{SizeY_EmblemGain}}}" halign="right" valign="center2" class="text-default" id="label-gain" />
						<label pos="{{{PosX_EmblemPoints}}} {{{PosY_EmblemPoints}}}" z-index="3" size="{{{SizeX_EmblemPoints}}} {{{SizeY_EmblemPoints}}}" halign="center" valign="center2" class="text-medium" id="label-points" />
						<quad pos="{{{PosX_NextEmblem}}} {{{PosY_EmblemProgress}}}" z-index="2" size="{{{SizeX_NextEmblem}}} {{{SizeY_EmblemProgress}}}" halign="center" valign="center" keepratio="fit" id="quad-next-emblem" />
					</frame>
				</frame>
				<!-- Best scores -->
				<frame pos="0 {{{PosY_BestScores}}}" z-index="2">
					<frame z-index="0">
						<quad size="{{{SizeX_RightColumn}}} {{{SizeY_BestScores}}}" class="bg-dark" />
						<quad size="{{{SizeX_RightColumn}}} {{{SizeY_BestScores}}}" class="bg-blur" />
					</frame>
					<frame z-index="1">
						<label pos="{{{PosX_BestScoresLegend}}} {{{PosY_BestScoresLegend}}}" z-index="0" size="{{{SizeX_BestScoresLegend}}} {{{SizeY_BestScoresLegend}}}" halign="center" valign="center2" class="text-medium" text="{{{BestScoresLegend}}}" />
						<frame z-index="1" id="frame-best-scores">
							{{{BestScoresML}}}
						</frame>
					</frame>
				</frame>
				<!-- Reward -->
				<frame pos="0 {{{PosY_Reward}}}" z-index="3" id="frame-reward">
					<frame z-index="0">
						<quad size="{{{SizeX_RightColumn}}} {{{SizeY_Reward}}}" class="bg-dark" />
						<quad size="{{{SizeX_RightColumn}}} {{{SizeY_Reward}}}" class="bg-blur" />
					</frame>
					<frame z-index="1">
						<quad pos="{{{PosX_RewardRoulette}}} {{{PosY_RewardRoulette}}}" size="{{{SizeX_RewardRoulette}}} {{{SizeY_RewardRoulette}}}" halign="center" valign="center" />
						<quad pos="{{{PosX_RewardArrow}}} {{{PosY_RewardArrow}}}" size="{{{SizeX_RewardArrow}}} {{{SizeY_RewardArrow}}}" halign="center" valign="center" />
						<quad pos="{{{PosX_RewardResult}}} {{{PosY_RewardResult}}}" size="{{{SizeX_RewardResult}}} {{{SizeY_RewardResult}}}" halign="center" valign="center" />
					</frame>
				</frame>
			</frame>
		</frame>
	</frame>
</frame>
<script><!--
#Include "TextLib" as TL
#Include "MathLib" as ML
#Include "AnimLib" as AL

CTmMlPlayer GetOwner() {
	if (GUIPlayer != Null) return GUIPlayer;
	return InputPlayer;
}

Void UpdateRanking(CMlFrame _Frame, Text[] _Names, Integer[] _Scores) {
	if (_Frame == Null) return;
	
	foreach (Key => Control in _Frame.Controls) {
		declare Frame_Item <=> (Control as CMlFrame);
		
		if (_Names.existskey(Key) && _Scores.existskey(Key)) {
			declare Label_Rank <=> (Frame_Item.GetFirstChild("label-rank") as CMlLabel);
			declare Label_Name <=> (Frame_Item.GetFirstChild("label-name") as CMlLabel);
			declare Label_Score <=> (Frame_Item.GetFirstChild("label-score") as CMlLabel);
			Label_Rank.Value = TL::ToText(Key + 1);
			Label_Name.Value = _Names[Key];
			Label_Score.Value = TL::ToText(_Scores[Key]);
			Frame_Item.Visible = True;
		} else {
			Frame_Item.Visible = False;
		}
	}
}

Void UpdateBestScores(CMlFrame _Frame, Integer[] _BestScores) {
	if (_Frame == Null) return;
	
	foreach (Key => Control in _Frame.Controls) {
		declare Frame_Item <=> (Control as CMlFrame);
		
		if (_BestScores.existskey(Key)) {
			declare Label_Rank <=> (Frame_Item.GetFirstChild("label-rank") as CMlLabel);
			declare Label_Score <=> (Frame_Item.GetFirstChild("label-score") as CMlLabel);
			Label_Rank.Value = TL::ToText(Key + 1);
			Label_Score.Value = TL::ToText(_BestScores[Key]);
		} else {
			declare Label_Rank <=> (Frame_Item.GetFirstChild("label-rank") as CMlLabel);
			declare Label_Score <=> (Frame_Item.GetFirstChild("label-score") as CMlLabel);
			Label_Rank.Value = TL::ToText(Key + 1);
			Label_Score.Value = TL::ToText(0);
		}
	}
}

Void SetEmblems(Text[] _EmblemsLogo, Integer _CurrentLevel, Integer _NextLevel, CMlQuad _Quad_CurrentEmblem, CMlQuad _Quad_NextEmblem) {
	if (_Quad_CurrentEmblem != Null) {
		if (_EmblemsLogo.existskey(_CurrentLevel)) {
			_Quad_CurrentEmblem.ImageUrl = _EmblemsLogo[_CurrentLevel];
			_Quad_CurrentEmblem.Visible = True;
		} else {
			_Quad_CurrentEmblem.Visible = False;
		}
	}
	
	if (_Quad_NextEmblem != Null) {
		if (_EmblemsLogo.existskey(_NextLevel)) {
			_Quad_NextEmblem.ImageUrl = _EmblemsLogo[_NextLevel];
			_Quad_NextEmblem.Visible = True;
		} else {
			_Quad_NextEmblem.Visible = False;
		}
	}
}

Void SetXP(Integer[] _EmblemsXP, Text[] _EmblemsLogo, Integer _XP, CMlQuad _Quad_CurrentEmblem, CMlQuad _Quad_NextEmblem, CMlQuad _Quad_ProgressBar) {
	declare CurrentLevel = 0;
	declare NextLevel = 0;
	declare CurrentXP = 0;
	declare NextXP = 0;
	
	foreach (Level => EmblemXP in _EmblemsXP) {
		if (EmblemXP <= _XP) {
			CurrentLevel = Level;
			CurrentXP = EmblemXP;
		}
		NextXP = EmblemXP;
		NextLevel = Level;
		if (EmblemXP > _XP) {
			break;
		}
	}
	
	declare LevelTotalXP = NextXP - CurrentXP;
	declare LevelXP = _XP - CurrentXP;
	declare Ratio = 0.;
	// Last emblem already reached
	if (_EmblemsXP.count > 0 && _XP > _EmblemsXP[_EmblemsXP.count - 1]) {
		Ratio = 1.;
	} else {
		if (LevelTotalXP > 0) Ratio = LevelXP / (LevelTotalXP * 1.);
		if (Ratio < 0.) Ratio = 0.;
		else if (Ratio > 1.) Ratio = 1.;
	}
	
	if (_Quad_ProgressBar != Null) {
		_Quad_ProgressBar.Size.X = {{{SizeX_EmblemProgressBar}}} * Ratio;
	}
	
	SetEmblems(_EmblemsLogo, CurrentLevel, NextLevel, _Quad_CurrentEmblem, _Quad_NextEmblem);
}

Void SetXPProgression(Integer _OldXP, Integer _NewXP, CMlLabel _Label_Points, CMlLabel _Label_Gain) {
	declare DiffXP = _NewXP - _OldXP;
	if (_Label_Points != Null) {
		_Label_Points.Value = TL::ToText(_NewXP);
	}
	if (_Label_Gain != Null) {
		if (DiffXP > 0) {
			_Label_Gain.Value = "+"^DiffXP;
		} else {
			_Label_Gain.Value = TL::ToText(DiffXP);
		}
	}
}

Integer GetAnimDuration(Integer[] _EmblemsXP, Integer _OldXP, Integer _NewXP) {
	declare Duration = 0;
	
	foreach (Level => EmblemXP in _EmblemsXP) {
		if (EmblemXP >= _OldXP) {
			Duration += 2500;
		}
		if (EmblemXP > _NewXP) {
			break;
		}
	}
	
	if (Duration > 10000) Duration = 10000;
	
	return Duration;
}

main() {
	declare Frame_Rankings <=> (Page.GetFirstChild("frame-ranking") as CMlFrame);
	declare Frame_Score <=> (Page.GetFirstChild("frame-score") as CMlFrame);
	declare Label_Score <=> (Frame_Score.GetFirstChild("label-score") as CMlLabel);
	declare Frame_BestScores <=> (Page.GetFirstChild("frame-best-scores") as CMlFrame);
	declare Frame_Emblems <=> (Page.GetFirstChild("frame-emblem") as CMlFrame);
	declare Quad_CurrentEmblem <=> (Frame_Emblems.GetFirstChild("quad-current-emblem") as CMlQuad);
	declare Quad_NextEmblem <=> (Frame_Emblems.GetFirstChild("quad-next-emblem") as CMlQuad);
	declare Quad_ProgressBar <=> (Frame_Emblems.GetFirstChild("quad-progress-bar") as CMlQuad);
	declare Label_Points <=> (Frame_Emblems.GetFirstChild("label-points") as CMlLabel);
	declare Label_Gain <=> (Frame_Emblems.GetFirstChild("label-gain") as CMlLabel);
	
	declare netread Net_LibChanPro_RankingUpdate for Teams[0] = -1;
	declare netread Net_LibChanPro_RankingNames for Teams[0] = Text[];
	declare netread Net_LibChanPro_RankingScores for Teams[0] = Integer[];
	declare netread Net_LibChanPro_EmblemsLogoUpdate for Teams[0] = -1;
	declare netread Net_LibChanPro_EmblemsLogo for Teams[0] = Text[];
	declare netread Net_LibChanPro_EmblemsXPUpdate for Teams[0] = -1;
	declare netread Net_LibChanPro_EmblemsXP for Teams[0] = Integer[];
	
	declare PrevOwnerId = NullId;
	declare PrevRankingUpdate = -2;
	declare PrevScore = 0;
	declare PrevBestScoresUpdate = -2;
	declare PrevEmblemsLogoUpdate = -2;
	declare PrevEmblemsXPUpdate = -2;
	declare PrevXPUpdate = -2;
	
	declare XP = 0;
	declare XP_AnimStart = -1;
	declare XP_AnimBase = 0;
	declare XP_AnimChange = 0;
	declare XP_AnimDuration = 0;
	
	while (True) {
		yield;
		
		if (PrevRankingUpdate != Net_LibChanPro_RankingUpdate) {
			PrevRankingUpdate = Net_LibChanPro_RankingUpdate;
			UpdateRanking(Frame_Rankings, Net_LibChanPro_RankingNames, Net_LibChanPro_RankingScores);
		}
		
		if (
			PrevEmblemsLogoUpdate != Net_LibChanPro_EmblemsLogoUpdate ||
			PrevEmblemsXPUpdate!= Net_LibChanPro_EmblemsXPUpdate
		) {
			PrevEmblemsLogoUpdate = Net_LibChanPro_EmblemsLogoUpdate;
			PrevEmblemsXPUpdate = Net_LibChanPro_EmblemsXPUpdate;
			
			PrevOwnerId = NullId;
		}
		
		declare Owner <=> GetOwner();
		if (Owner != Null) {
			if (PrevOwnerId != Owner.Id) {
				PrevOwnerId = Owner.Id;
				PrevBestScoresUpdate = -2;
				PrevXPUpdate = -2;
			}
			
			if (Owner.Score != Null) {
				declare netread Net_LibChanPro_Score for Owner.Score = 0;
				
				if (PrevScore != Net_LibChanPro_Score) {
					PrevScore = Net_LibChanPro_Score;
					Label_Score.Value = TL::ToText(Net_LibChanPro_Score);
				}
				
				declare netread Net_LibChanPro_BestScoresUpdate for Owner.Score = -1;
				declare netread Net_LibChanPro_BestScores for Owner.Score = Integer[];
				if (PrevBestScoresUpdate != Net_LibChanPro_BestScoresUpdate) {
					PrevBestScoresUpdate = Net_LibChanPro_BestScoresUpdate;
					UpdateBestScores(Frame_BestScores, Net_LibChanPro_BestScores);
				}
				
				declare netread Net_LibChanPro_XPUpdate for Owner.Score = -1;
				declare netread Net_LibChanPro_OldXP for Owner.Score = 0;
				declare netread Net_LibChanPro_NewXP for Owner.Score = 0;
				if (PrevXPUpdate != Net_LibChanPro_XPUpdate) {
					PrevXPUpdate = Net_LibChanPro_XPUpdate;
					SetXPProgression(Net_LibChanPro_OldXP, Net_LibChanPro_NewXP, Label_Points, Label_Gain);
					
					XP_AnimStart = Now;
					XP_AnimBase = Net_LibChanPro_OldXP;
					XP_AnimChange = Net_LibChanPro_NewXP - Net_LibChanPro_OldXP;
					XP_AnimDuration = GetAnimDuration(Net_LibChanPro_EmblemsXP, Net_LibChanPro_OldXP, Net_LibChanPro_NewXP);
				}
			}
		}
		
		if (XP_AnimStart >= 0 && XP_AnimStart <= Now) {
			if (Now >= XP_AnimStart + XP_AnimDuration) {
				XP_AnimStart = -1;
				XP = XP_AnimBase + XP_AnimChange;
				SetXP(Net_LibChanPro_EmblemsXP, Net_LibChanPro_EmblemsLogo, XP, Quad_CurrentEmblem, Quad_NextEmblem, Quad_ProgressBar);
			} else {
				XP = ML::NearestInteger(AL::EaseOutQuad(Now - XP_AnimStart, XP_AnimBase * 1., XP_AnimChange *1., XP_AnimDuration));
				if (XP > XP_AnimBase + XP_AnimChange) {
					XP = XP_AnimBase + XP_AnimChange;
				}
				SetXP(Net_LibChanPro_EmblemsXP, Net_LibChanPro_EmblemsLogo, XP, Quad_CurrentEmblem, Quad_NextEmblem, Quad_ProgressBar);
			}
		}
	}
}
--></script>
</manialink>""";
}

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

// ---------------------------------- //
/** Update the players ranking
 *
 *	@param	_Names										The ordered names
 *	@param	_Scores										The ordered scores
 */
Void SetRanking(Text[] _Names, Integer[] _Scores) {
	declare netwrite Net_LibChanPro_RankingUpdate for Teams[0] = -1;
	declare netwrite Net_LibChanPro_RankingNames for Teams[0] = Text[];
	declare netwrite Net_LibChanPro_RankingScores for Teams[0] = Integer[];
	Net_LibChanPro_RankingNames = _Names;
	Net_LibChanPro_RankingScores = _Scores;
	Net_LibChanPro_RankingUpdate = Now;
}

// ---------------------------------- //
/** Update the player's score (points)
 *
 *	@param	_Score										The player's score
 *	@param	_Points										The player's points
 */
Void SetScore(CTmScore _Score, Integer _Points) {
	if (_Score == Null) return;
	
	declare netwrite Net_LibChanPro_Score for _Score = 0;
	Net_LibChanPro_Score = _Points;
}

// ---------------------------------- //
/** Update the player's best scores (points)
 *
 *	@param	_Score										The player's score
 *	@param	_BestPoints								The player's best points
 */
Void SetBestScores(CTmScore _Score, Integer[] _BestPoints) {
	if (_Score == Null) return;
	
	declare netwrite Net_LibChanPro_BestScoresUpdate for _Score = -1;
	declare netwrite Net_LibChanPro_BestScores for _Score = Integer[];
	Net_LibChanPro_BestScores = _BestPoints;
	Net_LibChanPro_BestScoresUpdate = Now;
}

// ---------------------------------- //
/** Update the channel cumulated XP per level
 *
 *	@param	_EmblemsXP								XP cumulated to reach each level
 */
Void SetEmblemsXP(Integer[] _EmblemsXP) {
	declare netwrite Net_LibChanPro_EmblemsXPUpdate for Teams[0] = -1;
	declare netwrite Net_LibChanPro_EmblemsXP for Teams[0] = Integer[];
	Net_LibChanPro_EmblemsXP = _EmblemsXP;
	Net_LibChanPro_EmblemsXPUpdate = Now;
}

// ---------------------------------- //
/** Update the emblems' logo
 *
 *	@param	EmblemsLogo								Path to the emblems' logo
 */
Void SetEmblemsLogo(Text[] _EmblemsLogo) {
	declare netwrite Net_LibChanPro_EmblemsLogoUpdate for Teams[0] = -1;
	declare netwrite Net_LibChanPro_EmblemsLogo for Teams[0] = Text[];
	Net_LibChanPro_EmblemsLogo = _EmblemsLogo;
	Net_LibChanPro_EmblemsLogoUpdate = Now;
}

// ---------------------------------- //
/** Update the player's channel XP
 *
 *	@param	_Score										The player's score
 *	@param	_OldXP										The old amount of XP
 *	@param	_NewXP										The new amount of XP
 */
Void SetXP(CTmScore _Score, Integer _OldXP, Integer _NewXP) {
	if (_Score == Null) return;
	
	declare netwrite Net_LibChanPro_XPUpdate for _Score = -1;
	declare netwrite Net_LibChanPro_OldXP for _Score = 0;
	declare netwrite Net_LibChanPro_NewXP for _Score = 0;
	Net_LibChanPro_OldXP = _OldXP;
	Net_LibChanPro_NewXP = _NewXP;
	Net_LibChanPro_XPUpdate = Now;
}

// ---------------------------------- //
/// Unload the library
Void Unload() {
	Layers::Destroy(C_LayerName);
	
	SetEmblemsXP(Integer[]);
	SetEmblemsLogo(Text[]);
	SetRanking(Text[], Integer[]);
	
	foreach (Score in Scores) {
		SetScore(Score, 0);
		SetBestScores(Score, Integer[]);
		SetXP(Score, 0, 0);
	}
}

// ---------------------------------- //
/// Load the library
Void Load() {
	Layers::Create(C_LayerName, Private_GetChannelProgressionML());
	//Layers::Attach(C_LayerName);
}