xquery version "1.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.
:)

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 vs  = "http://art-decor.org/ns/decor/valueset" at "../api/api-decor-valueset.xqm";

let $projectPrefix          := if (request:exists()) then request:get-parameter('prefix',()) else ('onco-gyn-')
let $id                     := if (request:exists()) then request:get-parameter('id',())[string-length() gt 0][1] else ('2.16.840.1.113883.2.4.3.11.60.40.2.0.1.7')
let $effectiveDate          := if (request:exists()) then request:get-parameter('effectiveDate',())[. castable as xs:dateTime][1] else ('2015-04-01T00:00:00')

let $valueSetsAll           :=
    if (empty($id)) then () else (
        if (empty($projectPrefix)) then
            vs:getValueSetById($id, (), false())
        else (
            vs:getValueSetById($id, (), $projectPrefix, (), false())
        )
    )

let $id                     := if (empty($id)) then $valueSetsAll/descendant-or-self::valueSet/@id else $id
let $mostRecent             := string(max($valueSetsAll/descendant-or-self::valueSet/xs:dateTime(@effectiveDate)))
let $effectiveDate          := if ($effectiveDate castable as xs:dateTime) then $effectiveDate else $mostRecent
let $name                   := $valueSetsAll/descendant-or-self::valueSet[@id]/@name

let $decor                  := $get:colDecorData/decor

let $allAssociations        :=
    $decor//terminologyAssociation[@valueSet = $id] |
    $decor//terminologyAssociation[@valueSet = $name][../valueSet[@id][@name = $name]]
    
let $staticAssociations     := $allAssociations[@flexibility = $effectiveDate]
let $dynamicAssociations    :=
    if ($mostRecent = $effectiveDate) then 
        $allAssociations[empty(@flexibility)] | $allAssociations[@flexibility='dynamic']
    else ()
let $allAssociations        := $staticAssociations | $dynamicAssociations

let $allAssociationsMap     := map:merge(for $de-id in distinct-values($allAssociations/@conceptId) return map:entry($de-id, ()))

let $originalConcepts       := 
    for $v in $decor//valueDomain/conceptList[@id | @ref][not(ancestor::history)]
    return 
        if (map:contains($allAssociationsMap, $v/(@id | @ref))) then 
            $v/ancestor::concept[1] 
        else ()
    
let $allInherits            := 
    for $c in $originalConcepts
    return 
        $decor//inherit[@ref = $c/@id][@effectiveDate = $c/@effectiveDate]

let $allInherits            := $allInherits[not(ancestor::history)]/ancestor::concept[1]

let $allContains            := 
    for $c in $originalConcepts
    let $d  := $decor//contains[@ref = $c/@id]
    return $d[@flexibility = $c/@effectiveDate]

let $allContains            := $allContains[not(ancestor::history)]/parent::concept

let $inScopeConcepts        := $originalConcepts|$allInherits|$allContains
    
let $staticTemplAssocs      := 
    $decor/rules//vocabulary[@valueSet = $id][@flexibility = $effectiveDate] | 
    $decor/rules//vocabulary[@valueSet = $name][@flexibility = $effectiveDate]
let $dynamicTemplAssocs     :=
    if ($mostRecent = $effectiveDate) then 
        $decor/rules//vocabulary[@valueSet = $id][empty(@flexibility)] | 
        $decor/rules//vocabulary[@valueSet = $id][@flexibility = 'dynamic'] |
        $decor/rules//vocabulary[@valueSet = $name][empty(@flexibility)] |
        $decor/rules//vocabulary[@valueSet = $name][@flexibility = 'dynamic']
    else ()

(: 
ALERT/FIXME: opportunistic approach towards finding the right set of associations. Official picture:
- An association points to "our value set" if it lives the same project
- An association points to "our value set" if it lives in a project that references our project AND valueSet/@ref

Now the problem is that we do not know if it references our project unless we check both buildingBlockRepository/@url AND @ident, 
but how do we know that @url points to "our server"? Opportunistically we now check @ident only, which could mean incorrect hits 
if there's project on another server by the same prefix.
Second thing is that we now do not check if the terminologyAssociation or vocabulary references a valueSet/@ref, hence if a project 
happens both a reference to "our project", and a value set by the same name, but which is not "our value set", it might give yet another
incorrect hit.

Both scenarios are somewhat unlikely but surely not impossible.
:)

return
<usage prefix="{$projectPrefix}" id="{$id}" name="{$name}" effectiveDate="{$effectiveDate}" mostRecent="{$mostRecent}" isMostRecent="{$mostRecent = $effectiveDate}">
{
    if ($mostRecent = $effectiveDate) then (
        for $concept in $inScopeConcepts[ancestor::decor/project/@prefix = $dynamicAssociations/ancestor::decor/project/@prefix]
        let $originalId     := $concept/@id
        let $originalEd     := $concept/@effectiveDate
        let $readableId     := art:getNameForOID($originalId, (), $concept/ancestor::decor)
        
        let $fullConcept    := art:getOriginalForConcept($concept)
        let $dynBindings    := $dynamicAssociations[@conceptId=$fullConcept/valueDomain/conceptList/(@id|@ref)]
        return
            if (exists($dynBindings)) then (
                <association id="{distinct-values($dynBindings/@conceptId)}" type="dynamic"
                        conceptId="{$readableId}" originalConceptId="{$originalId}" originalConceptEffectiveDate="{$originalEd}"
                        prefix="{$concept/ancestor::decor/project/@prefix}" projectName="{$concept/ancestor::decor/project/name[1]}">
                {
                    if ($concept/@versionLabel) then attribute conceptVersionLabel {$concept/@versionLabel} else ()
                    ,
                    for $att in $concept/ancestor::dataset/@*
                    return
                        attribute {concat('dataset', upper-case(substring(name($att), 1, 1)), substring(name($att), 2))} {$att}
                    ,
                    $fullConcept/name
                }
                </association>
            ) else ()
    
    ) else ()
}
{
    for $concept in $inScopeConcepts[ancestor::decor/project[@prefix = $staticAssociations/ancestor::decor/project/@prefix]]
    let $originalId     := $concept/@id
    let $originalEd     := $concept/@effectiveDate
    let $readableId     := art:getNameForOID($originalId, (), $concept/ancestor::decor)
    
    let $fullConcept    := art:getOriginalForConcept($concept)
    let $statBindings   := ($staticAssociations[@conceptId = $fullConcept/valueDomain/conceptList/@id] | 
                            $staticAssociations[@conceptId = $fullConcept/valueDomain/conceptList/@ref])
    return
        if (exists($statBindings)) then (
            <association id="{distinct-values($statBindings/@conceptId)}" type="static"
                    conceptId="{$readableId}" originalConceptId="{$originalId}" originalConceptEffectiveDate="{$originalEd}"
                    prefix="{$concept/ancestor::decor/project/@prefix}" projectName="{$concept/ancestor::decor/project/name[1]}">
            {
                if ($concept/@versionLabel) then attribute conceptVersionLabel {$concept/@versionLabel} else ()
                ,
                for $att in $concept/ancestor::dataset/@*
                return
                    attribute {concat('dataset', upper-case(substring(name($att), 1, 1)), substring(name($att), 2))} {$att}
                ,
                $fullConcept/name
            }
            </association>
        ) else ()
}
{
    for $terminologyAssociation in $decor[project/@prefix = $projectPrefix]//terminologyAssociation[@code][@codeSystem]
    let $code                   := $terminologyAssociation/@code
    let $codeSystem             := $terminologyAssociation/@codeSystem
    let $codeSystemVersion      := $terminologyAssociation/@codeSystemVersion
    let $inScope                := $valueSetsAll//concept[@code = $code][@codeSystem = $codeSystem]
    let $inScope                := if ($inScope) then $inScope else $valueSetsAll//exception[@code = $code][@codeSystem = $codeSystem]
    return
        if ($inScope) then (
            let $originalConcept    := $decor//concept[@id = $terminologyAssociation/@conceptId][not(ancestor::history)]
            let $originalParent     := $originalConcept/ancestor::concept[1]
            let $projectConcepts    := $originalParent[ancestor::decor/project/@prefix=$projectPrefix] | $decor[project/@prefix=$projectPrefix]//concept[inherit/@ref=$originalParent/@id][inherit/@effectiveDate=$originalParent/@effectiveDate][not(ancestor::history)]
            return
                <terminologyAssociation>
                {
                    $terminologyAssociation/@*
                    ,
                    $originalConcept/name
                }
                {
                    for $concept in $projectConcepts
                    return
                        <concept id="{$concept/@id}" effectiveDate="{$concept/@effectiveDate}"/>
                }
                </terminologyAssociation>
        ) else ()
}
{
    for $template in $dynamicTemplAssocs
    let $tr         := $template[1]/ancestor::template
    let $parent     := concat($template/ancestor::template/@id,$template/ancestor::template/@ref,$template/ancestor::template/@effectiveDate,$template/ancestor::template/@flexibility)
    group by $parent
    return
        <template id="{$tr/@id}" name="{$tr/@name}" displayName="{$tr/@displayName}" effectiveDate="{$tr/@effectiveDate}" 
                  type="dynamic" prefix="{$template/ancestor::decor/project/@prefix}" projectName="{$template[1]/ancestor::decor/project/name[1]}"/>
}
{
    for $template in $staticTemplAssocs
    let $tr         := $template[1]/ancestor::template
    let $parent     := concat($template/ancestor::template/@id,$template/ancestor::template/@ref,$template/ancestor::template/@effectiveDate,$template/ancestor::template/@flexibility)
    group by $parent
    return
        <template id="{$tr/@id}" name="{$tr/@name}" displayName="{$tr/@displayName}" effectiveDate="{$tr/@effectiveDate}" 
                  type="static" prefix="{$template/ancestor::decor/project/@prefix}" projectName="{$template[1]/ancestor::decor/project/name[1]}"/>
}
</usage>

