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.
:)
import module namespace art             = "http://art-decor.org/ns/art" at "art-decor.xqm";
import module namespace get             = "http://art-decor.org/ns/art-decor-settings" at "art-decor-settings.xqm";
import module namespace adserver        = "http://art-decor.org/ns/art-decor-server" at "../api/api-server-settings.xqm";

declare %private function local:addValueSetRef($decor as element(), $repoPrefix as xs:string, $association as element()) as item()* {
    let $repoDecor          := art:getDecorByPrefix($repoPrefix)
    
    let $repoVs             := $repoDecor/terminology/valueSet[(@id|@ref|@name)=$association/@valueSet][1]
    let $projectVs          := $decor/terminology/valueSet[(@id|@ref|@name)=$association/@valueSet][1]
    
    let $valueSetRefElm     := <valueSet ref="{$repoVs/(@id|@ref)}" name="{$repoVs/@name}">{$repoVs/@displayName}</valueSet>
    
    (: request:get-hostname() will give 127.0.0.1 :)
    let $hostNameAndPort    := if (request:exists()) then replace(adserver:getServerURLServices(),'^https?://?([^/]+).*','$1') else ('localhost:8877')
    let $buildingBlockElm   := <buildingBlockRepository url="http://{$hostNameAndPort}/decor/services/" ident="{$repoPrefix}"/>
    
    let $addValueSetRef      :=
        if (empty($projectVs) or $projectVs/(@id|@ref) != $repoVs/(@id|@ref)) then (
            let $dummy1 := update insert $valueSetRefElm following $decor/terminology/*[last()]
            let $dummy2 := 
                if (not($decor/project/buildingBlockRepository[@url=$buildingBlockElm/@url][@ident=$buildingBlockElm/@ident][empty(@format)] |
                        $decor/project/buildingBlockRepository[@url=$buildingBlockElm/@url][@ident=$buildingBlockElm/@ident][@format='decor'])) then (
                    let $dummy3 := update insert $buildingBlockElm following $decor/project/(author|reference|restURI|defaultElementNamespace|contact)[last()]
                    return 'true'
                ) else ('false')
            
            return if ($dummy2='true') then 'ref-and-bbr' else 'ref'
        ) 
        else ()
    
    return
        if ($addValueSetRef='ref-and-bbr') then
            <info>The terminology association points to value set {$valueSetRefElm/@name/string()} ({$valueSetRefElm/@ref/string()}) that did not exist yet, so a value set reference was created. A building 
                block repository link with url {$buildingBlockElm/@url/string()} and ident {$buildingBlockElm/@ident/string()} was also created to make this reference work.</info>
        else if ($addValueSetRef='ref') then
            <info>The terminology association points to value set {$valueSetRefElm/@name/string()} ({$valueSetRefElm/@ref/string()}) that did not exist yet, so a value set was created. The building 
                block repository link required to make this reference work already existed so it was not created.</info>
        else ()
};

let $terminologyAssociation     := if (request:exists()) then request:get-data()/association else (
    (:<association bind="code" conceptId="2.16.840.1.113883.3.1937.99.62.3.2.5.1" conceptFlexibility=""
        code="DM" codeSystem="2.16.840.1.113883.3.1937.99.62.3.5.2" displayName="Datum meting" codeSystemName="" codeSystemVersion="" equivalence="" 
        valueSet="" flexibility="" strength="" 
        effectiveDate="" expirationDate="" versionLabel="" 
        prefix="demo-1" inheritFromPrefix="demo1-" parentConceptId="2.16.840.1.113883.3.1937.99.62.3.2.5" parentConceptFlexibility="2011-01-28T00:00:00" transactionId="2.16.840.1.113883.3.1937.99.62.3.4.2" transactionEffectiveDate="2012-09-05T16:59:35"/>:)
)
let $projectPrefix              := $terminologyAssociation/@prefix[string-length() gt 0]
let $transactionId              := $terminologyAssociation/@transactionId[string-length() gt 0]
let $transactionEffectiveDate   := $terminologyAssociation/@transactionEffectiveDate[string-length() gt 0]

(: relevant for transaction bindings to conceptList/concept to know which concept to add them to :)
let $conceptId                  := if ($terminologyAssociation/@parentConceptId[string-length() gt 0]) then $terminologyAssociation/@parentConceptId else $terminologyAssociation/@conceptId
let $conceptEffectiveDate       := if ($terminologyAssociation/@parentConceptFlexibility[string-length() gt 0]) then $terminologyAssociation/@parentConceptFlexibility else $terminologyAssociation/@conceptFlexibility

let $newAssociation             := art:prepareTerminologyAssociationForUpdate($terminologyAssociation)

let $concept                    :=
    if ($transactionId) then
        art:getTransactionConcept($conceptId, $conceptEffectiveDate, $transactionId, $transactionEffectiveDate, (), ())
    else (
        art:getConcept($conceptId, $conceptEffectiveDate)
    )
    
let $decor                      := 
    if (empty($projectPrefix)) then
        $concept/ancestor::decor
    else (
        art:getDecorByPrefix($projectPrefix)
    )

(: should not have multiple connections to the same valueSet (regardless of version), so delete those first in case we're updating strength :)
let $deleteAssociations         :=
    if ($newAssociation[@valueSet]) then 
        if ($transactionId) then
            update delete $concept/terminologyAssociation[@conceptId = $newAssociation/@conceptId][@valueSet = $newAssociation/@valueSet]
        else (
            update delete $decor/terminology/terminologyAssociation[@conceptId = $newAssociation/@conceptId][@valueSet = $newAssociation/@valueSet]
        )
    else ()
(: should not have multiple connections to the same code/codeSystem (regardless of version), so delete those first in case we're updating equivalence :)
let $deleteAssociations         :=
    if ($newAssociation[@code]) then
        if ($transactionId) then
            update delete $concept/terminologyAssociation[@conceptId = $newAssociation/@conceptId][@code = $newAssociation/@code][@codeSystem = $newAssociation/@codeSystem]
        else (
            update delete $decor/terminology/terminologyAssociation[@conceptId = $newAssociation/@conceptId][@code = $newAssociation/@code][@codeSystem = $newAssociation/@codeSystem]
        )
    else ()
    
let $updateTerminology          :=
    if ($decor/terminology) then () else (
        update insert <terminology/> following $decor/ids
    )

let $updateAssociation          :=
    if ($transactionId) then (
        update insert $newAssociation into $concept
    )
    else
    if ($decor/terminology/terminologyAssociation) then (
        update insert $newAssociation following $decor/terminology/terminologyAssociation[last()]
    )
    else 
    if ($decor/terminology/*) then (
        update insert $newAssociation preceding $decor/terminology/*[1]
    )
    else (
        update insert $newAssociation into $decor/terminology
    )

let $updateAssociation          :=
    if ($newAssociation[@valueSet] and $terminologyAssociation[string-length(@inheritFromPrefix) gt 0][not(@inheritFromPrefix = $projectPrefix)]) then
        local:addValueSetRef($decor, $terminologyAssociation/@inheritFromPrefix, $terminologyAssociation)
    else ()

return
    <data-safe>{true()}</data-safe>