<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0" 
    xpath-default-namespace="http://www.ieee.org/11073/nomenclature"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema" 
	xmlns:fo="http://www.w3.org/1999/XSL/Format">
	<!--
	codeOutput.8g.xsl   2012-06-14T16
	Expand IEEE 11073 nomenclature partition of base terms that has zero or more discriminators.
	2009-01-14 Added support the "named" discriminator groups, e.g. _[CHAMBER] for IDC nomenclature.
	2009-05-01 Changed tag names to match version 8f; added "part" attribute to <xid> output elements.
	2009-05-02 Create a dtoken list (rather than subtree) to avoid namespace issues.  WORKS with no default IEEE namespace.
	2009-05-12 Added number(...) to several variables to work around XSLT type errors.
	2012-06-14 8g If PART="0" and CODE="0", "MDC_ECG_LEAD_CONFIG" is output (otherwise <null> is used instead of _CONFIG).
	Paul Schluter and Nate Stoddard, GE Healthcare
	- - -
	Outputs xml file containing expanded RefID strings and code values, e.g. <xid part="10" code="256">MDC_ECG_WAVC_PWAVE</xid>
	- - -
	Limitations:
	1.  The input file is a single IEEE P1073 nomenclature partition; batch processing of multiple partitions currently not supported.
	2.  Optimize (speed) by using a xsl:for-each to process least significant discriminator.
	- - -
	-->
	<xsl:output method="xml"/>
	<!-- Filename for discriminators (need to add wildcard support for aECG, IDC and other partitions -->
	<!-- <xsl:variable name="discrimFileName" select="'ECG_ANN_discrims.8f.xml'"/> -->
	<!-- <xsl:variable name="discrimFileName" select="'IDC_discrims.8f.xml'"/> -->
	<xsl:variable name="discrimFileName" select="'ECG_ANN_discrims.8g.xml'"/>
	<!-- root template -->
	<xsl:template match="/">
		<!-- -->
		<xsl:text>
			</xsl:text>
		<xsl:element name="terms">
			<!-- Find each <term> find a match -->
			<xsl:apply-templates select="/partition/Terms/term" mode="TermSearch"/>
		</xsl:element>
	</xsl:template>
	<!-- Find each term -->
	<xsl:template match="term" mode="TermSearch">
		<!-- -->
		<!-- Build a temporary dtokens list of the idref attribute value for each discriminator. -->
		<xsl:variable name="dtokens" as="xs:token*">
			<xsl:analyze-string select="./REFID" regex="(_\[(.+?)\])">	<!-- regex-group(2) will have desired discrim ID string -->
				<xsl:matching-substring>
					<xsl:sequence select="xs:token(regex-group(2))"/>   <!-- select substring inside [ ... ] -->
				</xsl:matching-substring>
			</xsl:analyze-string>
			<xsl:for-each select="/partition/partitionDescription/discriminatorRef">
				<xsl:sequence select="current()/@idref"/>
			</xsl:for-each>
		</xsl:variable>
		<!-- -->
		<!-- -->
		<xsl:choose>
			<xsl:when test="count($dtokens) = 0">
				<!-- Discriminators are not used for this term.  Just list code and ReferenceID. -->
				<xsl:element name="xid">
					<xsl:attribute name="part"><xsl:value-of select="(./PART, /partition/partitionDescription/basePart, '?')[1]"/></xsl:attribute>
					<xsl:attribute name="code"><xsl:value-of select="(./CODE10, '?')[1]"/></xsl:attribute>
					<xsl:value-of select="./REFID"/>
				</xsl:element>
				<!-- Output new line for readability -->
				<xsl:text>
				</xsl:text>
			</xsl:when>
			<!-- -->
			<xsl:otherwise>
				<!-- Discriminators are used with this term:  process "named" and "tail-end" discriminators for each term -->
				<xsl:call-template name="Discriminator">
					<xsl:with-param name="ptokens" as="xs:token*" select="$dtokens"/>
					<xsl:with-param name="counter" select="1"/>
					<xsl:with-param name="string" select="./REFID"/>
					<xsl:with-param name="part" select="(./PART, /partition/partitionDescription/basePart, '?')[1]"/>
					<xsl:with-param name="code" select="./CODE10"/>
					<xsl:with-param name="runningOffset" select="0"/>
					<xsl:with-param name="parentIndex" select="0"/>
					<xsl:with-param name="excludeList" select="concat(normalize-space(@exclude),' ')"/>
				</xsl:call-template>
			</xsl:otherwise>
		</xsl:choose>
	</xsl:template>
	<!-- -->
	<!-- Template:  Output a row for each Discriminator -->
	<!-- -->
	<xsl:template name="Discriminator">
		<xsl:param name="ptokens"/>
		<xsl:param name="counter"/>
		<xsl:param name="string"/>
		<xsl:param name="part"/>
		<xsl:param name="code"/>
		<xsl:param name="runningOffset"/>
		<xsl:param name="parentIndex"/>
		<xsl:param name="excludeList"/>
		<!-- Walk through all the Discriminators -->
		<xsl:for-each select="$ptokens">
			<xsl:if test="$counter = position()">
				<xsl:variable name="cIDREF" select="current()"/>									<!-- current discrim IDREF without underscore and braces -->
				<xsl:variable name="_cIDREF" select="concat( '_[', $cIDREF, ']')"/>			<!-- _[IDREF] with underscore and braces -->
				<xsl:variable name="rgx_cIDREF" select="concat( '_\[', $cIDREF, '\]')"/>	<!-- _[IDREF] as a regex target string  ("." in "[1..10]" ok ;-) -->
				<xsl:variable name="currentBits" select="number(document($discrimFileName)//discriminator[@id=current()]/dBits)"/>
				<xsl:variable name="currentSpan" select="number(subsequence(('1', '2', '4', '8', '16', '32', '64', '128', '256'), $currentBits+1, 1))"/>
				<xsl:for-each select="document($discrimFileName)//discriminator[@id=current()]/discrim">
					<xsl:variable name="suffix" select="dSuffix"/>
					<xsl:variable name="pos" select="dOffset"/>
					<!-- Some interesting math here 
							pos = the position of this Discriminator
							currentOffset = using all the parents offsets (index * span), plus the running offset from our direct parent,.
							thisOffset = the end offset of this subterm... uses the pos along with previously calculated offsets.
					-->
					<xsl:variable name="currentOffset" select="($currentSpan * (number($parentIndex) + number($runningOffset)))"/>
					<xsl:variable name="thisOffset" select="$currentOffset + number($pos) + 0"/>
					<!-- If this is the lowest level discriminator, then output XML, otherwise pass to the next level -->
					<xsl:choose>
						<xsl:when test="count($ptokens) = $counter">
							<!-- Here we have valid combination of a Reference ID and discriminator terms. -->
							<!-- Determine whether the Reference ID contains any excluded discriminator terms. -->
							<xsl:variable name="has-token">
								<xsl:call-template name="contains-token">
									<xsl:with-param name="refidString" select="if (contains($string, $_cIDREF)) then replace($string, $rgx_cIDREF, $suffix) else concat($string, $suffix)"/>
									<xsl:with-param name="discrimList" select="$excludeList"/>
								</xsl:call-template>
							</xsl:variable>
							<!-- If no excluded terms, then output the <xid> element containing the RefID and numeric code -->
							<xsl:if test="$has-token='false'">
								<xsl:element name="xid">
									<!-- Output partition number -->
									<xsl:attribute name="part"><xsl:value-of select="$part"/></xsl:attribute>
									<!-- Add the code for this term to the offset and output code attribute -->
									<xsl:attribute name="code"><xsl:value-of select="$thisOffset+$code"/></xsl:attribute>
									<!-- Output the REFID -->
									<xsl:choose>
										<xsl:when test="($part+$thisOffset+$code) = 0">
											<!-- Output 'MDC_ECG_LEAD_CONFIG' when PART and CODE10 = 0  (added as special case on 2012-06-14) -->
											<xsl:value-of select="'MDC_ECG_LEAD_CONFIG'"/>
										</xsl:when>
										<xsl:otherwise>
											<!-- Join the parent string with this subterm to create final REFID  (as typical case) -->
											<xsl:value-of select="if (contains($string, $_cIDREF)) then replace($string, $rgx_cIDREF, $suffix) else concat($string, $suffix)"/>									
										</xsl:otherwise>
									</xsl:choose>
								</xsl:element>
								<!-- Output new line for readability -->
								<xsl:text>
								</xsl:text>
							</xsl:if>
						</xsl:when>
						<xsl:otherwise>
							<!-- Call the template again, with the updated offsets, and joined strings -->
							<xsl:call-template name="Discriminator">
								<xsl:with-param name="ptokens" select="$ptokens"/>
								<xsl:with-param name="counter" select="$counter+1"/>
								<xsl:with-param name="string" select="if (contains($string, $_cIDREF)) then replace($string, $rgx_cIDREF, $suffix) else concat($string, $suffix)"/>
								<xsl:with-param name="part" select="$part"/>
								<xsl:with-param name="code" select="$code"/>
								<xsl:with-param name="runningOffset" select="$currentOffset"/>
								<xsl:with-param name="parentIndex" select="$pos"/>
								<xsl:with-param name="excludeList" select="$excludeList"/>
							</xsl:call-template>
						</xsl:otherwise>
					</xsl:choose>
				</xsl:for-each>
			</xsl:if>
		</xsl:for-each>
	</xsl:template>
	<!-- -->
	<!-- Template:  Test ReferenceID string to see if it contains any excluded discriminators -->
	<!-- -->
	<xsl:template name="contains-token">
		<xsl:param name="refidString"/>
		<xsl:param name="discrimList"/>
		<xsl:variable name="nlist" select="concat(normalize-space($discrimList), ' ')"/>
		<xsl:variable name="first" select="substring-before($nlist, ' ')"/>
		<xsl:variable name="rest" select="substring-after($nlist, ' ')"/>
		<xsl:choose>
			<xsl:when test="$first">
				<xsl:choose>
					<xsl:when test="contains(concat($refidString, '_'), concat($first, '_'))">true</xsl:when>
					<xsl:otherwise>
						<xsl:call-template name="contains-token">
							<xsl:with-param name="refidString" select="$refidString"/>
							<xsl:with-param name="discrimList" select="$rest"/>
						</xsl:call-template>
					</xsl:otherwise>
				</xsl:choose>
			</xsl:when>
			<xsl:otherwise>false</xsl:otherwise>
		</xsl:choose>
	</xsl:template>
	<!-- Fin -->
</xsl:stylesheet>
