<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                xmlns:db="http://docbook.org/ns/docbook"
                xmlns:dbe="http://docbook.org/ns/docbook/errors"
                xmlns:err='http://www.w3.org/2005/xqt-errors'
                xmlns:ext="http://docbook.org/extensions/xslt"
                xmlns:f="http://docbook.org/ns/docbook/functions"
                xmlns:fp="http://docbook.org/ns/docbook/functions/private"
                xmlns:ghost="http://docbook.org/ns/docbook/ephemeral"
                xmlns:h="http://www.w3.org/1999/xhtml"
                xmlns:m="http://docbook.org/ns/docbook/modes"
                xmlns:mp="http://docbook.org/ns/docbook/modes/private"
                xmlns:map="http://www.w3.org/2005/xpath-functions/map"
                xmlns:t="http://docbook.org/ns/docbook/templates"
                xmlns:tp="http://docbook.org/ns/docbook/templates/private"
                xmlns:v="http://docbook.org/ns/docbook/variables"
                xmlns:vp="http://docbook.org/ns/docbook/variables/private"
                xmlns:xi='http://www.w3.org/2001/XInclude'
                xmlns:xs="http://www.w3.org/2001/XMLSchema"
                xmlns="http://www.w3.org/1999/xhtml"
                exclude-result-prefixes="#all"
                version="3.0">

<!-- This will all be in XProc 3.0 eventually, hack for now... -->
<xsl:import href="main.xsl"/>

<!--
<xsl:mode xmlns:saxon="http://saxon.sf.net/"
          name="m:docbook" saxon:trace="yes"/>
-->

<xsl:variable name="v:standard-transforms" as="map(*)*">
  <xsl:map>
    <xsl:map-entry key="'stylesheet-location'"
                   select="resolve-uri('transforms/00-logstruct.xsl', static-base-uri())"/>
  </xsl:map>
  <xsl:map>
    <xsl:map-entry key="'stylesheet-location'"
                   select="resolve-uri('transforms/10-xinclude.xsl', static-base-uri())"/>
    <xsl:map-entry key="'functions'" select="'Q{http://docbook.org/extensions/xslt}xinclude'"/>
    <xsl:map-entry key="'test'" select="'exists(//xi:include)'"/>
  </xsl:map>
  <xsl:map>
    <xsl:map-entry key="'stylesheet-location'"
                   select="resolve-uri('transforms/20-db4to5.xsl', static-base-uri())"/>
    <xsl:map-entry key="'test'">
      not(namespace-uri(/*) = 'http://docbook.org/ns/docbook')
    </xsl:map-entry>
    <xsl:map-entry key="'extra-params'"
                   select="map { QName('', 'base-uri'): 'base-uri(/*)' }"/>
  </xsl:map>
  <xsl:map>
    <xsl:map-entry key="'stylesheet-location'"
                   select="resolve-uri('transforms/30-transclude.xsl', static-base-uri())"/>
    <xsl:map-entry key="'test'" select="'f:is-true($docbook-transclusion)'"/>
  </xsl:map>
  <xsl:map>
    <xsl:map-entry key="'stylesheet-location'"
                   select="resolve-uri('transforms/40-profile.xsl', static-base-uri())"/>
    <xsl:map-entry key="'test'">
         f:is-true($dynamic-profiles)
      or $profile-lang != ''         or $profile-revisionflag != ''
      or $profile-role != ''         or $profile-arch != ''
      or $profile-audience != ''     or $profile-condition != ''
      or $profile-conformance != ''  or $profile-os != ''
      or $profile-outputformat != '' or $profile-revision != ''
      or $profile-security != ''     or $profile-userlevel != ''
      or $profile-vendor != ''       or $profile-wordsize != ''
    </xsl:map-entry>
  </xsl:map>
  <xsl:map>
    <xsl:map-entry key="'stylesheet-location'"
                   select="resolve-uri('transforms/50-normalize.xsl', static-base-uri())"/>
  </xsl:map>
  <xsl:map>
    <xsl:map-entry key="'stylesheet-location'"
                   select="resolve-uri('transforms/60-annotations.xsl', static-base-uri())"/>
    <xsl:map-entry key="'test'" select="'exists(//db:annotation)'"/>
  </xsl:map>
  <xsl:map>
    <xsl:map-entry key="'stylesheet-location'"
                   select="resolve-uri('transforms/70-xlinkbase.xsl', static-base-uri())"/>
  </xsl:map>
  <xsl:if test="exists($local-conventions)">
    <xsl:map>
      <xsl:map-entry key="'stylesheet-location'" select="$local-conventions"/>
    </xsl:map>
  </xsl:if>
  <xsl:map>
    <xsl:map-entry key="'stylesheet-location'"
                   select="resolve-uri('transforms/75-validate.xsl', static-base-uri())"/>
    <xsl:map-entry key="'functions'"
                   select="'Q{http://docbook.org/extensions/xslt}validate-with-relax-ng'"/>
    <xsl:map-entry key="'test'"
                   select="'normalize-space($relax-ng-grammar) != '''''"/>
  </xsl:map>
  <xsl:map>
    <xsl:map-entry key="'stylesheet-location'"
                   select="resolve-uri('transforms/80-oxy-markup.xsl', static-base-uri())"/>
    <xsl:map-entry key="'test'">
      f:is-true(f:pi(/*/db:info, 'oxy-markup', $oxy-markup))
      and exists(//processing-instruction()[starts-with(name(), 'oxy_')])
    </xsl:map-entry>
  </xsl:map>
</xsl:variable>

<xsl:variable name="vp:transforms" as="map(*)*">
  <xsl:for-each select="$transform-original">
    <xsl:choose>
      <xsl:when test=". instance of map(*)">
        <xsl:sequence select="."/>
      </xsl:when>
      <xsl:when test=". instance of xs:string or . instance of xs:untypedAtomic">
        <xsl:sequence select="map {
            'stylesheet-location': resolve-uri(., static-base-uri())
          }"/>
      </xsl:when>
      <xsl:otherwise>
        <xsl:sequence select="error($dbe:INVALID-TRANSFORM, 
                                    'Each $transform-original must be a string or a map')"/>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:for-each>

  <xsl:sequence select="$v:standard-transforms"/>

  <xsl:for-each select="$transform-before">
    <xsl:choose>
      <xsl:when test=". instance of map(*)">
        <xsl:sequence select="."/>
      </xsl:when>
      <xsl:when test=". instance of xs:string or . instance of xs:untypedAtomic">
        <xsl:sequence select="map {
            'stylesheet-location': resolve-uri(., static-base-uri())
          }"/>
      </xsl:when>
      <xsl:otherwise>
        <xsl:sequence select="error($dbe:INVALID-TRANSFORM, 
                                    'Each $transform-preprocessed must be a string or a map')"/>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:for-each>
</xsl:variable>

<!-- If a document or element is being processed in the default
     mode (and not the m:docbook mode), assume we're starting 
     a transformation. -->
<xsl:template match="/">
  <xsl:choose>
    <!-- Hack: if the source is a localization source document, transform
         it into a localization document. -->
    <xsl:when xmlns:ls="http://docbook.org/ns/docbook/l10n/source"
              test="/ls:locale">
      <xsl:result-document method="xml" indent="yes">
        <xsl:apply-templates select="/ls:locale" mode="mp:transform-locale"/>
      </xsl:result-document>
    </xsl:when>
    <xsl:otherwise>
      <xsl:call-template name="t:docbook"/>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

<xsl:template name="t:docbook">
  <xsl:param name="vp:loop-count" select="0" tunnel="yes"/>
  <xsl:param name="return" as="xs:string" select="'main-document'"/>

  <xsl:if test="$vp:loop-count gt 0">
    <xsl:message terminate="yes">
      <xsl:text>Loop detected, perhaps a mode is missing?</xsl:text>
    </xsl:message>
  </xsl:if>

  <xsl:variable name="starting-base-uri" as="xs:string">
    <xsl:choose>
      <xsl:when test="true()" use-when="function-available('ext:cwd')">
        <xsl:sequence select="resolve-uri(base-uri(.), ext:cwd())"/>
      </xsl:when>
      <xsl:when test="true()">
        <xsl:sequence select="resolve-uri(base-uri(.), static-base-uri())"/>
      </xsl:when>
    </xsl:choose>
  </xsl:variable>
  
  <!--
  <xsl:message select="'Starting base uri:', $starting-base-uri"/>
  -->

  <xsl:variable name="document" as="document-node()">
    <xsl:choose>
      <xsl:when test="./self::document-node()">
        <xsl:sequence select="."/>
      </xsl:when>
      <xsl:otherwise>
        <xsl:document>
          <xsl:sequence select="."/>
        </xsl:document>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:variable>
  
  <xsl:variable name="pre-processing-result" as="map(*)" select="
      fp:run-transforms($document, $vp:transforms,
      map {
        xs:QName('vp:starting-base-uri'): $starting-base-uri,
        xs:QName('vp:current-output-uri'):current-output-uri()
      })"/>
  
  <xsl:call-template name="tp:write-secondary-documents">
    <xsl:with-param name="transform-result" select="$pre-processing-result"/>
  </xsl:call-template>

  <xsl:variable name="document" as="document-node()">
    <xsl:sequence select="$pre-processing-result?output"
        />
  </xsl:variable>

  <xsl:if test="string($transformed-docbook-input) != ''">
    <xsl:try>
      <xsl:variable name="href" 
                    select="resolve-uri($transformed-docbook-input, base-uri(/))"/>
      <xsl:result-document href="{$href}" method="xml" indent="no"
                           exclude-result-prefixes="#all">
        <xsl:sequence select="$document"/>
      </xsl:result-document>
      <xsl:if test="$v:debug = 'intermediate-results'">
        <xsl:message select="'Transformed DocBook input saved:', $href"/>
      </xsl:if>
      <xsl:catch>
        <xsl:message select="'Failed to save transformed input:', $transformed-docbook-input"/>
        <xsl:message select="$err:description"/>
      </xsl:catch>
    </xsl:try>
  </xsl:if>

  <xsl:variable name="transformed-html" as="document-node()">
    <xsl:apply-templates select="$document" mode="m:docbook">
      <xsl:with-param name="vp:loop-count" select="1" tunnel="yes"/>
    </xsl:apply-templates>
  </xsl:variable>

  <xsl:if test="string($transformed-docbook-output) != ''">
    <xsl:try>
      <xsl:variable name="href" 
                    select="resolve-uri($transformed-docbook-output, base-uri(/))"/>
      <xsl:result-document href="{$href}" method="xml" indent="no"
                           exclude-result-prefixes="#all">
        <xsl:sequence select="$transformed-html"/>
      </xsl:result-document>
      <xsl:if test="$v:debug = 'intermediate-results'">
        <xsl:message select="'Transformed DocBook output saved:', $href"/>
      </xsl:if>
      <xsl:catch>
        <xsl:message select="'Failed to save transformed output:', $transformed-docbook-output"/>
        <xsl:message select="$err:description"/>
      </xsl:catch>
    </xsl:try>
  </xsl:if>

  <xsl:variable name="result" as="document-node()">
    <xsl:call-template name="t:chunk-cleanup">
      <xsl:with-param name="docbook" select="$document"/>
      <xsl:with-param name="source" select="$transformed-html"/>
    </xsl:call-template>
  </xsl:variable>

  <xsl:variable name="post-processing" as="map(*)*">
    <xsl:for-each select="$transform-after">
      <xsl:choose>
        <xsl:when test=". instance of map(*)">
          <xsl:sequence select="."/>
        </xsl:when>
        <xsl:when test=". instance of xs:string or . instance of xs:untypedAtomic">
          <xsl:sequence select="map {
                                  'stylesheet-location': resolve-uri(., static-base-uri())
                                }"/>
        </xsl:when>
        <xsl:otherwise>
          <xsl:sequence select="error($dbe:INVALID-TRANSFORM, 
                                      'Each $transform-preprocessed must be a string or a map')"/>
        </xsl:otherwise>
      </xsl:choose>
    </xsl:for-each>
  </xsl:variable>
  
  <xsl:variable name="post-processing-result" as="map(*)" select="
      fp:run-transforms($result, $post-processing, map {
        xs:QName('vp:current-output-uri'): current-output-uri()
      }
      )"/>
  
  <xsl:call-template name="tp:write-secondary-documents">
    <xsl:with-param name="transform-result" select="$post-processing-result"/>
  </xsl:call-template>

  <xsl:variable name="result" as="document-node()">
    <xsl:apply-templates select="$post-processing-result?output" mode="mp:remove-ghosts"/>
  </xsl:variable>

  <xsl:choose>
    <xsl:when test="$return = 'raw-results'">
      <xsl:sequence select="map {
          'document': $document,
          'output': $result
        }"/>
    </xsl:when>
    <xsl:when test="$return = 'chunked-results'">
      <xsl:variable name="chunks" as="map(xs:string, item()*)">
        <xsl:call-template name="t:chunk-output">
          <xsl:with-param name="docbook" select="$document"/>
          <xsl:with-param name="source" select="$result"/>
        </xsl:call-template>
      </xsl:variable>
      <xsl:sequence select="map {
          'document': $document,
          'chunks': $chunks
        }"/>
    </xsl:when>
    <xsl:when test="$return = 'main-document'">
      <xsl:variable name="result" as="map(xs:string, item()*)">
        <xsl:call-template name="t:chunk-output">
          <xsl:with-param name="docbook" select="$document"/>
          <xsl:with-param name="source" select="$result"/>
        </xsl:call-template>
      </xsl:variable>

      <xsl:for-each select="map:keys($result)">
        <xsl:if test=". != 'output'">
          <xsl:apply-templates select="map:get($result, .)" mode="m:chunk-write">
            <xsl:with-param name="href" select="."/>
          </xsl:apply-templates>
        </xsl:if>
      </xsl:for-each>

      <xsl:choose>
        <xsl:when test="not($result?output/h:html)">
          <xsl:sequence select="$result?output"/>
        </xsl:when>
        <xsl:when test="f:is-true($generate-html-page)">
          <xsl:sequence select="$result?output"/>
        </xsl:when>
        <xsl:otherwise>
          <xsl:sequence select="$result?output/h:html/h:body/node()
                                except $result?output/h:html/h:body/h:script"/>
        </xsl:otherwise>
      </xsl:choose>
    </xsl:when>
    <xsl:otherwise>
        <xsl:sequence select="error($dbe:INVALID-RESULTS-REQUESTED,
                                    'Unexepcted return: ' || $return)"/>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

<xsl:template name="t:chunk-cleanup" as="document-node()">
  <xsl:param name="source" as="document-node()" select="."/>
  <xsl:param name="docbook" as="document-node()" required="yes"/>

  <xsl:apply-templates select="$source" mode="m:chunk-cleanup">
    <xsl:with-param name="docbook" select="$docbook" tunnel="yes"/>
  </xsl:apply-templates>
</xsl:template>

<xsl:template name="t:chunk-output" as="map(xs:string, item()*)">
  <xsl:param name="source" as="document-node()" select="."/>
  <xsl:param name="docbook" as="document-node()" required="yes"/>

  <xsl:apply-templates select="$source" mode="m:chunk-output">
    <xsl:with-param name="docbook" select="$docbook" tunnel="yes"/>
  </xsl:apply-templates>
</xsl:template>

<xsl:template match="node()" mode="m:chunk-write">
  <xsl:param name="href" as="xs:string" required="yes"/>

  <xsl:result-document href="{$href}">
    <xsl:choose>
      <xsl:when test="not(self::h:html)">
        <!-- If this happens not to be an h:html element, just output it. -->
        <xsl:sequence select="."/>
      </xsl:when>
      <xsl:when test="f:is-true($generate-html-page)">
        <!-- If this is an h:html element, and generate-html-page is true,
             output just output it. -->
        <xsl:sequence select="."/>
      </xsl:when>
      <xsl:otherwise>
        <!-- We got an h:html, but the user has requested 'raw' output.
             Attempt to strip out the generated html page wrapper. -->
        <xsl:sequence select="h:body/node() except h:body/h:script"/>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:result-document>
</xsl:template>

<xsl:function name="fp:run-transforms" as="map(*)">
  <xsl:param name="document" as="document-node()"/>
  <xsl:param name="transforms" as="map(*)*"/>
  <xsl:sequence select="fp:run-transforms($document, $transforms, ())"/>
</xsl:function>

<xsl:function name="fp:run-transforms" as="map(*)">
  <xsl:param name="document" as="document-node()"/>
  <xsl:param name="transforms" as="map(*)*"/>
  <xsl:param name="extra-parameters" as="map(*)?"/>

  <xsl:choose>
    <xsl:when test="empty($transforms)">
      <xsl:sequence select="map {'output': $document}"/>
    </xsl:when>
    <xsl:otherwise>
      <xsl:iterate select="$transforms">
        <xsl:param name="document" as="document-node()" select="$document"/>
        <xsl:param name="secondary-documents" as="map(*)" select="map {}"/>
        <xsl:param name="extra-parameters" as="map(*)?" select="$extra-parameters"/>
        <xsl:on-completion select="
            let $main := map {'output': $document}
            return
              map:merge(($main, $secondary-documents))"/>

        <xsl:variable name="next-result" as="map(*)?">

          <xsl:variable name="functions" as="xs:boolean*">
            <xsl:for-each select=".?functions">
              <xsl:sequence select="function-available(.)"/>
            </xsl:for-each>
          </xsl:variable>

          <xsl:variable name="process" as="xs:boolean">
            <xsl:choose>
              <xsl:when test="exists($functions) and false() = $functions">
                <xsl:sequence select="false()"/>
              </xsl:when>
              <xsl:when test="exists(.?test)">
                <xsl:evaluate xpath=".?test" as="xs:boolean" with-params="$vp:dynamic-parameters"
                  context-item="$document"/>
              </xsl:when>
              <xsl:otherwise>
                <xsl:sequence select="true()"/>
              </xsl:otherwise>
            </xsl:choose>
          </xsl:variable>

          <xsl:choose>
            <xsl:when test="exists($functions) and false() = $functions">
              <xsl:message use-when="'pipeline' = $v:debug"
                select="'Unavailable: ' || .?stylesheet-location"/>
            </xsl:when>
            <xsl:when test="not($process)">
              <xsl:message use-when="'pipeline' = $v:debug"
                select="'Unnecessary: ' || .?stylesheet-location"/>
            </xsl:when>
            <xsl:otherwise>
              <xsl:message use-when="'pipeline' = $v:debug"
                select="'Processing : ' || .?stylesheet-location"/>

              <xsl:sequence select="
                  transform(map {
                    'stylesheet-location': .?stylesheet-location,
                    'source-node': $document,
                    'static-params': $vp:static-parameters,
                    'stylesheet-params': map:merge(($vp:dynamic-parameters,
                    $extra-parameters,
                    .?extra-params))
                  })"/>
            </xsl:otherwise>
          </xsl:choose>

        </xsl:variable>

        <xsl:next-iteration>
          <xsl:with-param name="document">
            <xsl:choose>
              <xsl:when test="exists($next-result)">
                <xsl:sequence select="$next-result?output"/>
              </xsl:when>
              <xsl:otherwise>
                <xsl:sequence select="$document"/>
              </xsl:otherwise>
            </xsl:choose>
          </xsl:with-param>
          <xsl:with-param name="secondary-documents" as="map(*)">
            <xsl:choose>
              <xsl:when test="exists($next-result)">
                <xsl:sequence select="
                    let $m := map:remove($next-result, 'output')
                    return
                      map:merge(($secondary-documents, $m))"/>
              </xsl:when>
              <xsl:otherwise>
                <xsl:sequence select="$secondary-documents"/>
              </xsl:otherwise>
            </xsl:choose>
          </xsl:with-param>
          <xsl:with-param name="extra-parameters" select="()"/>
        </xsl:next-iteration>
      </xsl:iterate>
    </xsl:otherwise>
  </xsl:choose>
</xsl:function>
  
<xsl:template name="tp:write-secondary-documents">
  <xsl:param name="transform-result" as="map(*)"/>
  <xsl:for-each select="map:keys($transform-result)[. ne 'output']">
    <xsl:variable name="href" as="xs:string" select="."/>
    <xsl:try>
      <xsl:result-document href="{$href}">
        <xsl:sequence select="$transform-result($href)"/>
        <xsl:message select="'Result Doc : ' || $href" use-when="'pipeline' = $v:debug"/>
      </xsl:result-document>
      <xsl:catch>
        <xsl:message select="'Can''t write secondary transformation result in file ' || $href || ': ' || $err:description"/>
      </xsl:catch>
    </xsl:try>
  </xsl:for-each>
</xsl:template>  
  
<!-- mode to remove @ghost:* attributes and the ghost namespace -->  
<xsl:mode name="mp:remove-ghosts"  />
  
<xsl:template mode="mp:remove-ghosts" match="/">
  <xsl:document>
    <xsl:apply-templates mode="mp:remove-ghosts" select="*"/>
  </xsl:document>
</xsl:template>  
  
<xsl:template mode="mp:remove-ghosts" match="*" >
  <xsl:copy copy-namespaces="no">
    <xsl:apply-templates mode="mp:remove-ghosts" select="@* except @ghost:*, node()"/>
  </xsl:copy> 
</xsl:template>  
  
<xsl:template mode="mp:remove-ghosts" match="@* | node()"  priority="-1">
  <xsl:copy/>
</xsl:template>      

</xsl:stylesheet>
