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 %private function local:prepareTransaction($transaction as element(), $storedScenario as element()?) as element() {
    let $storedTransaction  := $storedScenario//transaction[@id = $transaction/@id][@effectiveDate = $transaction/@effectiveDate]
    return
    <transaction>
    {
        $transaction/@id,
        if ($transaction[@effectiveDate castable as xs:dateTime]) then
            $transaction/@effectiveDate
        else 
        if ($transaction/ancestor::*[@effectiveDate castable as xs:dateTime]) then
            ($transaction/ancestor::*/@effectiveDate[. castable as xs:dateTime])[last()]
        else (
            attribute effectiveDate {substring(string(current-dateTime()),1,19)}
        )
        ,
        if ($transaction[@statusCode]) then
            $transaction/@statusCode
        else (
            attribute statusCode {'draft'}
        ),
        $transaction/@versionLabel[not(.='')],
        $transaction/@expirationDate[not(.='')],
        $transaction/@officialReleaseDate[not(.='')],
        $transaction/@type[not(.='')],
        $transaction/@label[not(.='')],
        $transaction/@model[not(.='')],
        $transaction/@canonicalUri[not(.='')],
        attribute lastModifiedDate {substring(string(current-dateTime()), 1, 19)}
        ,
        for $node in $transaction/name
        return
            art:parseNode($node)
        ,
        for $node in $transaction/desc[string-length()>0]
        return
            art:parseNode($node)
        ,
        for $trigger in $transaction/trigger[string-length()>0]
        let $trigger    := art:parseNode($trigger)
        return
            <trigger>{$trigger/@*[not(.='')], $trigger/node()}</trigger>
        ,
        for $node in $transaction/condition[string-length()>0]
        return
            art:parseNode($node)
        ,
        for $node in $transaction/dependencies[string-length()>0]
        return
            art:parseNode($node)
        ,
        (: new ... 2021-05-21 :)
        for $pa in $transaction/publishingAuthority[string-length()>0]
        return 
            <publishingAuthority>
            {
                $pa/@*[not(.='')],
                for $node in $pa/addrLine
                return
                    art:parseNode($node)
            }
            </publishingAuthority>
        ,
        (: new ... 2021-05-21 :)
        for $node in $transaction/property[string-length()>0]
        return
            art:parseNode($node)
        ,
        (: new ... 2021-05-21 :)
        for $node in $transaction/copyright[string-length()>0]
        return
            art:parseNode($node)
        ,
        if ($transaction/@type='group') then
            for $t in $transaction/transaction
            return
                local:prepareTransaction($t, $storedScenario)
        else (
            let $storedTransaction  := 
                if ($transaction[string-length(@effectiveDate)>0]) 
                then $storedScenario//transaction[@id=$transaction/@id][@effectiveDate=$transaction/@effectiveDate]
                else $storedScenario//transaction[@id=$transaction/@id]
            return (
                <actors>
                {
                    for $actor in $transaction/actors/actor[string-length(@id)>0]
                    return
                        <actor id="{$actor/@id}" role="{$actor/@role}"/>
                }
                </actors>
                ,
                if ($transaction/representingTemplate/(@ref|@sourceDataset)[string-length()>0]) then (
                    <representingTemplate>
                    {
                        (:KH/AH 2013-02-21 @displayName is deprecated, so don't save that... Just save known valid attributes:)
                        (:AH 2015-04-26 @sourceDatasetFlexibility new:)
                        $transaction/representingTemplate/(@ref | @flexibility | @sourceDataset | @sourceDatasetFlexibility | @representingQuestionnaire | @representingQuestionnaireFlexibility)[string-length() gt 0],
                        (:merge with stored transaction concepts as these would not be part of a save in the 
                        scenario editor, but would come from the transaction editor. suppose that you edit a 
                        scenario before refreshing the scenario editor, you might actually overwrite what you 
                        have done in the transaction editor. :)
                        if ($storedTransaction/representingTemplate[@sourceDataset=$transaction/representingTemplate/@sourceDataset]) then
                            $storedTransaction/representingTemplate/node()
                        else ()
                    }
                    </representingTemplate>
                ) else ()
            )
        )
    }
    </transaction>
};

declare %private function local:prepareScenario($scenario as element(), $storedScenario as element()?) as element() {
<scenario>
{
    $scenario/@id,
    if ($scenario[@effectiveDate castable as xs:dateTime]) then (
        $scenario/@effectiveDate
    ) 
    else (
        attribute effectiveDate {substring(string(current-dateTime()),1,19)}
    )
    ,
    if ($scenario[@statusCode]) then
        $scenario/@statusCode
    else (
        attribute statusCode {'draft'}
    ),
    $scenario/@versionLabel[not(.='')],
    $scenario/@expirationDate[not(.='')],
    $scenario/@officialReleaseDate[not(.='')],
    $scenario/@canonicalUri[not(.='')],
    attribute lastModifiedDate {substring(string(current-dateTime()), 1, 19)}
    ,
    for $name in $scenario/name
    return
        art:parseNode($name)
    ,
    for $desc in $scenario/desc
    return
        art:parseNode($desc)
    ,
    for $trigger in $scenario/trigger[string-length()>0]
    let $trigger    := art:parseNode($trigger)
    return
        <trigger>{$trigger/@*[not(.='')], $trigger/node()}</trigger>
    ,
    for $node in $scenario/condition[string-length()>0]
    return
        art:parseNode($node)
    ,
    (: new ... 2021-05-21 :)
    for $node in $scenario/publishingAuthority
    return
        <publishingAuthority>
        {
            $node/@id[not(. = '')],
            $node/@name[not(. = '')],
            for $addr in $node/addrLine
            return
                art:parseNode($addr)
        }
        </publishingAuthority>
    ,
    (: new ... 2021-05-21 :)
    for $node in $scenario/property
    return
        art:parseNode($node)
    ,
    (: new ... 2021-05-21 :)
    for $node in $scenario/copyright
    return
        art:parseNode($node)
    ,
    for $transaction in $scenario/transaction
    return
        local:prepareTransaction($transaction, $storedScenario)
}
</scenario>
};

declare %private function local:setNewId($decor as element(), $attr as item(), $base as xs:string) as xs:string {
    let $ids    := $decor//@id[starts-with(.,$base)][matches(.,'\.\d+$')]
    let $newid  := concat($base,'.',if ($ids) then (max($ids/xs:integer(tokenize(.,'\.')[last()]))+1) else (1))
    let $update := update value $attr with $newid
    
    return $newid
};

let $post               := if (request:exists()) then (request:get-data()/scenarios) else ()
let $decor              := $get:colDecorData//decor[project/@prefix=$post/@projectPrefix]

let $update :=
    if ($decor/scenarios) then () else ( 
        update insert <scenarios><actors/></scenarios> preceding $decor/ids 
    )

let $update :=
    for $scenario in $post/scenario[edit/@mode='edit']
    let $storedScenario     := 
        if ($scenario[@effectiveDate[not(.='')]]) then 
            $decor//scenario[@id=$scenario/@id][@effectiveDate=$scenario/@effectiveDate]
        else (
            (:20151021 AH we had a bug where no effectiveDate was added to scenarios:)
            $decor//scenario[@id=$scenario/@id][empty(@effectiveDate)]
        )
    let $preparedScenario   := local:prepareScenario($scenario, $storedScenario)
    let $updateAction       := 
        if ($storedScenario) then
            update replace $storedScenario with $preparedScenario
        else
        if ($decor/scenarios[questionnaire | questionnaireresponse]) then
            update insert $preparedScenario preceding $decor/scenarios/(questionnaire | questionnaireresponse)[1]
        else (
            update insert $preparedScenario into $decor/scenarios
        )
    return
        $preparedScenario

let $lockUpdate         :=
    for $object in $post/scenario[edit/@mode='edit'] | $post/scenario[edit/@mode='edit']//transaction
    return
        update delete decor:getLocks($object/@id, $object/@effectiveDate, (), true())

let $statusUpdate       := update value $decor//scenario[@statusCode='new']/@statusCode with 'draft'
let $statusUpdate       := update value $decor//transaction[@statusCode='new']/@statusCode with 'draft'

let $bugFix             :=
    for $node in    $decor/scenarios/scenario[not(@effectiveDate castable as xs:dateTime)] | 
                    $decor/scenarios//transaction[not(@effectiveDate castable as xs:dateTime)]
    let $att    :=
        if ($node/ancestor::*[@effectiveDate castable as xs:dateTime]) then (
            $node/ancestor::*/@effectiveDate[. castable as xs:dateTime]
        ) else (
            attribute effectiveDate {substring(string(current-dateTime()),1,19)}
        )
    return
        update insert $att[last()] into $node

let $scenariosBase      := (decor:getDefaultBaseIdsP($decor, $decor:OBJECTTYPE-SCENARIO)/@id)[1]
let $transactionsBase   := (decor:getDefaultBaseIdsP($decor, $decor:OBJECTTYPE-TRANSACTION)/@id)[1]

(:20151021 AH we had a bug where ids were malformed on scenarios and transactions:)
let $bugFix             := 
    if (string-length($scenariosBase)=0) then () else (
        for $node in $decor/scenarios/scenario[matches(@id,'[^\d]$')]
        return local:setNewId($decor, $node/@id, $scenariosBase)
    )
(:20151021 AH we had a bug where ids were malformed on scenarios and transactions:)
let $bugFix             := 
    if (string-length($transactionsBase)=0) then () else (
        for $node in $decor/scenarios//transaction[matches(@id,'[^\d]$')]
        return local:setNewId($decor, $node/@id, $transactionsBase)
    )

return
<scenarios/>
