xquery version "3.0";
(:
    ART-DECOR® STANDARD COPYRIGHT AND LICENSE NOTE
    Copyright © ART-DECOR Expert Group and ART-DECOR Open Tools GmbH
    see https://docs.art-decor.org/copyright and https://docs.art-decor.org/licenses

    This file is part of the ART-DECOR® tools suite.
:)

(:
   Xquery for retrieving template for editing
   Input:
   - template/@id
   - template/@effectiveDate
   - languageCode
   
:)
import module namespace get     = "http://art-decor.org/ns/art-decor-settings" at "art-decor-settings.xqm";
import module namespace art     = "http://art-decor.org/ns/art" at "art-decor.xqm";
import module namespace decor   = "http://art-decor.org/ns/decor" at "../api/api-decor.xqm";
import module namespace vs      = "http://art-decor.org/ns/decor/valueset" at "../api/api-decor-valueset.xqm";
import module namespace templ   = "http://art-decor.org/ns/decor/template" at "../api/api-decor-template.xqm";

declare variable $mode          := if (request:exists()) then request:get-parameter('mode',()) else ('new');

(: keep it simple for performance. if the requested valueSet is in the project terminology by ref, don't look further :)
declare %private function local:isValueSetInScope($projectPrefix as xs:string, $ref as attribute()?,$flexibility as attribute()?,$valueSetList as element()*) as attribute()* {
    if (string-length($ref)=0) then ()
    else (
        let $vsElms := $valueSetList[(@id|@name|@ref)=$ref]
        let $vsEff  := if ($flexibility castable as xs:dateTime) then $flexibility else (max($vsElms[@effectiveDate]/xs:dateTime(@effectiveDate)))
       
        return
            if ($vsElms[@effectiveDate=$vsEff]) then (
                attribute vsid {$vsElms[@effectiveDate=$vsEff][1]/(@id|@ref)},
                attribute vsname {$vsElms[@effectiveDate=$vsEff][1]/@name},
                attribute vsdisplayName {$vsElms[@effectiveDate=$vsEff][1]/@displayName}
            )
            else if ($vsElms[@ref]) then (
                attribute vsid {$vsElms[1]/(@id|@ref)},
                attribute vsname {$vsElms[1]/@name},
                attribute vsdisplayName {$vsElms[1]/@displayName}
            )
            else (
                attribute linkedartefactmissing {'true'}
            )
    )
};

(: keep it simple for performance. if the requested template is in the project rules by ref, don't look further :)
declare %private function local:isTemplateInScope($projectPrefix as xs:string, $ref as attribute()?,$flexibility as attribute()?,$templateList as element()*) as attribute()* {
    if (string-length($ref)=0) then ()
    else (
        let $templates  := (templ:getTemplateByRef($ref,if (matches($flexibility,'^\d{4}')) then $flexibility else 'dynamic',$projectPrefix)/*/template[@id])[1]
        
        return
            if ($templates) then (
                attribute tmid {$templates/@id},
                attribute tmdisplayName {if ($templates/@displayName) then $templates/@displayName else $templates/@name}
            ) else (
                attribute linkedartefactmissing {'true'}
            )
    )
};

declare %private function local:getDatatype($datatype as xs:string?, $classification-format as xs:string?) as xs:string? {
    let $classification-format  := if (empty($classification-format)) then 'hl7v3xml1' else $classification-format
    let $datatypes              := $get:colDecorCore//supportedDataTypes[@type=$classification-format]
    let $datatypeName           := if (contains($datatype,':')) then substring-after($datatype,':') else ($datatype)
    let $flavor                 := $datatypes//flavor[@name=$datatypeName]
    return
        if ($flavor) then ($flavor/ancestor::dataType[1]/@name) else ($datatype)
};

(: re-write attribute strength CNE is required and CWE is extensible :)
declare %private function local:rewriteStrength($os as xs:string?) as xs:string? {
    let $r := if ($os = 'CNE') then 'required' else if ($os = 'CWE') then 'extensible' else $os
    return $r
};

declare %private function local:recurseItemForEdit($projectPrefix as xs:string, $item as element(),$language as xs:string,$valueSetList as element()*,$templateList as element()*,$selected as xs:boolean) as element()* {
if ($item/name()='attribute') then
    for $att in templ:normalizeAttributes($item)
    return
    <attribute>
    {
        $att/@name,
        $att/@value,
        attribute conformance {
            if ($att[@prohibited='true']) then 'NP' else
            if ($att[@isOptional='true']) then 'O' else
                'R'
        }
        ,
        $att/@datatype,
        $att/@id
        ,
        if ($att/@strength) then (
            attribute strength {local:rewriteStrength($att/@strength)}
        ) else ()
        ,
        if ($att/@datatype) then (
            attribute originalType {local:getDatatype($att/@datatype,$item/ancestor::template/classification/@format)},
            attribute originalStrength {local:rewriteStrength($att/@strength)}
        ) else ()
        ,
        attribute originalConformance {
            if ($selected and $mode = 'new') then 
                if ($att[@prohibited='true']) then 'NP' else
                if ($att[@isOptional='true']) then 'O' else
                    'R'
            else ('O')
        }
        ,
        if ($selected) then (attribute selected {''}) else ()
        ,
        for $desc in $att/desc
        return
        art:serializeNode($desc)
        ,
        for $subitem in $att/(* except desc)
        return
            if ($subitem[name()='vocabulary']) then
                <vocabulary>
                {
                    $subitem/(@valueSet|@flexibility|@code|@codeSystem|@codeSystemName|@codeSystemVersion|@displayName|@domain)[not(.='')],
                    local:isValueSetInScope($projectPrefix, $subitem/@valueSet,$subitem/@flexibility,$valueSetList),
                    $subitem/*
                }
                </vocabulary>
            else (
                $subitem
            )
    }
    </attribute>
else if ($item/name()='element') then
    <element>
    {
        $item/(@* except (@strength|@tmid|@tmname|@tmdisplayName|@linkedartefactmissing)),
        local:isTemplateInScope($projectPrefix, $item/@contains,$item/@flexibility,$templateList)
        ,
        if ($item/@strength) then (
            attribute strength {local:rewriteStrength($item/@strength)}
        ) else ()
        ,
        if ($item/@datatype) then (
            attribute originalType {local:getDatatype($item/@datatype,$item/ancestor::template/classification/@format)},
            attribute originalStrength {local:rewriteStrength($item/@strength)}
        )
        else ()
        ,
        if ($item/@minimumMultiplicity) then () else (
            attribute minimumMultiplicity {''}
        )
        ,
        if ($item/@maximumMultiplicity) then () else (
            attribute maximumMultiplicity {''}
        )
        ,
        attribute originalMin {'0'},
        attribute originalMax {'*'}
        ,
        if ($item/@conformance) then () else (
            let $conformance := 
                if ($item[@isMandatory='true']) then 'R' else
                if ($item[@minimumMultiplicity castable as xs:integer]/@minimumMultiplicity > 0) then 'R' else ('O')
            return
            attribute conformance {$conformance}
        )
        ,
        if ($item/@isMandatory) then () else (
            attribute isMandatory {'false'}
        )
        ,
        if ($item/@strength) then () else (
            attribute strength {''}
        )
        ,
        if ($selected) then (attribute selected {''}) else ()
        ,
        for $desc in $item/desc
        return
            art:serializeNode($desc)
        ,
        for $vocabulary in $item/vocabulary
        return
            <vocabulary>
            {
                $vocabulary/(@valueSet|@flexibility|@code|@codeSystem|@codeSystemName|@codeSystemVersion|@displayName|@domain)[not(.='')],
                local:isValueSetInScope($projectPrefix, $vocabulary/@valueSet,$vocabulary/@flexibility,$valueSetList),
                $vocabulary/*
            }
            </vocabulary>
        ,
        $item/property,
        $item/item,
        $item/text,
        for $example in $item/example
        return
        <example type="{$example/@type}" caption="{$example/@caption}">
        {
            if (count($example/*)>1) then (
                fn:serialize(<art:placeholder xmlns:art="urn:art-decor:example">{$example/node()}</art:placeholder>,
                    <output:serialization-parameters xmlns:output="http://www.w3.org/2010/xslt-xquery-serialization">
                        <output:method>xml</output:method>
                        <output:encoding>UTF-8</output:encoding>
                    </output:serialization-parameters>
                )
            )
            else (
                fn:serialize($example/node(),
                    <output:serialization-parameters xmlns:output="http://www.w3.org/2010/xslt-xquery-serialization">
                        <output:method>xml</output:method>
                        <output:encoding>UTF-8</output:encoding>
                    </output:serialization-parameters>
                )
            )
        }
        </example>
        ,
        for $subItem in $item/(attribute|element|choice|include|let|defineVariable|assert|report|constraint)
        return
        local:recurseItemForEdit($projectPrefix, $subItem,$language,$valueSetList,$templateList,$selected)
    }
    </element>
else if ($item/name()='choice') then
    <choice>
    {
        $item/@*,
        if ($item/@minimumMultiplicity) then () else (
            attribute minimumMultiplicity {''}
        )
        ,
        if ($item/@maximumMultiplicity) then () else (
            attribute maximumMultiplicity {''}
        )
        ,
        attribute originalMin {'0'}
        ,
        attribute originalMax {'*'}
        ,
        if ($selected) then (attribute selected {''}) else ()
        ,
        for $desc in $item/desc
        return
        art:serializeNode($desc)
        ,
        $item/item,
        for $subItem in $item/(element|choice|include|constraint)
        return
        local:recurseItemForEdit($projectPrefix, $subItem,$language,$valueSetList,$templateList,$selected)
    }
    </choice>
else if ($item/name()='include') then
    <include>
    {
        $item/(@* except (@tmid|@tmname|@tmdisplayName|@linkedartefactmissing)),
        local:isTemplateInScope($projectPrefix, $item/@ref,$item/@flexibility,$templateList)
        ,
        if ($item/@minimumMultiplicity) then () else (
            attribute minimumMultiplicity {''}
        )
        ,
        if ($item/@maximumMultiplicity) then () else (
            attribute maximumMultiplicity {''}
        )
        ,
        attribute originalMin {'0'}
        ,
        attribute originalMax {'*'}
        ,
        if ($item/@conformance) then () else (
            let $conformance := if ($item/@minimumMultiplicity='1') then 'R' else ('O')
            return
            attribute conformance {$conformance}
        )
        ,
        if ($item/@isMandatory) then () else (
            attribute isMandatory {'false'}
        )
        ,
        if ($item/@flexibility) then () else (
            attribute flexibility {'dynamic'}
        )
        ,
        if ($selected) then (attribute selected {''}) else ()
        ,
        for $desc in $item/desc
        return
        art:serializeNode($desc)
        ,
        $item/item,
        for $example in $item/example
        return
        <example type="{$example/@type}" caption="{$example/@caption}">
        {
            if (count($example/*)>1) then (
                fn:serialize(<art:placeholder xmlns:art="urn:art-decor:example">{$example/node()}</art:placeholder>,
                    <output:serialization-parameters xmlns:output="http://www.w3.org/2010/xslt-xquery-serialization">
                        <output:method>xml</output:method>
                        <output:encoding>UTF-8</output:encoding>
                    </output:serialization-parameters>
                )
            )
            else (
                fn:serialize($example/node(),
                    <output:serialization-parameters xmlns:output="http://www.w3.org/2010/xslt-xquery-serialization">
                        <output:method>xml</output:method>
                        <output:encoding>UTF-8</output:encoding>
                    </output:serialization-parameters>
                )
            )
        }
        </example>
        ,
        for $subItem in $item/constraint
        return
        local:recurseItemForEdit($projectPrefix, $subItem,$language,$valueSetList,$templateList,$selected)
    }
    </include>
else if ($item/name()='let') then
    <let>
    {
        if ($selected) then (attribute selected {''}) else (),
        attribute name {$item/@name},
        attribute value {$item/@value},
        $item/node()
    }
    </let>
else if ($item/name()='assert') then
    <assert>
    {
        if ($selected) then (attribute selected {''}) else (),
        attribute role {$item/@role},
        attribute test {$item/@test},
        attribute see {$item/@see},
        attribute flag {$item/@flag},
        art:serializeNode($item)/node()
    }
    </assert>
else if ($item/name()='report') then
    <report>
    {
        if ($selected) then (attribute selected {''}) else (),
        attribute role {$item/@role},
        attribute test {$item/@test},
        attribute see {$item/@see},
        attribute flag {$item/@flag},
        art:serializeNode($item)/node()
    }
    </report>
else if ($item/name()='constraint') then
    <constraint>{if ($selected) then (attribute selected {''}) else (),$item/(@* except @selected),art:serializeNode($item)/node()}</constraint>
else()
};

declare %private function local:index-of-node( $nodes as node()* , $nodeToFind as node() )  as xs:integer* {
    for $seq in (1 to count($nodes))
    return $seq[$nodes[$seq] is $nodeToFind]
};

declare %private function local:index-node-in-set( $nodes as node()*, $nodeToFind as node() ) as element()* {
    let $n      :=
        if ($nodeToFind[@name]/name()='attribute') then
            $nodes[name()=$nodeToFind/name()][@name = $nodeToFind/@name]
        else
        if ($nodeToFind[@contains]/name()='element') then
            $nodes[name()=$nodeToFind/name()][@name = $nodeToFind/@name][@contains = $nodeToFind/@contains]
        else
        if ($nodeToFind[@name = ('hl7:templateId', 'cda:templateId')][attribute[@name = 'root'][@value]]/name()='element') then
            $nodes[name()=$nodeToFind/name()][@name = $nodeToFind/@name][attribute[@name = 'root'][@value = $nodeToFind/attribute[@name = 'root']/@value]]
        else
        if ($nodeToFind/name()='element') then
            $nodes[name()=$nodeToFind/name()][@name = $nodeToFind/@name]
        else
        if ($nodeToFind/name()='choice') then
            if ($nodeToFind[element]) then
                $nodes[self::choice][element/@name = $nodeToFind/element/@name]
            else
            if ($nodeToFind[include]) then
                $nodes[self::choice][include/@ref = $nodeToFind/include/@ref]
            else
            if ($nodeToFind[choice]) then
                $nodes[self::choice][choice]
            else
            if ($nodeToFind[@minimumMultiplicity][@maximumMultiplicity]) then
                $nodes[self::choice][@minimumMultiplicity = $nodeToFind/@minimumMultiplicity][@maximumMultiplicity = $nodeToFind/@maximumMultiplicity]
            else
            if ($nodeToFind[@minimumMultiplicity]) then
                $nodes[self::choice][@minimumMultiplicity = $nodeToFind/@minimumMultiplicity]
            else
            if ($nodeToFind[@maximumMultiplicity]) then
                $nodes[self::choice][@maximumMultiplicity = $nodeToFind/@maximumMultiplicity]
            else (
                $nodes[self::choice]
            )
        else
        if ($nodeToFind/name()='include') then
            $nodes[name()=$nodeToFind/name()][@ref = $nodeToFind/@ref]
        else
        if ($nodeToFind/name()='let') then
            $nodes[name()=$nodeToFind/name()][@name = $nodeToFind/@name]
        else
        if ($nodeToFind/name()='defineVariable') then
            $nodes[name()=$nodeToFind/name()][@name = $nodeToFind/@name]
        else
        if ($nodeToFind/name()='assert') then
            $nodes[name()=$nodeToFind/name()][@test = $nodeToFind/@test]
        else
        if ($nodeToFind/name()='report') then
            $nodes[name()=$nodeToFind/name()][@test = $nodeToFind/@test]
        else
        if ($nodeToFind/name()='text') then 
            $nodes[name()=$nodeToFind/name()][. = $nodeToFind]
        else
        (: demo10 FHIR constraint, does not have language, only @key :)
        if ($nodeToFind[@key]/name()='constraint') then
            $nodes[name()=$nodeToFind/name()][@key = $nodeToFind/@key]
        else
        if ($nodeToFind/name()='constraint') then
            $nodes[name()=$nodeToFind/name()][@language = $nodeToFind/@language]
        else
        if ($nodeToFind/name()='example') then
            $nodes[name()=$nodeToFind/name()][@language = $nodeToFind/@language][count(preceding-sibling::example) + 1]
        else (
            $nodes[name()=$nodeToFind/name()][string-join(for $att in (@* except (@isOptional|@prohibited|@datatype|@id)) order by name($att) return $att,'')=$nodeToFind/string-join(for $att in (@* except (@isOptional|@prohibited|@datatype|@id)) order by name($att) return $att,'')]
        )
    let $n      :=
        if ($n[2]) then 
            if ($n[@id = $nodeToFind/@id]) then 
                $n[@id = $nodeToFind/@id] 
            (:else 
            if ($n[deep-equal(self::node(), $nodeToFind)]) then 
                $n[deep-equal(self::node(), $nodeToFind)]:) 
            else $n
        else $n
        
    return $n
};

(: Returns paths like this
    element[@name='hl7:informEvent'][empty(@contains)][@id='2.16.840.1.113883.2.4.3.111.3.7.9.16'][1]
:)
declare %private function local:path-to-node ( $nodes as node()* )  as xs:string* { 
    string-join(
        for $item in $nodes/ancestor-or-self::*[not(descendant-or-self::template)]
        return (
            string-join(
                ($item/name(),
                if ($item[@name]/name()='attribute') then (
                    '[@name=''',replace($item/@name,'''',''''''),''']'
                )
                else
                if ($item[@name][@contains]/name()='element') then (
                    '[@name=''',replace($item/@name,'''',''''''),''']',
                    '[@contains=''',replace($item/@contains,'''',''''''),''']'
                )
                else
                if ($item[@name = ('hl7:templateId', 'cda:templateId')][attribute[@name = 'root'][@value]]/name()='element') then (
                    '[@name=''',replace($item/@name,'''',''''''),''']','[attribute[@name = ''root''][@value = ''',$item/attribute[@name = 'root']/@value,''']]',
                    '[',count($item/preceding-sibling::element[@name = $item/@name][attribute[@name = 'root'][@value]]) + 1,']'
                )
                else
                if ($item[@name][empty(@contains)]/name()='element') then (
                    '[@name=''',replace($item/@name,'''',''''''),''']','[empty(@contains)]',
                    '[',count($item/preceding-sibling::element[@name = $item/@name][empty(@contains)]) + 1,']'
                )
                else
                if ($item/name()='choice') then (
                    if ($item[element]) then (
                        '[element/@name=(',for $n in $item/element/@name return concat('''',replace($n,'''',''''''),''''),')]'
                    ) else
                    if ($item[include]) then (
                        '[include/@ref=(',for $n in $item/include/@ref return concat('''',replace($n,'''',''''''),''''),')]'
                    ) else
                    if ($item[choice]) then (
                        '[choice]'
                    ) else
                    if ($item[@minimumMultiplicity][@maximumMultiplicity]) then (
                        '[@minimumMultiplicity = ',$item/@minimumMultiplicity,']','[@maximumMultiplicity = ',$item/@maximumMultiplicity,']'
                    ) else
                    if ($item[@minimumMultiplicity]) then (
                        '[@minimumMultiplicity = ',$item/@minimumMultiplicity,']'
                    ) else
                    if ($item[@maximumMultiplicity]) then (
                        '[@maximumMultiplicity = ',$item/@maximumMultiplicity,']'
                    ) else (
                        ''
                    )
                )
                else
                if ($item/name()='include') then (
                    '[@ref=''',replace($item/@ref,'''',''''''),''']'
                )
                else
                if ($item/name()='let') then (
                    '[@name=''',replace($item/@name,'''',''''''),''']'
                )
                else
                if ($item/name()='defineVariable') then (
                    '[@name=''',replace($item/@name,'''',''''''),''']'
                )
                else
                if ($item/name()='assert') then (
                    '[@test=''',replace($item/@test,'''',''''''),''']'
                )
                else
                if ($item/name()='report') then (
                    '[@test=''',replace($item/@test,'''',''''''),''']'
                )
                else
                if ($item/name()='constraint') then (
                    '[@language=''',replace($item/@language,'''',''''''),''']'
                )
                else
                if ($item/name()='example') then (
                    '[',count(preceding-sibling::example) + 1,']'
                )
                else
                if ($item/name()='text') then (
                    '[. = ''',replace(., '''', ''''''),''']'
                )
                else
                if ($item[@name]) then (
                    '[@name=''',replace($item/@name,'''',''''''),''']'
                )
                else
                if ($item[@code][@codeSystem]) then (
                    '[@code=''',replace($item/@code,'''',''''''),''']',
                    '[@codeSystem=''',replace($item/@codeSystem,'''',''''''),''']'
                )
                else
                if ($item[@valueSet]) then (
                    '[@valueSet=''',replace($item/@valueSet,'''',''''''),''']'
                )
                else
                if ($item[@language]) then (
                    '[@language=''',replace($item/@language,'''',''''''),''']'
                )
                else (
                    let $n  := $item/(@* except (@isOptional|@prohibited|@datatype|@id))[1]
                    return (
                        if ($n) then concat('[@',name($n),'=''',replace($n,'''',''''''),''']') else ()
                    )
                )
            ), '')
        )
    , '/')
};

declare %private function local:mergePrototypeTemplateForEdit($projectPrefix as xs:string, $item as element(), $prototype as element(), $language as xs:string, $valueSetList as element()*, $templateList as element()*) as element()* {
(: get corresponding node in prototype :)
let $node           := util:eval-inline($prototype,local:path-to-node($item))
let $node           := if ($node[@id = $item/@id]) then $node[@id = $item/@id][1] else $node[1]
(: get the reversed sequence for previous siblings of the corresponding node in the prototype template, if any :)
let $precedingNodes := reverse($node/preceding-sibling::*)

let $results        := (
    (: if there are no preceding nodes in template, get all preceding nodes from prototype
        else get preceding nodes up to node with same name as preceding template node
    :)
    if (count($item/preceding-sibling::*)=0 and count($node/preceding-sibling::*)>0) then (
        for $precedingNode in $node/preceding-sibling::*
        return
            local:recurseItemForEdit($projectPrefix,$precedingNode,$language,$valueSetList,$templateList,false())
    )
    else (
        (: check if there are preceding nodes in prototype that are not in the template
            in order not get retrieve every single node preceding the current one, we first 
            try to find a previous matching node, anything between the previous and the 
            current item should be merged into the current template in this context 
        :)
        let $indexNode  := 
            for $precedingItem in $item/preceding-sibling::*
            let $n      := local:index-node-in-set($precedingNodes, $precedingItem)
            return
                if (count($n) le 1) then ($n) else (
                    error(QName('http://art-decor.org/ns/error', 'MultipleIndexNodes'), concat('Unexpected error. Found multiple index nodes so we do not know what to merge here: ', local:path-to-node($precedingItem), ', index nodes: ', string-join(for $i in $n return concat('''&lt;',$i/name(),string-join(for $att in $i/@* return concat(' ',$att/name(),'=''',data($att),''''),''),'&gt;'''),' ')))
                )
        
        let $index      := if ($indexNode) then local:index-of-node($precedingNodes,$indexNode[last()]) else (count($precedingNodes))
        
        for $n in reverse($precedingNodes)
        let $lin := local:index-of-node($precedingNodes,$n) lt $index and 
                    $n[ self::attribute|
                        self::element|
                        self::choice|
                        self::include|
                        self::let|
                        self::defineVariable|
                        self::assert|
                        self::report|
                        self::constraint ]
        return
            if ($lin) then local:recurseItemForEdit($projectPrefix, $n, $language, $valueSetList, $templateList, false()) else ()
    )
    ,
    if ($item/name()='attribute') then
        for $att in templ:normalizeAttributes($item)
        return
        <attribute selected="{if ($node) then 'original' else ''}">
        {
            $att/@name,
            $att/@value,
            attribute conformance {
                if ($att[@prohibited='true']) then 'NP' else
                if ($att[@isOptional='true']) then 'O' else
                    'R'
            }
            ,
            $att/@datatype,
            $att/@id
            ,
            if ($node/@datatype) then (
                attribute originalType {local:getDatatype($node/@datatype,$node/ancestor::template/classification/@format)},
                attribute originalStrength {local:rewriteStrength($node/@strength)}
            ) else 
            if ($att/@datatype) then (
                attribute originalType {local:getDatatype($att/@datatype,$att/ancestor::template/classification/@format)},
                attribute originalStrength {local:rewriteStrength($att/@strength)}
            )
            else ()
            ,
            attribute originalConformance {
                if ($node/@prohibited='true') then 'NP' else 
                if ($node/@isOptional='true') then 'O' else 
                if ($node) then 'R' else 
                    'O'
            }
            ,
            for $desc in $att/desc
            return
            art:serializeNode($desc)
            ,
            for $subitem in $att/(* except desc)
            return
                if ($subitem[name()='vocabulary']) then
                    <vocabulary>
                    {
                        $subitem/(@valueSet|@flexibility|@code|@codeSystem|@codeSystemName|@codeSystemVersion|@displayName|@domain)[not(.='')],
                        local:isValueSetInScope($projectPrefix, $subitem/@valueSet,$subitem/@flexibility,$valueSetList),
                        $subitem/*
                    }
                    </vocabulary>
                else (
                    $subitem
                )
        }
        </attribute>
    else if ($item/name()='element') then
        <element selected="{if ($node) then 'original' else ''}">
        {
            $item/(@* except (@strength|@tmid|@tmname|@tmdisplayName|@linkedartefactmissing)),
            local:isTemplateInScope($projectPrefix, $item/@contains,$item/@flexibility,$templateList)
            ,
            if ($item/@strength) then attribute strength {local:rewriteStrength($item/@strength)} else ()
            ,
            if ($node/@datatype) then (
                attribute originalType {local:getDatatype($node/@datatype,$node/ancestor::template/classification/@format)},
                attribute originalStrength {local:rewriteStrength($node/@strength)}
            ) else 
            if ($item/@datatype) then (
                attribute originalType {local:getDatatype($item/@datatype,$item/ancestor::template/classification/@format)},
                attribute originalStrength {local:rewriteStrength($item/@strength)}
            )
            else ()
            ,
            if ($item/@minimumMultiplicity) then () else (
                attribute minimumMultiplicity {''}
            )
            ,
            if ($item/@maximumMultiplicity) then () else (
                attribute maximumMultiplicity {''}
            )
            ,
            if ($node/@minimumMultiplicity) then
                attribute originalMin {$node/@minimumMultiplicity}
            else (
                attribute originalMin {'0'}
            )
            ,
            if ($node/@maximumMultiplicity) then
                attribute originalMax {$node/@maximumMultiplicity}
            else (
                attribute originalMax {'*'}
            )
            ,
            if ($item/@conformance) then () else (
                attribute conformance {'O'}
            )
            ,
            if ($item/@isMandatory) then () else (
                attribute isMandatory {'false'}
            )
            ,
            if ($item/@strength) then () else (
                attribute strength {''}
            )
            ,
            for $desc in $item/desc
            return
                art:serializeNode($desc)
            ,
            for $vocabulary in $item/vocabulary
            return
                <vocabulary>
                {
                    $vocabulary/(@valueSet|@flexibility|@code|@codeSystem|@codeSystemName|@codeSystemVersion|@displayName|@domain)[not(.='')],
                    local:isValueSetInScope($projectPrefix, $vocabulary/@valueSet,$vocabulary/@flexibility,$valueSetList),
                    $vocabulary/*
                }
                </vocabulary>
            ,
            $item/property,
            $item/item,
            $item/text,
            for $example in $item/example
            return
            <example type="{$example/@type}" caption="{$example/@caption}">
            {
                if (count($example/*)>1) then (
                    fn:serialize(<art:placeholder xmlns:art="urn:art-decor:example">{$example/node()}</art:placeholder>,
                        <output:serialization-parameters xmlns:output="http://www.w3.org/2010/xslt-xquery-serialization">
                            <output:method>xml</output:method>
                            <output:encoding>UTF-8</output:encoding>
                        </output:serialization-parameters>
                    )
                )
                else (
                    fn:serialize($example/node(),
                        <output:serialization-parameters xmlns:output="http://www.w3.org/2010/xslt-xquery-serialization">
                            <output:method>xml</output:method>
                            <output:encoding>UTF-8</output:encoding>
                        </output:serialization-parameters>
                    )
                )
            }
            </example>
            ,
            for $subItem in $item/(attribute|element|choice|include|constraint|let|defineVariable|assert|report)
            return
                local:mergePrototypeTemplateForEdit($projectPrefix, $subItem, $prototype, $language, $valueSetList, $templateList)
        }
        </element>
    else if ($item/name()='choice') then
        <choice selected="{if ($node) then 'original' else ''}">
        {
            $item/@*,
            if (not($item/@minimumMultiplicity)) then
                attribute minimumMultiplicity {''}
            else ()
            ,
            if (not($item/@maximumMultiplicity)) then
                attribute maximumMultiplicity {''}
            else ()
            ,
            if ($node/@minimumMultiplicity) then
                attribute originalMin {$node/@minimumMultiplicity}
            else (attribute originalMin {'0'})
            ,
            if ($node/@maximumMultiplicity) then
                attribute originalMax {$node/@maximumMultiplicity}
            else (attribute originalMax {'*'})
            ,
            for $desc in $item/desc
            return
            art:serializeNode($desc)
            ,
            $item/item,
            for $subItem in $item/(element|choice|include|constraint)
            return
            local:mergePrototypeTemplateForEdit($projectPrefix, $subItem, $prototype, $language, $valueSetList, $templateList)
        }
        </choice>
    else if ($item/name()='include') then
        <include selected="{if ($node) then 'original' else ''}">
        {
            $item/(@* except (@tmid|@tmname|@tmdisplayName|@linkedartefactmissing)),
            local:isTemplateInScope($projectPrefix, $item/@ref,$item/@flexibility,$templateList)
            ,
            if (not($item/@minimumMultiplicity)) then
                attribute minimumMultiplicity {''}
            else ()
            ,
            if (not($item/@maximumMultiplicity)) then
                attribute maximumMultiplicity {''}
            else ()
            ,
            if ($node/@minimumMultiplicity) then
                attribute originalMin {$node/@minimumMultiplicity}
            else (
                attribute originalMin {'0'}
            )
            ,
            if ($node/@maximumMultiplicity) then
                attribute originalMax {$node/@maximumMultiplicity}
            else (
                attribute originalMax {'*'}
            )
            ,
            if (not($item/@conformance)) then
                attribute conformance {'O'}
            else ()
            ,
            if (not($item/@isMandatory)) then
                attribute isMandatory {'false'}
            else ()
            ,
            for $desc in $item/desc
            return
            art:serializeNode($desc)
            ,
            $item/item
            ,
            for $example in $item/example
            return
            <example type="{$example/@type}" caption="{$example/@caption}">
            {
                if (count($example/*)>1) then (
                    fn:serialize(<art:placeholder xmlns:art="urn:art-decor:example">{$example/node()}</art:placeholder>,
                        <output:serialization-parameters xmlns:output="http://www.w3.org/2010/xslt-xquery-serialization">
                            <output:method>xml</output:method>
                            <output:encoding>UTF-8</output:encoding>
                        </output:serialization-parameters>
                    )
                )
                else (
                    fn:serialize($example/node(),
                        <output:serialization-parameters xmlns:output="http://www.w3.org/2010/xslt-xquery-serialization">
                            <output:method>xml</output:method>
                            <output:encoding>UTF-8</output:encoding>
                        </output:serialization-parameters>
                    )
                )
            }
            </example>
            ,
            for $subItem in $item/constraint
            return
                local:mergePrototypeTemplateForEdit($projectPrefix, $subItem, $prototype, $language, $valueSetList, $templateList)
        }
        </include>
    else if ($item/name()='let') then
        <let selected="{if ($node) then 'original' else ''}">
        {
            attribute name {$item/@name},
            attribute value {$item/@value},
            $item/node()
        }
        </let>
    else if ($item/name()='defineVariable') then
        <defineVariable selected="{if ($node) then 'original' else ''}">
        {
            attribute name {$item/@name},
            attribute value {$item/@value},
            $item/node()
        }
        </defineVariable>
    else if ($item/name()='assert') then
        <assert selected="{if ($node) then 'original' else ''}">
        {
            attribute role {$item/@role},
            attribute test {$item/@test},
            attribute see {$item/@see},
            attribute flag {$item/@flag},
            art:serializeNode($item)/node()
        }
        </assert>
    else if ($item/name()='report') then
        <report selected="{if ($node) then 'original' else ''}">
        {
            attribute role {$item/@role},
            attribute test {$item/@test},
            attribute see {$item/@see},
            attribute flag {$item/@flag},
            art:serializeNode($item)/node()
        }
        </report>
    else if ($item/name()='constraint') then
        <constraint selected="{if ($node) then 'original' else ''}">
        {
            $item/(@* except @selected),
            art:serializeNode($item)/node()
        }
        </constraint>
    else (),
    (:
       if parent is not 'template' check if there are prototype nodes after the last template node
    :)
    if ($item/following-sibling::*) then () else 
    if ($node) then (
        (: our own current template had a match in the prototype :)
        for $n in ( $node/following-sibling::attribute|
                    $node/following-sibling::element|
                    $node/following-sibling::choice|
                    $node/following-sibling::include|
                    $node/following-sibling::let|
                    $node/following-sibling::defineVariable|
                    $node/following-sibling::assert|
                    $node/following-sibling::report|
                    $node/following-sibling::constraint)
        return
            local:recurseItemForEdit($projectPrefix, $n, $language, $valueSetList, $templateList, false())
    )
    else (
        (: since our current node did not match, find any preceding node from our template that matched in the prototype :)
        let $precedingNodes :=
            for $n in ( $item/preceding-sibling::attribute|
                        $item/preceding-sibling::element|
                        $item/preceding-sibling::choice|
                        $item/preceding-sibling::include|
                        $item/preceding-sibling::let|
                        $item/preceding-sibling::defineVariable|
                        $item/preceding-sibling::assert|
                        $item/preceding-sibling::report|
                        $item/preceding-sibling::constraint)
            (: get corresponding node in prototype :)
            let $nodeInPrototype    := util:eval-inline($prototype,local:path-to-node($n))
            let $nodeInPrototype    := if ($nodeInPrototype[@id = $n/@id]) then $nodeInPrototype[@id = $n/@id][1] else $nodeInPrototype[1]
            return
                $nodeInPrototype
        
        (: add all nodes from the prototype template following the last match that we had, to the end of the current template :)
        for $n in ( $precedingNodes[last()]/following-sibling::attribute|
                    $precedingNodes[last()]/following-sibling::element|
                    $precedingNodes[last()]/following-sibling::choice|
                    $precedingNodes[last()]/following-sibling::include|
                    $precedingNodes[last()]/following-sibling::let|
                    $precedingNodes[last()]/following-sibling::defineVariable|
                    $precedingNodes[last()]/following-sibling::assert|
                    $precedingNodes[last()]/following-sibling::report|
                    $precedingNodes[last()]/following-sibling::constraint)
         return
            local:recurseItemForEdit($projectPrefix, $n, $language, $valueSetList, $templateList, false())
    )
)

for $result in $results
return
    if ($result/attribute) then (
        element {name($result)} {
            $result/@*,
            $result/attribute[last()] | $result/attribute[last()]/preceding-sibling::* except $result/element,
            $result/attribute[last()]/preceding-sibling::element | $result/attribute[last()]/following-sibling::node()
        }
    )
    else (
        $result
    )
            
};

(:get prototype for editor with normalized attributes:)
declare %private function local:getPrototype($id as xs:string?,$flexibility as xs:string?,$projectPrefix as xs:string) as element()? {
    let $prototype      := 
        if (string-length($id)=0) then () else (
            templ:getTemplateById($id,if (matches($flexibility,'^\d{4}')) then $flexibility else 'dynamic',$projectPrefix)/*/template[@id]
        )
    return
        if ($prototype) then $prototype[1] else <template/>
};

let $projectPrefix              := if (request:exists()) then request:get-parameter('prefix',())                        else ()
let $id                         := if (request:exists()) then request:get-parameter('id',())[not(.='')]                 else ()
let $effectiveDate              := if (request:exists()) then request:get-parameter('effectiveDate',())[not(.='')]      else ()
let $breakLock                  := if (request:exists()) then xs:boolean(request:get-parameter('breakLock','false'))    else (false())

let $language                   := if (request:exists()) then request:get-parameter('language',())[not(.='')]           else ()
let $decor                      := art:getDecorByPrefix($projectPrefix)
let $language                   := if ($language) then $language else $decor/project/@defaultLanguage

let $template                   := local:getPrototype($id, $effectiveDate, $projectPrefix)
let $templateAssociations       := if (empty($id)) then () else $decor//templateAssociation[@templateId = $id][@effectiveDate=$effectiveDate]

let $valueSetList               := $decor/terminology/valueSet
let $templateList               := $decor/rules/template
let $lock                       := if ($mode='edit') then decor:setLock($id, $effectiveDate, false()) else (<true/>)

let $response :=
    (:check if user is author:)
    if ($lock[self::false]) then
        <template>{$lock/node()}</template>
    else if (not($mode=('edit','new','version','adapt'))) then
        <template>{'MODE ''',$mode,''' UNSUPPORTED'}</template>
    else (
        let $specialization := $template/relationship[@type='SPEC'][@template][1]
        (:get prototype for editor with normalized attributes:)
        let $prototype      := if ($specialization) then (local:getPrototype($specialization/@template,$specialization/@flexibility,$projectPrefix)) else ()
        
        let $useBaseId      := 
            decor:getDefaultBaseIds($projectPrefix, $decor:OBJECTTYPE-TEMPLATE)[1]/@id
        
        let $namespaces-attrs       := 
            for $ns at $i in art:getDecorNamespaces($decor) 
            return attribute {QName($ns/@uri,concat($ns/@prefix,':dummy-',$i))} {$ns/@uri}
        
        return
        <template 
            projectPrefix="{$decor/project/@prefix}" 
            baseId="{$useBaseId}"
            originalId="{$template/@id}"
            id="{$template/@id}"
            name="{$template/@name}"
            displayName="{$template/@displayName}"
            effectiveDate="{$template/@effectiveDate}"
            statusCode="{if ($mode='edit') then $template/@statusCode else 'new'}"
            versionLabel="{$template/@versionLabel}"
            isClosed="{$template/@isClosed}"
            expirationDate="{$template/@expirationDate}"
            officialReleaseDate="{$template/@officialReleaseDate}"
            canonicalUri="{$template/@canonicalUri}">
        {
            $namespaces-attrs
        }
            <edit mode="{$mode}"/>
        {
            $lock/*
        }
        {
            if (not($template/desc[@language=$language])) then
                <desc language="{$language}"/>
            else()
            ,
            for $desc in $template/desc
            return
            art:serializeNode($desc)
        }
        {
            $template/classification
        }
        {
            if ($mode=('new','version','adapt')) then (
                if ($template[@id]) then (
                    <relationship type="{if ($mode = 'new') then 'SPEC' else upper-case($mode)}" template="{$template/@id}" selected="template" flexibility="{$template/@effectiveDate}" templateName="{$template/@name}"/>
                ) else ()
            ) else ()
            ,
            for $relationship in $template/relationship
            let $addAttribute := if ($relationship/@template) then 'model' else 'template'
            return
            <relationship>
            {
                $relationship/@*,
                attribute selected {if ($relationship/@template) then 'template' else 'model'},
                attribute {$addAttribute} {''},
                if (not($relationship/@flexibility)) then
                    attribute flexibility {''}
                else()
            }
            </relationship>
        }
        {
            for $node in $template/publishingAuthority
            return
            <publishingAuthority>
            {
                $node/@*,
                attribute selected {''},
                $node/addrLine
            }
            </publishingAuthority>
        }
        {
            for $node in $template/endorsingAuthority
            return
            <endorsingAuthority>
            {
                $node/@*,
                attribute selected {''},
                $node/addrLine
            }
            </endorsingAuthority>
        }
        {
            for $node in $template/purpose
            return
            art:serializeNode($node)
        }
        {
            for $node in $template/copyright
            return
            art:serializeNode($node)
        }
        {
            if ($template/context/@path) then
                <context id="" path="{$template/context/@path}" selected="path"/>
            else if ($template/context/@id) then
                <context id="{$template/context/@id}" path="" selected="id"/>
            else(<context id="" path="" selected=""/>)
        }
        {
            if ($template/item) then
                <item label="{$template/item/@label}">
                {
                    for $desc in $template/item/desc
                    return
                    art:serializeNode($desc)
                }
                </item>
            else()
        }
        {
            for $example in $template/example
            return
            <example type="{$example/@type}" caption="{$example/@caption}">
            {
                if (count($example/*)>1) then (
                    fn:serialize(<art:placeholder xmlns:art="urn:art-decor:example">{$example/node()}</art:placeholder>,
                        <output:serialization-parameters xmlns:output="http://www.w3.org/2010/xslt-xquery-serialization">
                            <output:method>xml</output:method>
                            <output:encoding>UTF-8</output:encoding>
                        </output:serialization-parameters>
                    )
                )
                else (
                    fn:serialize($example/node(),
                        <output:serialization-parameters xmlns:output="http://www.w3.org/2010/xslt-xquery-serialization">
                            <output:method>xml</output:method>
                            <output:encoding>UTF-8</output:encoding>
                        </output:serialization-parameters>
                    )
                )
            }
            </example>
            ,
            if (not($template/example)) then
                <example type="neutral" caption=""></example>
            else()
        }
        {
            if (exists($specialization) and exists($prototype)) then (
                (: if template is empty, check prototype for relevant parts :)
                if ($template/(attribute|element|choice|include|let|defineVariable|assert|report|constraint)) then (
                    for $item in $template/(attribute|element|choice|include|let|defineVariable|assert|report|constraint)
                    return
                    local:mergePrototypeTemplateForEdit($projectPrefix,$item,$prototype,$language,$valueSetList,$templateList)
                ) else (
                    for $item in $prototype/(attribute|element|choice|include|let|defineVariable|assert|report|constraint)
                    return
                    local:recurseItemForEdit($projectPrefix,$item,$language,$valueSetList,$templateList,false())
                )
            )
            else (
                for $item in $template/(attribute|element|choice|include|let|defineVariable|assert|report|constraint)
                return
                local:recurseItemForEdit($projectPrefix,$item,$language,$valueSetList,$templateList,true())
            )
        }
        {
            (: by adding them here, they stay in the clients XML until he closes the form :)
            if ($templateAssociations) then 
                <staticAssociations>
                {
                    for $association in $templateAssociations/concept
                    let $concept            := art:getConcept($association/@ref, $association/@effectiveDate)
                    let $originalConcept    := art:getOriginalForConcept($concept)
                    let $path               := 
                        for $c in $concept/ancestor::concept
                        let $oc         := art:getOriginalForConcept($c)
                        return if ($oc/name[@language=$language]) then $oc/name[@language=$language] else ($oc/name[1])
                    return
                        <concept
                            datasetId="{$concept/ancestor::dataset/@id}" 
                            datasetEffectiveDate="{$concept/ancestor::dataset/@effectiveDate}" 
                            datasetName="{if ($concept/ancestor::dataset/name[@language = $language]) then $concept/ancestor::dataset/name[@language = $language] else $concept/ancestor::dataset/name[1]}" 
                            ref="{$association/@ref}" 
                            effectiveDate="{$association/@effectiveDate}" 
                            elementId="{$association/@elementId}" 
                            refdisplay="{art:getNameForOID($association/@ref, $language, $decor)}"
                            path="{string-join($path,' / ')}">
                        {
                            $originalConcept/name
                        }
                        </concept>
                }
                </staticAssociations>
            else ()
        }
        </template>
    )
  
return
$response