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-decor.xqm";
import module namespace aduser          = "http://art-decor.org/ns/art-decor-users" at "../api/api-user-settings.xqm";

declare variable $concept           := if (request:exists() and request:get-parameter('debug','false')='false') then request:get-data()/concept else ();
declare variable $inheritType       := if (request:exists()) then request:get-parameter('inheritType', 'designcopy') else ('containment');
declare variable $selectedConcepts  := map:merge(
    for $id in tokenize($concept/concepts,'\s')
    return
        map:entry($id, ())
);
declare variable $baseId            := concat(string-join(tokenize($concept/@id,'\.')[position()!=last()],'.'),'.');

declare %private function local:resolveInherit($concept as element(), $level as xs:integer) as element(concept)? {
    if ($concept/inherit/@ref) then
        let $storedConcept          := art:getConcept($concept/@id, $concept/@effectiveDate)
        let $inheritedConcept       := art:getConcept($concept/inherit/@ref, $concept/inherit/@effectiveDate)
        let $projectPrefix          := $inheritedConcept/ancestor::decor/project/@prefix
        
        let $originalConcept        := art:getOriginalForConcept($inheritedConcept)
        let $associations           := art:getConceptAssociations($inheritedConcept)
        
        let $datasets               := $storedConcept/ancestor::datasets
        let $setmaxcounter          := if ($datasets[@maxcounter]) then () else update insert attribute maxcounter {()} into $datasets
        
        (:build history before adding in new concepts:)
        let $history                := 
            if ($level=0) then (
                <history validTimeHigh="{format-dateTime(current-dateTime(), '[Y0001]-[M01]-[D01]T[H01]:[m01]:[s01]')}">
                {
                    art:removeGroupContent($storedConcept)
                }
                </history>
            ) else ()
        
        let $editedConcept          := 
        <concept navkey="{util:uuid()}" id="{$concept/@id}" statusCode="{$concept/@statusCode}" effectiveDate="{$concept/@effectiveDate}" 
                 expirationDate="{$concept/@expirationDate}" officialReleaseDate="{$concept/@officialReleaseDate}"
                 versionLabel="{$concept/@versionLabel}" type="{$originalConcept/@type}" canonicalUri="{$concept/@canonicalUri}">
        {
            attribute lastModifiedDate {format-dateTime(current-dateTime(), '[Y0001]-[M01]-[D01]T[H01]:[m01]:[s01]')}
        }
        {
            $concept/edit,
            $concept/lock[@type = $decor:OBJECTTYPE-DATASETCONCEPT],
            if ($inheritType = 'containment') then
                <contains ref="{$concept/inherit/@ref}" flexibility="{$concept/inherit/@effectiveDate}">
                {
                    $projectPrefix,
                    attribute datasetId {$inheritedConcept/ancestor::dataset/@id},
                    attribute datasetEffectiveDate {$inheritedConcept/ancestor::dataset/@effectiveDate},
                    attribute iType {$originalConcept/@type}, 
                    attribute iStatusCode {$inheritedConcept/@statusCode}, 
                    if ($inheritedConcept[@expirationDate]) then attribute iExpirationDate {$inheritedConcept/@expirationDate} else (),
                    if ($inheritedConcept[@versionLabel]) then attribute iVersionLabel {$inheritedConcept/@versionLabel} else (),
                    attribute iddisplay {art:getNameForOID($concept/inherit/@ref, $inheritedConcept/ancestor::decor/project/@defaultLanguage, $inheritedConcept/ancestor::decor)}
                }
                </contains>
            else (
                <inherit ref="{$concept/inherit/@ref}" effectiveDate="{$concept/inherit/@effectiveDate}">
                {
                    $projectPrefix,
                    attribute datasetId {$inheritedConcept/ancestor::dataset/@id},
                    attribute datasetEffectiveDate {$inheritedConcept/ancestor::dataset/@effectiveDate},
                    attribute iType {$originalConcept/@type}, 
                    attribute iStatusCode {$inheritedConcept/@statusCode}, 
                    if ($inheritedConcept[@expirationDate]) then attribute iExpirationDate {$inheritedConcept/@expirationDate} else (),
                    if ($inheritedConcept[@versionLabel]) then attribute iVersionLabel {$inheritedConcept/@versionLabel} else (),
                    attribute iddisplay {art:getNameForOID($concept/inherit/@ref, $inheritedConcept/ancestor::decor/project/@defaultLanguage, $inheritedConcept/ancestor::decor)}
                }
                </inherit>
            )
            ,
            for $name in $originalConcept/name
            return
            art:serializeNode($name)
            ,
            $associations
        }
        {
            let $subConcepts        := 
                if ($inheritedConcept[contains] or $inheritType = 'containment') then () else 
                if (count(map:keys($selectedConcepts))=0) then $inheritedConcept/concept else (
                    (: I don't get why child concepts would be need to be checked in the list, but that is what the old logic said :)
                    (: $inheritedConcept/concept[(concat(@id,@effectiveDate), concept/concat(@id,@effectiveDate)) = map:keys($selectedConcepts)] :)
                    $inheritedConcept/concept[concat(@id,@effectiveDate) = map:keys($selectedConcepts)]
                )
            
            for $subConcept in $subConcepts
            let $newCurrentId       := 
                if ($datasets[@maxcounter castable as xs:integer]) then $datasets/@maxcounter + 1 else (
                    max($datasets//concept[matches(@id,concat($baseId,'\d+$'))]/xs:integer(tokenize(@id,'\.')[last()]))+1
                )
            let $newCurrentId       := if ($newCurrentId) then $newCurrentId else (1)
            let $newId              := concat($baseId,$newCurrentId)
            let $newEffectiveDate   := format-dateTime(current-dateTime(), '[Y0001]-[M01]-[D01]T[H01]:[m01]:[s01]')
            let $username           := get:strCurrentUserName()
            let $userDisplayName    := aduser:getUserDisplayName($username)
            let $newLock            := 
                <lock type="{$decor:OBJECTTYPE-DATASETCONCEPT}" ref="{$newId}" effectiveDate="{$newEffectiveDate}" 
                      user="{$username}" userName="{$userDisplayName}" since="{current-dateTime()}" prefix="{$projectPrefix}"/>
            let $insertLock         := update insert $newLock into $get:docDecorLocks/decorLocks
            let $newConcept         :=
                <concept id="{$newId}" type="{$subConcept/@type}" statusCode="new" effectiveDate="{$newEffectiveDate}" lastModifiedDate="{$newEffectiveDate}">
                    <edit mode="edit"/>
                    {$newLock}
                    <inherit ref="{$subConcept/@id}" effectiveDate="{$subConcept/@effectiveDate}"/>
                </concept>
            (: inserting it as we go ensures the right id count :)
            let $insertNewConcept   := update insert $newConcept into $storedConcept
            let $setmaxcounter      := update value $datasets/@maxcounter with $newCurrentId
            return
                local:resolveInherit($newConcept, $level + 1)
        }
        {
            $history
            ,
            $concept/history
        }
        </concept>
        
        return $editedConcept
    else ()
};

let $level          := 0

(: may or may not return a concept :)
let $storedConcept  := if ($concept) then (art:getConcept($concept/@id, $concept/@effectiveDate)) else ()

(: delete our temp counter :)
let $delete         := update delete $storedConcept/ancestor::datasets/@maxcounter

let $editedConcept  := 
    try {
        local:resolveInherit($concept, $level)
    }
    catch * {
        art:getConcept($concept/@id, $concept/@effectiveDate)
    }

(: delete our temp counter :)
let $delete         := update delete $storedConcept/ancestor::datasets/@maxcounter

let $storedConcept  := 
    if ($editedConcept) then (
        art:getConcept($editedConcept/@id, $editedConcept/@effectiveDate)
    ) else ()

(: storing the result one more time ensures that the top level concept is written correctly too. 
    without this we will return more info than we have saved in the project. :)
let $replace        := 
    if (exists($editedConcept) and exists($storedConcept) and $editedConcept[concept]) then (
        (: first delete the stuff that was saved during local:resolveInherit() :)
        let $delete         := update delete $storedConcept/concept[@statusCode = 'new'][inherit]
        (: now resave using the intended structure but respect existing concepts from before the inherit. This way we can still cancel :)
        let $insert         := 
            if ($storedConcept[concept | history]) then 
                update insert $editedConcept/concept preceding ($storedConcept/concept | $storedConcept/history)[1]
            else (
                update insert $editedConcept/concept into $storedConcept
            )
        let $storedConcept  := art:getConcept($editedConcept/@id, $editedConcept/@effectiveDate)
        let $delete         := 
            for $c in $storedConcept//concept[@statusCode = 'new'][inherit]
            return (
                update delete $c/(@navkey | @type | @*[. = '']), 
                update delete $c/(* except (concept|inherit)),
                update delete $c/inherit/(@* except (@ref | @effectiveDate))
            )
        return ()
    ) else ()

return
    $editedConcept