<?xml version="1.0" encoding="utf-8"?>

<!--
*
*  From an XML file containing glider score information
*  produce an SVG file showing the progress of competitors.
*
*-->

<xsl:stylesheet
	version="2.0"
	xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
	xmlns:bmp="tag:edavies.nildram.co.uk,2006-05-23:bumps"
	exclude-result-prefixes="bmp">
	
<!--===============================================================================
*
*	Stylesheet overall setup.
*
*-->

<xsl:param name="scores" select="'cumulative'"/>				<!-- Scores to show, can be 
																	'cumulative' or 'daily'. -->

<xsl:param name="baseline" select="'yellow-jersey'"/>			<!-- Horizontal line of diagram,
																	 can be 'zero', 'day-winner',
																	 'yellow-jersey' or the ident
																	 of a glider. -->
																	 
<xsl:param name="width" select="1000"/>							<!-- Width of output in pixels. -->

<xsl:param name="height" select="800"/>							<!-- Height of output in pixels. -->

<xsl:param name="stylesheet" select="'bumps.css'"/>				<!-- Name of stylesheet to use. -->

<xsl:param name="linestyle" select="'bezier'"/>					<!-- Style of lines drawn between
																	 day scores for each glider. 
																	 Can be 'straight' or
																	 'bezier'. -->
	
<xsl:output
		method="xml" 
		encoding="utf-8"
		indent="yes" />
<!--	doctype-public="-//W3C//DTD SVG 1.0//EN"
		doctype-system="http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd"/> -->
	
<!--===============================================================================
*
*	Calculate the cumulative scores for each glider on each day, and find the
*	score to be shown for that glider on the day.
*
*	On call the context is a <results> element with no cumulative or shown attributes 
*	on the <glider-day> elements.  The result is the same <results> element with
*	those attributes added.
*
*-->
<xsl:template name="calc-cumulative-and-shown">

	<xsl:copy>

		<xsl:copy-of select="@*"/>
		<xsl:copy-of select="*[not(self::bmp:glider-day)]"/>
		
		<xsl:for-each select="bmp:glider-day">
			<xsl:copy>
				<xsl:copy-of select="@*"/>
				<xsl:variable name="ident" select="string(@ident)"/>
				<xsl:variable name="day-no" select="number(@day-no)"/>
				<xsl:variable name="cumulative"
							  select="sum(//bmp:glider-day[@ident=$ident][@day-no&lt;=$day-no]/@score)"/>
				<xsl:attribute name="cumulative" select="$cumulative"/>
				<xsl:attribute name="shown">
					<xsl:choose>
						<xsl:when test="$scores='daily'">
							<xsl:value-of select="number(@score)"/>
						</xsl:when>
						<xsl:when test="$scores='cumulative'">
							<xsl:value-of select="$cumulative"/>
						</xsl:when>
						<xsl:otherwise>
							<xsl:message terminate="yes">Don't understand scores parameter: '<xsl:value-of select="$scores"/>'</xsl:message>
						</xsl:otherwise>
					</xsl:choose>
				</xsl:attribute>
				<xsl:copy-of select="node()"/>
			</xsl:copy>
		</xsl:for-each>

	</xsl:copy>

</xsl:template>

	
<!--===============================================================================
*
*	Find the winners.
*
*	Mark the <glider-day> elements which are day winners and yellow jerseys with
*	attributes to indicate that they are.
*
*	On call the context is a <results> element with no day-winner or yellow-jersey
*	attributes on the <glider-day> elements.  Result is same but with those
*	attributes added.
*
*	Note that equal winners both get the attributes.
*
*-->
<xsl:template name="find-winners">

	<xsl:copy>

		<xsl:copy-of select="@*"/>
		<xsl:copy-of select="*[not(self::bmp:glider-day)]"/>
		
		<xsl:for-each select="bmp:glider-day">
			<xsl:copy>
				<xsl:copy-of select="@*"/>
				
				<xsl:variable name="day-no" select="number(@day-no)"/>
				<xsl:variable name="score" select="number(@score)"/>
				<xsl:variable name="cumulative" select="number(@cumulative)"/>
				
				<xsl:if test="count(//bmp:glider-day[@day-no=$day-no][@score>$score]) = 0">
					<xsl:attribute name="day-winner" select="'yes'"/>
				</xsl:if>

				<xsl:if test="count(//bmp:glider-day[@day-no=$day-no][@cumulative>$cumulative]) = 0">
					<xsl:attribute name="yellow-jersey" select="'yes'"/>
				</xsl:if>

				<xsl:copy-of select="node()"/>
			</xsl:copy>
		</xsl:for-each>

	</xsl:copy>

</xsl:template>


<!--===============================================================================
*
*	Find baseline.
*
*	Find the value of the shown score for each day which is on the baseline.
*
*-->
<xsl:template name="find-baseline">

	<xsl:copy>

		<xsl:copy-of select="@*"/>
		<xsl:copy-of select="*[not(self::bmp:day)]"/>
		
		<xsl:for-each select="bmp:day">
			<xsl:copy>
				<xsl:copy-of select="@*"/>
				
				<xsl:attribute name="baseline">
					<xsl:choose>
						<xsl:when test="$baseline='zero'">
							<xsl:value-of select="0"/>
						</xsl:when>
					
						<xsl:otherwise>
							<xsl:variable name="base-glider-day">
								<xsl:variable name="day-no" select="number(@day-no)"/>

								<xsl:choose>
									<xsl:when test="$baseline='day-winner'">
										<xsl:copy-of select="//bmp:glider-day[@day-no=$day-no][@day-winner='yes'][1]"/>
									</xsl:when>
								
									<xsl:when test="$baseline='yellow-jersey'">
										<xsl:copy-of select="//bmp:glider-day[@day-no=$day-no][@yellow-jersey='yes'][1]"/>
									</xsl:when>
									
									<xsl:otherwise>
										<xsl:copy-of select="//bmp:glider-day[@day-no=$day-no][@ident=$baseline][1]"/>
									</xsl:otherwise>
								</xsl:choose>
							</xsl:variable>
							
							<xsl:if test="count($base-glider-day//@shown) != 1">
								<xsl:message>Can't find baseline value for day <xsl:value-of select="@day-no"/></xsl:message>
							</xsl:if>
							
							<xsl:value-of select="$base-glider-day//@shown"/>
							
						</xsl:otherwise>
					</xsl:choose>
				</xsl:attribute>

				<xsl:copy-of select="node()"/>
			</xsl:copy>
		</xsl:for-each>

	</xsl:copy>

</xsl:template>


<!--===============================================================================
*
*	Find offsets for each glider-day from the baseline value for that day.
*
*-->
<xsl:template name="find-offsets">

	<xsl:copy>

		<xsl:copy-of select="@*"/>
		<xsl:copy-of select="*[not(self::bmp:glider-day)]"/>
		
		<xsl:for-each select="bmp:glider-day">
			<xsl:copy>
				<xsl:copy-of select="@*"/>
				
				<xsl:variable name="day-no" select="number(@day-no)"/>
				<xsl:variable name="baseline" select="number(//bmp:day[@day-no=$day-no]/@baseline)"/>
				
				<xsl:attribute name="offset">
					<xsl:value-of select="number(@shown) - $baseline"/>
				</xsl:attribute>

				<xsl:copy-of select="node()"/>
			</xsl:copy>
		</xsl:for-each>

	</xsl:copy>

</xsl:template>


<!--===============================================================================
*
*	Find the final scores for each of the gliders.
*
*	That is, the scores we show for each glider on the last day for which they get
*	a score.
*
*-->
<xsl:template name="find-final-scores">

	<xsl:copy>

		<xsl:copy-of select="@*"/>
		<xsl:copy-of select="*[not(self::bmp:glider)]"/>
		
		<xsl:for-each select="bmp:glider">
			<xsl:copy>
				<xsl:copy-of select="@*"/>
				
				<xsl:variable name="ident" select="string(@ident)"/>
				
				<xsl:variable name="days-backwards">
					<xsl:for-each select="//bmp:glider-day[@ident=$ident]">
						<xsl:sort select="@day-no" order="descending" data-type="number"/>
						<xsl:copy-of select="."/>
					</xsl:for-each>
				</xsl:variable>
				
				<xsl:attribute name="final-offset" select="$days-backwards//bmp:glider-day[1]/@offset"/>

				<xsl:copy-of select="node()"/>
			</xsl:copy>
		</xsl:for-each>

	</xsl:copy>

</xsl:template>


<!--===============================================================================
*
*	Find the scored positions of each of the gliders.
*
*	Gliders with equal scores are in the same position - e.g., if two have the
*	highest score then they are equal first.
*
*-->
<xsl:template name="find-scored-positions">

	<xsl:copy>

		<xsl:copy-of select="@*"/>
		<xsl:copy-of select="*[not(self::bmp:glider)]"/>
		
		<xsl:for-each select="bmp:glider">
			<xsl:copy>
				<xsl:copy-of select="@*"/>
				
				<xsl:variable name="final-offset" select="@final-offset"/>
				
				<xsl:attribute name="scored-position"
					select="count(//bmp:glider[number(@final-offset) > $final-offset]) + 1"/>
				
				<xsl:copy-of select="node()"/>
			</xsl:copy>
		</xsl:for-each>

	</xsl:copy>

</xsl:template>


<!--===============================================================================
*
*	Find the displayed positions for each of the gliders.
*
*	These are the same as the scored positions found above but tie broken on
*	glider number.
*
*-->
<xsl:template name="find-displayed-positions">

	<xsl:copy>

		<xsl:copy-of select="@*"/>
		<xsl:copy-of select="*[not(self::bmp:glider)]"/>
		
		<xsl:for-each select="bmp:glider">
			<xsl:copy>
				<xsl:copy-of select="@*"/>
				
				<xsl:variable name="scored-position" select="@scored-position"/>
				<xsl:variable name="ident" select="@ident"/>
				
				<xsl:attribute name="displayed-position"
					select="$scored-position +
							count(//bmp:glider[@scored-position=$scored-position][@ident &lt; $ident])"/>
				
				<xsl:copy-of select="node()"/>
			</xsl:copy>
		</xsl:for-each>

	</xsl:copy>

</xsl:template>


<!--===============================================================================
*
*	Find statistics, particular max and min values, etc.
*
*-->
<xsl:template name="find-stats">

	<xsl:copy>
	
		<xsl:copy-of select="@*|node()"/>

		<xsl:variable name="glider-days-by-offset">
			<xsl:for-each select="//bmp:glider-day">
				<xsl:sort select="@offset" order="ascending" data-type="number"/>
				<xsl:copy-of select="."/>
			</xsl:for-each>
		</xsl:variable>
	
		<bmp:min-offset>
			<xsl:value-of select="number($glider-days-by-offset/*[1]/@offset)"/>
		</bmp:min-offset>
	
		<bmp:max-offset>
			<xsl:value-of select="number($glider-days-by-offset/*[last()]/@offset)"/>
		</bmp:max-offset>
		
		<xsl:variable name="days-by-number">
			<xsl:for-each select="//bmp:day">
				<xsl:sort select="@day-no" order="ascending" data-type="number"/>
				<xsl:copy-of select="."/>
			</xsl:for-each>
		</xsl:variable>
		
		<bmp:last-day>
			<xsl:value-of select="$days-by-number/*[last()]/@day-no"/>
		</bmp:last-day>
		
	</xsl:copy>

</xsl:template>


<!--===============================================================================
*
*	Produce the ouput.
*
*-->
<xsl:template name="produce-output">

	<!--
	*
	*  Work out the horizontal layout information.
	*
	*  Actually, we work out the width of the drawing in units of the width of
	*  one day's column then work out the column width in pixels.
	*
	*-->
	<xsl:variable name="day-col-count" select="number(//bmp:last-day)"/>
	<xsl:variable name="id-col-width" select="1.2"/>
	<xsl:variable name="gutter-width" select="0.25"/>
	<xsl:variable name="vertical-margin-width" select="0.05"/>
	<xsl:variable name="total-width" select="$day-col-count +
											 ($day-col-count - 1) * $gutter-width +
											 $gutter-width +
											 $id-col-width +
											 $vertical-margin-width * 2"/>
	<xsl:variable name="col-width" select="$width div $total-width"/>
	<xsl:variable name="bezier-offset" select="$col-width * 0.7"/>
	
	<!--
	*
	*  Work out the vertical layout information.
	*
	*-->
	<xsl:variable name="header-height" select="$height * 0.15"/>
	<xsl:variable name="chart-start-row" select="$header-height + ($height div 30)"/>
	<xsl:variable name="chart-end-row" select="$height - ($height div 30)"/>
	<xsl:variable name="chart-range" select="$chart-start-row - $chart-end-row"/>
	<xsl:variable name="min-offset" select="number(//bmp:min-offset)"/>
	<xsl:variable name="max-offset" select="number(//bmp:max-offset)"/>
	<xsl:variable name="pixels-per-point" select="$chart-range div ($max-offset - $min-offset)"/>
	<xsl:variable name="baseline-row" select="$chart-end-row  - ($min-offset * $pixels-per-point)"/>
	<xsl:variable name="pixels-per-label" select="$chart-range div (count(//bmp:glider) - 1)"/>
	
	<!--
	*
	*  Actually produce the output.
	*
	*-->
	<xsl:processing-instruction name="xml-stylesheet">
		<xsl:text>href="</xsl:text><xsl:value-of select="$stylesheet"/><xsl:text>" type="text/css"</xsl:text>
	</xsl:processing-instruction>

	<svg:svg
		width="{$width}px" 
		height="{$height}px" 
		preserveAspectRatio="xMinYMin meet" 
		xmlns:svg="http://www.w3.org/2000/svg">
		
		<!-- Copy of the input data plus computed values. -->
		<xsl:copy-of select="/"/>
		
		<!-- Border. -->
		<svg:path d="M 0 0 L {$width - 1} 0 L {$width - 1} {$height - 1} L 0 {$height - 1} L 0 0" class="border"/>
		
		<!-- Competition title. -->
		<svg:text x="{$width div 2}" y="{$header-height * 0.4}" style="text-anchor: middle;" class="comp-title">
			<xsl:value-of select="//bmp:results/@comp"/>
		</svg:text>
		
		<!--Parameter labels. -->
		<svg:text x="{$width - $col-width * $vertical-margin-width}" 
				  y="{$header-height * 0.6}" 
				  style="text-anchor: end;" 
				  class="parameter-label">
			<xsl:text>Scores: </xsl:text>
			<xsl:value-of select="$scores"/>
		</svg:text>
		
		<svg:text x="{$width - $col-width * $vertical-margin-width}" 
				  y="{$header-height * 0.8}" 
				  style="text-anchor: end;" 
				  class="parameter-label">
			<xsl:text>Baseline: </xsl:text>
			<xsl:value-of select="$baseline"/>
		</svg:text>
		
		<!-- Information for each of the days. -->		 
		<xsl:for-each select="//bmp:day">
			<!-- Find centre of column for day. -->
			<xsl:variable name="x" select="$col-width * ($vertical-margin-width + (@day-no - 1) * (1 + $gutter-width) + 0.5)"/>
		
			<!-- Vertical line. -->
		 	<svg:line x1="{$x}" y1="{$chart-start-row}" x2="{$x}" y2="{$chart-end-row}" class="day-line"/>
		 	
		 	<!-- Day number. -->
		 	<svg:text x="{$x}" y="{$header-height * 0.75}" style="text-anchor: middle;" class="day-number">
		 		<xsl:text>Day </xsl:text>
		 		<xsl:value-of select="@day-no"/>
		 	</svg:text>

		 	<!-- Date. -->
		 	<svg:text x="{$x}" y="{$header-height * 0.95}" style="text-anchor: middle;" class="day-date">
		 		<xsl:value-of select="@date"/>
		 	</svg:text>

		</xsl:for-each>
		
		<!-- Information for each of the gliders. -->
		<xsl:for-each select="//bmp:glider">
		
			<xsl:variable name="ident" select="string(@ident)"/>
			<xsl:variable name="final-offset" select="number(@final-offset)"/>
			<xsl:variable name="class" select="concat('glider', @displayed-position)"/>
			
			<!-- Find the data for each of the days for the glider, in order. -->
			<xsl:variable name="days">
				<xsl:for-each select="//bmp:glider-day[@ident=$ident]">
					<xsl:sort select="@day-no" order="ascending" data-type="number"/>
					<xsl:copy-of select="."/>
				</xsl:for-each>
			</xsl:variable>
			
			<!-- Draw the lines between pairs of consecutive days. -->
			<xsl:for-each select="$days/bmp:glider-day">
			 	<xsl:variable name="x1" select="$col-width * ($vertical-margin-width + (@day-no - 1) * (1 + $gutter-width) + 0.5)"/>
			 	<xsl:variable name="y1" select="$chart-end-row + (number(@offset) - $min-offset) * $pixels-per-point"/>
			 	<xsl:for-each select="following-sibling::*[1]">
				 	<xsl:variable name="x2" select="$col-width * ($vertical-margin-width + (@day-no - 1) * (1 + $gutter-width) + 0.5)"/>
				 	<xsl:variable name="y2" select="$chart-end-row + (number(@offset) - $min-offset) * $pixels-per-point"/>
				 	
				 	<xsl:choose>
				 		<xsl:when test="$linestyle='straight'">
						 	<svg:line x1="{$x1}" y1="{$y1}" x2="{$x2}" y2="{$y2}" class="{$class} glider-score-line"/>
						</xsl:when>
						
						<xsl:when test="$linestyle='bezier'">
							<svg:path 
								d="M {$x1} {$y1} C {$x1 + $bezier-offset} {$y1}, {$x2 - $bezier-offset} {$y2}, {$x2} {$y2}"
								class="{$class} glider-score-line"/>
						</xsl:when>
						
						<xsl:otherwise>
							<xsl:message terminate="yes">Unknown linestyle</xsl:message>
						</xsl:otherwise>
				 	</xsl:choose>
				 </xsl:for-each>
			</xsl:for-each>
			
			<!-- Glider label on RHS. -->
			<xsl:variable name="last-score-x"
						  select="$col-width * ($vertical-margin-width + ($days/bmp:glider-day[last()]/@day-no - 1) * (1 + $gutter-width) + 0.5)"/>
			<xsl:variable name="last-score-y" 
						  select="$chart-end-row + (number(@final-offset) - $min-offset) * $pixels-per-point"/>
			<xsl:variable name="label-x"
						  select="$width - $col-width * ($vertical-margin-width + $id-col-width + $gutter-width + 0.25)"/>
			<xsl:variable name="label-y"
						  select="$chart-start-row - (@displayed-position - 1) * $pixels-per-label"/>
			<svg:text x="{$label-x}"
					  y="{$label-y}"
					  style="text-anchor: start;"
					  class="{$class} glider-label">
				<xsl:value-of select="@scored-position"/>
				<xsl:text> - </xsl:text>
				<xsl:value-of select="@ident"/>
				<xsl:text> - </xsl:text>
				<xsl:value-of select="@pilot"/>
			</svg:text>
			
			<svg:line x1="{$last-score-x}" y1="{$last-score-y}" x2="{$label-x}" y2="{$label-y}" class="{$class} glider-score-label-line"/>
		
		</xsl:for-each>

	</svg:svg>

</xsl:template>


<!--===============================================================================
*
*	Root template.
*
*-->
<xsl:template match="/">

	<xsl:variable name="step10">
		<xsl:for-each select="//bmp:results">
			<xsl:call-template name="calc-cumulative-and-shown"/>
		</xsl:for-each>
	</xsl:variable>
	
	<xsl:variable name="step20">
		<xsl:for-each select="$step10//bmp:results">
			<xsl:call-template name="find-winners"/>
		</xsl:for-each>
	</xsl:variable>
	
	<xsl:variable name="step30">
		<xsl:for-each select="$step20//bmp:results">
			<xsl:call-template name="find-baseline"/>
		</xsl:for-each>
	</xsl:variable>
	
	<xsl:variable name="step40">
		<xsl:for-each select="$step30//bmp:results">
			<xsl:call-template name="find-offsets"/>
		</xsl:for-each>
	</xsl:variable>

	<xsl:variable name="step50">
		<xsl:for-each select="$step40//bmp:results">
			<xsl:call-template name="find-final-scores"/>
		</xsl:for-each>
	</xsl:variable>

	<xsl:variable name="step60">
		<xsl:for-each select="$step50//bmp:results">
			<xsl:call-template name="find-stats"/>
		</xsl:for-each>
	</xsl:variable>

	<xsl:variable name="step70">
		<xsl:for-each select="$step60//bmp:results">
			<xsl:call-template name="find-scored-positions"/>
		</xsl:for-each>
	</xsl:variable>

	<xsl:variable name="step80">
		<xsl:for-each select="$step70//bmp:results">
			<xsl:call-template name="find-displayed-positions"/>
		</xsl:for-each>
	</xsl:variable>

	<xsl:for-each select="$step80//bmp:results">
		<xsl:call-template name="produce-output"/>
	</xsl:for-each>
	
	<xsl:text>
</xsl:text>

</xsl:template>

</xsl:stylesheet>
