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 decor           = "http://art-decor.org/ns/decor" at "../api/api-decor.xqm";
declare namespace hl7       = "urn:hl7-org:v3";

(: 
    This function updates the existing transaction,
    ignoring the concepts having absent atrribute 
:)

declare %private function local:prepareRepresentingTemplate($transaction as element()?, $savedTransaction as element(), $defaultLanguage as xs:string) as element()? {
    <representingTemplate>
    {
        $savedTransaction/(@sourceDataset | @sourceDatasetFlexibility | @ref | @flexibility | @representingQuestionnaire | @representingQuestionnaireFlexibility | @displayName)[not(.='')]
        ,
        for $concept in $transaction/concept[empty(@absent)]
        return
            local:prepareConcept($concept, $defaultLanguage)
    }
    </representingTemplate>
};

declare %private function local:prepareConcept($concept as element(), $defaultLanguage as xs:string) as item()+ {
    '&#10;                        ',
    comment{concat($concept/@type, ' ', tokenize($concept/@id,'\.')[last()], ' :: ', $concept/name[1])},
    '&#10;                        ',
    <concept ref="{$concept/@id}" flexibility="{$concept/@effectiveDate}">
    {
        if ($concept[@conformance = ('C', 'NP')]) then () else (
            (: keep empty min/max. this happens for incomplete transactions where not all is known yet :)
            $concept/@minimumMultiplicity,
            $concept/@maximumMultiplicity
        )
        ,
        if ($concept[@conformance = 'M']) then (
            attribute isMandatory {'true'}
        ) else (
            $concept/@conformance[not(.='')]
        ),
        $concept/@enableBehavior[not(.='')]
    }
    {
        for $node in $concept/context[string-length(.) gt 0]
        return
            art:parseNode($node)
    }
    {
        if ($concept[@conformance = 'C']) then (
            for $condition in $concept/condition
            return
            <condition>
            {
                if ($condition/@conformance='NP') then () else (
                    (: keep empty min/max. this happens for incomplete transactions where not all is known yet :)
                    $condition/@minimumMultiplicity,
                    $condition/@maximumMultiplicity
                )
                ,
                if ($condition/@conformance='M') then (
                    attribute isMandatory {'true'}
                ) else (
                    $condition/@conformance[not(.='')]
                )
                ,
                $condition/desc[string-length(.) gt 0],
                if ($condition[desc]) then () else if ($condition[string-length(.)=0]) then () else (
                    <desc language="{$defaultLanguage}">{$condition/text()}</desc>
                )
            }
            </condition>
        )
        else (),
        $concept/enableWhen
    }
    {
        (: prepare them while allowing for 'empty' associations, that don't bind to anything, signalling deletion in the context of this transaction :)
        for $node in $concept/terminologyAssociation
        return
            art:prepareTerminologyAssociationForUpdate($node, true())
        ,
        for $node in $concept/identifierAssociation
        return
            art:prepareIdentifierAssociationForUpdate($node, true())
    }
    {
        (: new in AD 3.10 - no real support, just make sure we don't loose it :)
        $concept/containedTransaction
    }
    </concept>
    ,
    for $childconcept in $concept/concept[empty(@absent)]
    return
        local:prepareConcept($childconcept, $defaultLanguage)
};

declare %private function local:mergeDatasets($savedTransaction as element(transaction), $mergeDataset as element(dataset), $conceptId as xs:string?, $conceptEffectiveDate as xs:string?) as element(dataset) {
    if (empty($conceptId)) then (
        (: get full thing :)
        element {name($mergeDataset)} {
            $mergeDataset/(@* except (@transactionId | @transactionEffectiveDate | @templateId | @templateEffectiveDate)),
            attribute transactionId {$savedTransaction/@id},
            attribute transactionEffectiveDate {$savedTransaction/@effectiveDate},
            if ($savedTransaction[representingTemplate/@ref]) then (
                attribute templateId {$savedTransaction/representingTemplate/@ref},
                if ($savedTransaction[representingTemplate/@flexibility]) then
                    attribute templateEffectiveDate {$savedTransaction/representingTemplate/@flexibility}
                else ()
            ) else (),
            $mergeDataset/node()
        }
    )
    else (
        let $savedDataset   := art:getDatasetTree((), (), $savedTransaction/@id, $savedTransaction/@effectiveDate, true())
        return
        element {name($mergeDataset)} {
            $mergeDataset/(@* except (@transactionId | @transactionEffectiveDate | @templateId | @templateEffectiveDate)),
            attribute transactionId {$savedTransaction/@id},
            attribute transactionEffectiveDate {$savedTransaction/@effectiveDate},
            if ($savedTransaction[representingTemplate/@ref]) then (
                attribute templateId {$savedTransaction/representingTemplate/@ref},
                if ($savedTransaction[representingTemplate/@flexibility]) then
                    attribute templateEffectiveDate {$savedTransaction/representingTemplate/@flexibility}
                else ()
            ) else (),
            for $concept in $savedDataset/node()
            return
                local:mergeConcepts($concept, $mergeDataset//concept[@id = $conceptId][@effectiveDate = $conceptEffectiveDate])
        }
    )
};

declare %private function local:mergeConcepts($concept as item(), $mergeConcept as element(concept)) as item() {
    if ($concept[@id = $mergeConcept/@id][@effectiveDate = $mergeConcept/@effectiveDate]) then
        $mergeConcept
    else
    if ($concept/descendant::concept[@id = $mergeConcept/@id][@effectiveDate = $mergeConcept/@effectiveDate]) then (
        element concept {
            $concept/(@* except @absent),
            for $subconcept in $concept/node()
            return
                local:mergeConcepts($subconcept, $mergeConcept)
        }
    )
    else ($concept)
};

(:let $transaction    := request:get-data()/dataset:)
let $editedAction               := if (request:exists()) then request:get-data()/* else (
    <!-- clone the concept ....3.2.1 and descendants from transaction ...3.4.2 into transaction ...3.4.3 --> |
    <transaction transactionId="2.16.840.1.113883.3.1937.99.62.3.4.3" transactionEffectiveDate="2012-09-05T16:59:35" 
                            id="2.16.840.1.113883.3.1937.99.62.3.4.2" effectiveDate="2012-09-05T16:59:35" 
                     conceptId="2.16.840.1.113883.3.1937.99.62.3.2.1" conceptEffectiveDate="2012-05-30T11:32:36"/>
)
let $deleteLock                 := if (request:exists()) then not(request:get-parameter('deletelock','true')='false') else (true())
let $cloneIntoTrxId             := if ($editedAction) then $editedAction/@transactionId[string-length() gt 0] else ()
let $cloneIntoTrxEd             := if ($cloneIntoTrxId) then $editedAction/@transactionEffectiveDate[string-length() gt 0] else ()
let $cloneFromTrxId             := if ($cloneIntoTrxId) then $editedAction/@id[string-length() gt 0] else ()
let $cloneFromTrxEd             := if ($cloneFromTrxId) then $editedAction/@effectiveDate[string-length() gt 0] else ()
let $cloneFromCptId             := if ($cloneFromTrxId) then $editedAction/@conceptId[string-length() gt 0] else ()
let $cloneFromCptEd             := if ($cloneFromCptId) then $editedAction/@conceptEffectiveDate[string-length() gt 0] else ()

let $transactionId              := $cloneIntoTrxId
let $transactionEffectiveDate   := if ($editedAction/@transactionEffectiveDate) then $editedAction/@transactionEffectiveDate else 'dynamic'

let $lock                       := decor:getLocks($transactionId, $transactionEffectiveDate, (), true())
let $savedTransaction           := art:getTransaction($transactionId, $transactionEffectiveDate)
let $defaultLanguage            := $savedTransaction/ancestor::decor/project/@defaultLanguage

let $editedTransaction          := 
    if ($editedAction[self::dataset]) then $editedAction else (
        let $mergeDataset       := art:getDatasetTree((), (), $cloneFromTrxId, $cloneFromTrxEd, true())
        return
            local:mergeDatasets($savedTransaction, $mergeDataset, $cloneFromCptId, $cloneFromCptEd)
    )

let $userCanEdit        :=
    if ($savedTransaction) then
        decor:authorCanEditP($savedTransaction/ancestor::decor, $decor:SECTION-SCENARIOS)
    else (false())
let $userlocks          := 
    if ($savedTransaction) then 
        decor:getLocks($transactionId, $transactionEffectiveDate, (), true())
    else ()
let $alllocks           :=
    if ($savedTransaction) then
        decor:getLocks($transactionId, $transactionEffectiveDate, (), false())
    else ()
let $locks              := 
    if ($userlocks) then $userlocks else if ($alllocks) then () else if ($userCanEdit) then (<pseudo-lock/>) else ()

let $response :=
    (:cannot update a transaction that does not exist:)
    if ($savedTransaction) then (
        (:check if user id author in project:)
        if ($userCanEdit) then (
            (:if there is a lock this object already is locked:)
            if ($locks) then (
                let $preparedRepTemp    := local:prepareRepresentingTemplate($editedTransaction, $savedTransaction/representingTemplate, $defaultLanguage)
                let $update             := update replace $savedTransaction/representingTemplate with $preparedRepTemp
            
                let $delete             := if ($deleteLock) then decor:deleteLock($transactionId, $transactionEffectiveDate, ()) else ()
                
                return <data-safe>true</data-safe>
            )
            else (
                <data-safe error="{concat('You don''t have permission to save this transaction/representingTemplate as you have no lock for the transaction with id="',$transactionId,'" effectiveDate="',$transactionEffectiveDate,'" user="',get:strCurrentUserName(),'"')}">false</data-safe>
            )
        )
        else (
            <data-safe error="{concat('You don''t have permission to save this transaction/representingTemplate. user="',get:strCurrentUserName(),'"')}">false</data-safe>
        )
    )
    else (
        <data-safe error="{concat('It''s not possible to save this data as the transaction with id="',$transactionId,'" and effectiveDate="',$transactionEffectiveDate,'" cannot be found.')}">false</data-safe>
    )

return
    $response
