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 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-server-settings.xqm";
import module namespace adserver    = "http://art-decor.org/ns/art-decor-server" at "../../api/api-server-settings.xqm";
declare namespace request   = "http://exist-db.org/xquery/request";
declare namespace datetime  = "http://exist-db.org/xquery/datetime";

(:When called with localAssets=true then we need relative local paths, else 
    we need our server services URL, but with matching scheme (http or https)
    If the scheme is https and servicesUrl is http this leads to a security 
    problem in some browsers
:)
declare variable $resourcePath          := 'https://assets.art-decor.org/ADAR/rv/assets';

(: This date is used for the new dataset and any child concepts :)
declare variable $now1      := format-dateTime(current-dateTime(), '[Y0001]-[M01]-[D01]T[H01]:[m01]:[s01]');
declare variable $now2      := format-dateTime(current-dateTime(), '[Y0001]-[M01]-[D01]T00:00:00');
declare variable $keepIds   := if (request:exists()) then request:get-parameter('keepIds','true') else ('true');
declare variable $updateta  := if (request:exists()) then request:get-parameter('templateassociations','true') else ('false');

declare %private function local:inheritConcept($concept as element(), $statusCode as xs:string, $baseId as xs:string, $decor as element(decor), $now as xs:string, $includeDeprecated as xs:boolean) as item()* {
(:
if keep ids then only the effective date is set to now
if not keep ids then 
    (1) either new ids are generated (later) based on base id for datalelements
    (2) or if the format of the id is datalement.version.itemid then the new id is datalement.version+1.itemid
:)
let $deid               := $concept/@id
let $deed               := $concept/@effectiveDate

let $nextdelmLeaf       := $decor/datasets/@nextConceptLeaf
let $id                 := if ($keepIds = 'true') then $deid else concat($baseId, '.', $nextdelmLeaf)
let $nextid             := update value $nextdelmLeaf with xs:integer($nextdelmLeaf) + 1

let $originalConcept    := art:getOriginalForConcept($concept)

let $nme                := $originalConcept/name[1]
(: Use id of concept this concept inherited from if applicable, else use id :)
(:let $inheritId          := if ($concept/inherit) then ($concept/inherit/@ref) else ($cid):)
(: Use effectiveDate of concept this concept inherited from if applicable, else use effectiveDate :)
(:let $inheritEff         := if ($concept/inherit) then ($concept/inherit/@effectiveDate) else ($eff):)
return (
    comment {'Inherits from: ',$nme,' (status:',$concept/@statusCode/string(),', type:',$originalConcept/@type/string(),')'},
    <concept id="{$id}" statusCode="{$statusCode}" effectiveDate="{$now}" lastModifiedDate="{$now}" oldId="{$deid}" oldEffectiveDate="{$deed}">
        <inherit ref="{$deid}" effectiveDate="{$deed}"/>
    {
        for $subConcept in $concept/concept
        return
            switch ($subConcept/@statusCode) 
            case 'deprecated'   return if ($includeDeprecated) then local:inheritConcept($subConcept, $subConcept/@statusCode, $baseId, $decor, $now, $includeDeprecated) else ()
            case 'cancelled'    return ()
            case 'rejected'     return ()
            default             return local:inheritConcept($subConcept, 'draft', $baseId, $decor, $now, $includeDeprecated)
    }
    </concept>
)
};

declare %private function local:copy-ds($input as node()) {
    let $xslt := 
        <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
             <xsl:template match="concept">
                <xsl:copy>
                     <xsl:apply-templates select="@* except (@oldId | @oldEffectiveDate)"/>
                     <xsl:apply-templates select="node()"/>
                </xsl:copy>
             </xsl:template>
             <xsl:template match="@*|node()">
                 <xsl:copy>
                     <xsl:apply-templates select="@*|node()"/>
                 </xsl:copy>
             </xsl:template>
         </xsl:stylesheet>
    return transform:transform($input, $xslt, ())
};

(:
<templateAssociation templateId="2.16.840.1.113883.2.4.3.36.10.900804" effectiveDate="2012-07-04T00:00:00">
    <concept ref="2.16.840.1.113883.2.4.3.36.77.2.1.150050" effectiveDate="2012-04-10T00:00:00" elementId="2.16.840.1.113883.2.4.3.36.10.900804.1"/>
    <concept ref="2.16.840.1.113883.2.4.3.36.77.2.2.150050" effectiveDate="2013-11-03T00:00:00" elementId="2.16.840.1.113883.2.4.3.36.10.900804.1"/>
    <concept ref="2.16.840.1.113883.2.4.3.36.77.2.3.150050" effectiveDate="2014-07-12T00:00:00" elementId="2.16.840.1.113883.2.4.3.36.10.900804.1"/>
</templateAssociation>
:)
declare %private function local:add-template-assocs($decor as node(), $concept as node(), $commitUpdate as xs:boolean) as item()* {
    for $templateAssociation in $decor//templateAssociation[concept[@ref=$concept/@oldId][@effectiveDate=$concept/@oldEffectiveDate]]
    return (
        <templateAssociation>
        {
            $templateAssociation/@*,
            for $association in $templateAssociation/concept[@ref=$concept/@oldId][@effectiveDate=$concept/@oldEffectiveDate]
            let $newAssociation     :=
                <concept ref="{$concept/@id}" effectiveDate="{$concept/@effectiveDate}">
                {
                    $association/(@* except (@ref | @effectiveDate))
                }
                </concept>
            let $dummy1 := 
                if ($commitUpdate) then (update insert $newAssociation following $templateAssociation/*[last()]) else ()
            return
                $newAssociation
        }
        </templateAssociation>
    )
    ,
    for $subconcept in $concept/concept
    return
        local:add-template-assocs($decor, $subconcept, $commitUpdate)
};

(: get all projects for drop-down population :)
declare %private function local:getAllProjects($projectPrefix as xs:string*, $language as xs:string?) as element(project)* {
    for $decor in collection($get:strDecorData)//decor[not(@private='true')] | art:getDecorByPrefix($projectPrefix)
    let $isDBA          := try {sm:is-dba(get:strCurrentUserName())} catch * {()}
    let $isAuthor       := if ($isDBA) then true() else decor:authorCanEditP($decor, $decor:SECTION-DATASETS)
    let $isBBR          := xs:string($decor/@repository='true')
    let $project        := $decor/project
    let $projectName    := if ($project/name[@language=$language]) then $project/name[@language=$language][1] else $project/name[1]
    where $isAuthor
    order by $isBBR, $projectName/lower-case(normalize-space())
    return
        <project>
        {
            $project/@id,
            $project/@prefix,
            $project/@defaultLanguage,
            attribute repository {$isBBR},
            attribute private {$project/parent::decor/@private='true'},
            attribute experimental {$project/@experimental='true'},
            attribute name {$projectName},
            attribute creationdate {xmldb:created(util:collection-name($project),util:document-name($project))},
            attribute modifieddate {xmldb:last-modified(util:collection-name($project),util:document-name($project))}
        }
        </project>
        
};

(: expect prefix|id|effectiveDate :)
let $projectPrefix          := if (request:exists()) then request:get-parameter('prefix', ())[not(. = '')] else ('jgz-')
let $datasetKey             := if (request:exists()) then request:get-parameter('dataset', ()) else ('2.16.840.1.113883.2.4.3.11.60.100.1.1|2012-06-08T12:42:06')
let $now                    := if (request:exists()) then request:get-parameter('now', ()) else ()
let $datasetId              := tokenize($datasetKey,'\|')[1]
let $datasetEffectiveDate   := tokenize($datasetKey,'\|')[2]


(: show or download :)
let $action                 := if (request:exists()) then request:get-parameter('action', 'show') else ('show')

let $includeDeprecated      := if (request:exists()) then request:get-parameter('includeDeprecated', 'true') else ('false')
let $includeDeprecated      := if ($includeDeprecated = ('', 'true')) then true() else false()

let $nmmax                  := 15
let $language               := if (request:exists()) then request:get-parameter('language',()) else ('nl-NL')
let $language               := if ($language[string-length()>0]) then $language else (adserver:getServerLanguage())

let $allProjects            := local:getAllProjects($projectPrefix, $language)
let $projectPrefix          := 
    if (string-length(normalize-space($projectPrefix))=0) then ($allProjects/@prefix)[1] else ($projectPrefix)

let $decor                  := if (empty($projectPrefix)) then () else art:getDecorByPrefix($projectPrefix)
let $baseIdsDS              := if (empty($decor)) then () else decor:getBaseIdsP($decor, $decor:OBJECTTYPE-DATASET)
let $baseIdsDE              := if (empty($decor)) then () else decor:getBaseIdsP($decor, $decor:OBJECTTYPE-DATASETCONCEPT)
let $defaultBaseIds         := if (empty($decor)) then () else decor:getDefaultBaseIdsP($decor, $decor:OBJECTTYPE-DATASETCONCEPT)

(: don't allow a baseId for dataset concept unless it is defined in the project :)
let $baseIdDE               := if (request:exists()) then request:get-parameter('baseIdDE',())[. = $baseIdsDE/@id] else ()
let $dfltDE                 := if (empty($baseIdDE)) then $defaultBaseIds/@id else $baseIdDE

return
    if (string-length(normalize-space($projectPrefix))=0 or string-length(normalize-space($datasetKey))=0) 
    then (
        if (response:exists()) then response:set-header('Content-Type','text/html; charset=utf-8') else (),
        <html>
            <head>
                <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
                <title>Create-new-decor-dataset-version</title>
                <link href="{$resourcePath}/css/default.css" rel="stylesheet" type="text/css"/>
                <style>tr:nth-child(even) {{ vertical-align: top; background-color: #eee; }}</style>
                <script>
                    function toggleDisplay(id) {{
                        var elm = document.getElementById(id);
                        if (elm != null) {{
                            if (elm.style.opacity == '1') {{ elm.style.opacity = '0.5'; }} else {{ elm.style.opacity = '1'; }}
                        }}
                    }};
                </script>
            </head>
            <body>
                <h1>Create new decor dataset version</h1>
                <div class="content">
                <form name="input" action="/art/modules/create-new-decor-dataset-version2.xquery" method="post">
                    <table border="0">
                        <tr>
                            <td>Project:</td>
                            <td>
                                <select name="prefix" id="prefixSelector" onchange="javascript:location.href=window.location.pathname+'?'{
                                    for $q in ('prefix','includeDeprecated','keepIds','now','templateassociations','action')
                                    return
                                    concat('+''',$q,'=''+document.getElementById(''',$q,'Selector'').options[document.getElementById(''',$q,'Selector'').selectedIndex].value+''&amp;''')
                                }" style="width: 500px;">
                                {
                                    for $p in $allProjects
                                    let $dprefix    := $p/@prefix
                                    return
                                        <option value="{$dprefix}">
                                        {
                                            if ($dprefix=$projectPrefix) then attribute {'selected'} {'true'} else (),
                                            if ($p/@repository='true') then '(BBR) ' else (),
                                            concat($p/@name,' (',$p/@prefix,' | ',$p/@defaultLanguage,')')
                                        } 
                                        </option>
                                }
                                </select>
                            </td>
                            <td>Required</td>
                        </tr>
                        <tr>
                            <td>Dataset:</td>
                            <td>
                            {
                                if (string-length($projectPrefix) = 0) then
                                    <div style="font-style: italic;">Please select a project first</div>
                                else (
                                    <select name="dataset" id="datasetSelector" style="width: 500px;">
                                    {
                                        for $q in art:getDecorByPrefix($projectPrefix)/datasets/dataset
                                        let $did    := $q/@id/string()
                                        let $ded    := $q/@effectiveDate/string()
                                        let $dnm    := if ($q/name[@language=$language]) then $q/name[@language=$language] else ($q/name[1])
                                        return
                                            <option value="{concat($did,'|',$ded)}">
                                            {
                                                if ($q[concat(@id,'|',@effectiveDate) = $datasetKey]) then attribute selected {'true'} else ()
                                                ,
                                                concat($dnm,' ',$q/@versionLabel,' ID ',art:getNameForOID($did, $language, $q/ancestor::decor),' (',$ded,')')
                                            }
                                            </option>
                                    }
                                    </select>
                                )
                            }
                            </td>
                            <td>Required. Determines the base dataset to create the new dataset from.</td>
                        </tr>
                        <tr>
                            <td>Include deprecated:</td>
                            <td>
                                <select name="includeDeprecated" id="includeDeprecatedSelector">
                                    <option value="true">Yes</option>
                                    <option value="false">No</option>
                                </select>
                            </td>
                            <td><ul>
                            <li>If "Yes" then deprecated concepts in the base dataset/templateAssociations are included. This is recommended</li>
                            <li>If "No" the deprecated concepts in the base dataset/templateAssociations are NOT included</li></ul>
                            <div>Note that cancelled, and rejected concept are never included as they are supposed to never have been published. Deprecated concepts however have been published and (potentially) implemented.</div>
                            </td>
                        </tr>
                        <tr>
                            <td>Keep IDs:</td>
                            <td>
                                <select name="keepIds" id="keepIdsSelector" onchange="javascript:toggleDisplay('conceptIdRow')">
                                    <option value="true">Yes</option>
                                    <option value="false">No</option>
                                </select>
                            </td>
                            <td><ul>
                            <li>If "Yes" then only the effective date is set to now, ids of concept are kept.</li>
                            <li>If "No" new ids are generated based on base id for datalelements and effective date is set to now OR<br/>
                            ... if the format of the id is datalement.version.itemid then the new id is datalement.version+1.itemid</li></ul>
                            </td>
                        </tr>
                        <tr id="conceptIdRow" style="opacity: {if ($keepIds = 'true') then '0.5' else '1'}">
                            <td>Concept Id Root:</td>
                            <td>
                                <select name="baseIdDE" id="baseIdDESelector" style="width: 500px;">
                                {
                                    for $p in $baseIdsDE
                                    order by $p/@id
                                    return
                                        <option value="{$p/@id}">
                                        {
                                            if ($p/@id = $dfltDE) then attribute {'selected'} {'true'} else (),
                                            concat($p/@id,' - ',$p/@prefix),
                                            if ($p/@id = $defaultBaseIds/@id) then ' (default)' else ()
                                        } 
                                        </option>
                                }
                                </select>
                            </td>
                            <td>Optional. SHALL match as defined baseId in the project for dataset concepts (type=DE). Only relevant if Keep IDs is off, i.e. new ids need to be generated. Determines which baseId shall be used for generating those. If Keep IDs is off and no baseId is specified, the ids are based on the projects defaultBaseId</td>
                        </tr>
                        <tr>
                            <td>Choose effectiveDate:</td>
                            <td>
                                <select name="now" id="nowSelector">
                                    <option value="{$now1}">{$now1}</option>
                                    <option value="{$now2}" selected="true">{$now2}</option>
                                </select>
                            </td>
                            <td>Select full date time or date only. Determines the @effectiveDate on the new dataset and contained concepts.</td>
                        </tr>
                        <tr>
                            <td>Commit template associations:</td>
                            <td>
                                <select name="templateassociations" id="templateassociationsSelector">
                                    <option value="true">Yes</option>
                                    <option value="false" selected="true">No</option>
                                </select>
                            </td>
                            <td>
                            <ul>
                            <li>If "Yes" then template associations are generated as copy of existing template associations for the selected dataset concepts and are immediately committed in the project<br/><b>Note that this immediately affects your project and that there is no undo possible!!</b></li>
                            <li>If "No" then template associations are generated and returned, but not committed to the project.</li>
                            </ul>
                            </td>
                        </tr>
                        <tr>
                            <td>Action:</td>
                            <td>
                                <select name="action" id="actionSelector">
                                    <option value="show">{if ($action='show') then attribute {'selected'} {'true'} else ()}Show</option>
                                    <option value="download">{if ($action='download') then attribute {'selected'} {'true'} else ()}Download</option>
                                    <option value="commit">{if ($action='commit') then attribute {'selected'} {'true'} else ()}Commit</option>
                                </select>
                            </td>
                            <td/>
                        </tr>
                        <tr>
                            <td style="padding-top: 1em;">&#160;</td>
                            <td style="padding-top: 1em;"><input type="submit" value="Submit" style="color: inherit;"/></td>
                            <td style="padding-top: 1em;">&#160;</td>
                        </tr>
                    </table>
                </form>
                </div>
            </body>
        </html>
    ) 
    else (
        if (response:exists()) then (
            response:set-header('Content-Type','text/xml; charset=utf-8'),
            if ($action = 'download') then (response:set-header('Content-Disposition', concat('attachment; filename=',$projectPrefix,$datasetId,'-',replace($now,':',''),'.xml'))) else ()
        ) else (),
        let $decor          := art:getDecorByPrefix($projectPrefix)
        let $sourceDataset  := art:getDataset($datasetId, $datasetEffectiveDate)
        
        let $isDBA          := try {sm:is-dba(get:strCurrentUserName())} catch * {()}
        let $isAuthor       := if ($isDBA) then true() else decor:authorCanEditP($decor, $decor:SECTION-DATASETS)
        
        (:<next base="1.2" max="2" next="{$max + 1}" id="1.2.3" type="DS"/>:)
        let $newDatasetId   := if ($keepIds = 'true') then $sourceDataset/@id else (decor:getNextAvailableIdP($decor, $decor:OBJECTTYPE-DATASET, ())/@id)
        
        let $delmbaseId     := decor:getNextAvailableIdP($decor, $decor:OBJECTTYPE-DATASETCONCEPT, $baseIdDE)
        let $nextdelmId     := $delmbaseId/@id
        
        (: keep counter in db, so we make sure we don't issue the same id twice for sibling concepts :)
        let $insert         :=
            if ($decor/datasets[@nextConceptLeaf]) then
                update value $decor/datasets/@nextConceptLeaf with xs:integer($delmbaseId/@max) + 1
            else (
                update insert attribute nextConceptLeaf {xs:integer($delmbaseId/@max) + 1} into $decor/datasets
            )
        
        let $result         :=
            <dataset id="{$newDatasetId}" statusCode="new" effectiveDate="{$now}" lastModifiedDate="{$now}">
            {
                $sourceDataset/(* except concept)
                ,
                <relationship type="VERSION" ref="{$sourceDataset/@id}" flexibility="{$sourceDataset/@effectiveDate}"/>
                ,
                for $node in $sourceDataset/concept 
                return
                    switch ($node/@statusCode) 
                    case 'pending'      return local:inheritConcept($node, $node/@statusCode, $dfltDE, $decor, $now, $includeDeprecated)
                    case 'deprecated'   return if ($includeDeprecated) then local:inheritConcept($node, $node/@statusCode, $dfltDE, $decor, $now, $includeDeprecated) else ()
                    case 'cancelled'    return ()
                    case 'rejected'     return ()
                    default             return local:inheritConcept($node, 'draft', $dfltDE, $decor, $now, $includeDeprecated)
            }
            </dataset>
        
        let $delete         := update delete $decor/datasets/@nextConceptLeaf
        
        (: get rid of helper attribute //concept/(@oldId | @oldEffectiveDate) :)
        let $cleands        := local:copy-ds($result)
        let $commitds       := 
            if ($action = 'commit' and $isAuthor) then 
                update insert $cleands into $decor/datasets
            else ()
            
        let $resulttas      := 
            for $subconcept in $result/concept
            return
                local:add-template-assocs($decor, $subconcept, $updateta='true')
        let $resulttas      :=
            for $resultta in $resulttas
            let $ideff      := $resultta/concat(@templateId,@effectiveDate)
            group by $ideff
            return
                element {$resultta[1]/name()} { $resultta[1]/@*, $resultta/node() }
            
        let $resultsc       := ()
        
        return
            <result action="{$action}" prefix="{$projectPrefix}" keepids="{$keepIds}" commitassociations="{$updateta='true'}">
            {
                $cleands,
                $resultsc,
                $resulttas
            }
            </result>
    )