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


declare namespace http                  = "http://expath.org/ns/http-client";
declare namespace sm                    = "http://exist-db.org/xquery/securitymanager";
declare namespace xs                    = "http://www.w3.org/2001/XMLSchema";
declare namespace output                = "http://www.w3.org/2010/xslt-xquery-serialization";
declare namespace json                  =  "http://www.json.org";
declare option    output:method "json";
declare option    output:media-type "application/json";
declare option    exist:serialize "json-ignore-whitespace-text-nodes=yes";

declare %private function local:getADRAMStatus($decorReferenceURL as xs:string?) as xs:string {
let $requeststatus  := 
    <http:request method="GET" href="{xs:anyURI($decorReferenceURL)}" status-only="true" follow-redirect="false" timeout="5">
        <http:header name="Content-Type" value="text/xml"/>
        <http:header name="Cache-Control" value="no-cache"/>
        <http:header name="Max-Forwards" value="1"/>
    </http:request>
let $requestadram  := 
    <http:request method="GET" href="{xs:anyURI(concat($decorReferenceURL, '/adram.config.xml'))}" status-only="false" follow-redirect="false" timeout="5">
        <http:header name="Content-Type" value="text/xml"/>
        <http:header name="Cache-Control" value="no-cache"/>
        <http:header name="Max-Forwards" value="1"/>
    </http:request>
(: check availablility of reference/@url :)
let $unavailable    := '500'
let $longtimeago    := '1900-01-01T00:00:00'
let $servicestatus  :=
    if (string-length($decorReferenceURL)=0)
    then '500'
    else
        (: get headers -:)
        (:
            <hc:response xmlns:hc="http://expath.org/ns/http-client" status="200" message="OK" spent-millis="314">
                <hc:header name="server" value="nginx"/>
                <hc:header name="date" value="Tue, 08 Oct 2019 06:30:21 GMT"/>
                <hc:header name="content-type" value="text/xml"/>
                <hc:header name="transfer-encoding" value="chunked"/>
                <hc:header name="connection" value="keep-alive"/>
                <hc:header name="x-xquery-cached" value="true"/>
                <hc:body media-type="text/xml"/>
            </hc:response>
            [body]
        :)
        try {
            let $pubDir         := http:send-request($requeststatus)
            let $statusCode     := $pubDir[1]/@status
            let $adramConfig    := 
                if ($statusCode='200') then (
                    http:send-request($requestadram)
                ) else ()
            let $adramStatus    := $adramConfig[1]/@status
            (:let $adramHeader := exists($response1/httpclient:headers/httpclient:header[@name='X-Powered-By'][@value='ART-DECOR-ADRAM']):)
            (: check wheter adram is configured there, if so get the @touch attribute to find out when the cron job last touched the config :) 
            let $response2      := 
                if ($adramStatus ='200') then (
                    let $adramLastRun := 
                        if ($adramConfig//adram[string-length(@touched)>0]) 
                        then $adramConfig//adram/@touched 
                        else $longtimeago
                    let $timediff     := days-from-duration(current-dateTime() - xs:dateTime($adramLastRun))
                    
                    (: if last touch is more than 2 days ago assume halted :)
                    return if ($timediff <= 2) then 'adram' else 'halted'
                )
                else (
                    (: if adram.config.xml does not exist then assume 'not configured' :)
                )
                
            return if ($statusCode='200') then concat($statusCode, ' ', $response2) else $unavailable
        } catch * { $unavailable }

return $servicestatus
};

let $project            := if (request:exists()) then request:get-parameter('project',())[string-length()>0]         else ()
let $language           := if (request:exists()) then request:get-parameter('language',())[string-length()>0]        else ()
let $projectId          := if (request:exists()) then request:get-parameter('id',())[string-length()>0]              else ()
let $checkADRAM         := if (request:exists()) then request:get-parameter('checkadram','false')[string-length()>0] else ()
let $decor              :=
    if ($project) then
        art:getDecorByPrefix($project)
    else if ($projectId) then
        art:getDecorById($projectId)
    else ()
let $logo               := $decor/project/reference/@logo
(:let $logosrc        := 
    if (empty($logo)) then () else (
        if (starts-with($logo,'http')) then ($logo) else (
            let $coll   := replace(util:collection-name($decorProject),'/db/apps/','')
            let $imgs   := concat($decorProject/@prefix,'logos')
            return
                (\:https://art-decor.org/decor/data/examples/demo1/demo1-logos/:\)
                concat(replace(adserver:getServerURLArt(),'art-decor/',''),$coll,'/',$imgs,'/',$logo)
        )
    ):)

(:XFORMS extras. This is used as prefix for project logos:)
let $projectColl        := replace(util:collection-name($decor),'^.*data/','')

let $decorProject       := $decor/project
let $projectId          := $decorProject/@id
let $ggl                := try { if ($projectId) then gg:getLinkedGovernanceGroups($projectId) else () } catch * {()}
let $updateUserPrefs    := try { aduser:updateProjectPreference($projectId) } catch * {()}
return
    if ($decor) then (
        <project 
            id="{$projectId}" 
            prefix="{$decorProject/@prefix}" 
            experimental="{$decorProject/@experimental='true'}"
            defaultLanguage="{$decorProject/@defaultLanguage}" 
            repository="{$decor/@repository='true'}"
            private="{$decor/@private='true'}"
            lastmodified="{xmldb:last-modified(util:collection-name($decorProject), concat($decorProject/@prefix, 'decor.xml'))}">
        {
            attribute collection {$projectColl},
            (:ADRAM extras:)
            if ($checkADRAM='true' and $decorProject/reference/@url[string-length()>0]) then (
                attribute servicestatus {local:getADRAMStatus($decorProject/reference/@url)}
            ) else ()
        }
        {
            for $name in $decorProject/name
            return
                <name json:array="true">
                {
                    $name/@*,
                    $name/text()
                }
                </name>,
            art:serializeDescriptionNodes($decorProject/desc, $decorProject/name/@language),
            for $copyright in $decorProject/copyright
            let $copyrightType  := if ($copyright[@type]) then $copyright/@type else 'author'
            return
                <copyright by="{$copyright/@by}" logo="{$copyright/@logo}" years="{$copyright/@years}" type="{$copyrightType}" json:array="true">
                {
                    for $node in $copyright/addrLine
                    return
                        <addrLine>{$node/@*[string-length() gt 0], if ($node[@type]) then () else (attribute type {''}), $node/node()}</addrLine>
                }
                </copyright>
            ,
            for $license in $decor/license
            return
                <license json:array="true">{$license/@*, $license/node()}</license>
            ,
            for $author in $decorProject/author
            return
                <author json:array="true">
                {
                    $author/(@* except @dbactive),
                    if ($author/@email) then () else attribute email {''},
                    if ($author/@notifier) then () else attribute notifier {''},
                    (: does the user have an active eXist-db account? :)
                    attribute dbactive {
                        try {
                            if (sm:user-exists($author/@username)) then sm:is-account-enabled($author/@username) else false()
                        } 
                        catch * {true()}
                    }
                    ,
                    $author/node()
                }
                </author>
            ,
            <reference url="{$decorProject/reference/@url}" logo="{$logo}">
            {()(:if ($logosrc) then attribute logosrc {$logosrc} else ():)}
            </reference>
            ,
            for $rui in $decorProject/restURI
            return
                <restURI json:array="true">
                {
                    $rui/@*,
                    $rui/node()
                }
                </restURI>
            ,
            if ($decorProject[defaultElementNamespace]) then 
                $decorProject/defaultElementNamespace
            else (
                <defaultElementNamespace ns="hl7:"/>
            )
            ,
            $decorProject/contact,
            for $bbr in $decorProject/buildingBlockRepository
            return
                <buildingBlockRepository json:array="true">
                {
                    $bbr/@*,
                    if ($bbr[@format]) then () else (attribute format {'decor'}),
                    $bbr/node()
                }
                </buildingBlockRepository>
            ,
            for $gg in $ggl/partOf/@ref
            return <group id="{$gg}"/>
            (:for $ggl in $get:colDecorData/governance-group-links[@ref = $projectId], $gg in $ggl/partOf/@ref
            return <group id="{$gg}"/>:)
            ,
            art:getDecorNamespaces($decor)
        }
        {
            <statistics>
                <datasets count="{count($decor/datasets/dataset)}" concepts="{count($decor/datasets/dataset//concept)}"/>
                <scenarios count="{count($decor/scenarios/scenario)}" transactions="{count($decor/scenarios//transaction)}"/>
                <terminologies count="{count($decor/terminology/(valueSet|codeSystem))}"/>
                <profiles count="{count($decor/rules/(template|profile))}"/>
                <issues count="{count($decor/issues/issue)}" open="{count($decor/issues/issue[@statusCode = ('open')])}"/>
                <releases count="{count($decor/project/release)}"/>
            </statistics>
        }
        {
            (
                for $h in ( hist:ListHistory('TM', $project) | hist:ListHistory('VS', $project) )/history
                let $at := $h/parent::*/@artefactType
                order by $h/@date descending
                return
                    <history>
                    {
                        <what>
                        {
                            concat($at,
                            " <i>",
                            $h/artefact/@displayName/string(),
                            "</i> (",
                            $h/@artefactId/string(),
                            ")"
                            )
                        }
                        </what>,
                        <performer>
                        {
                            $h/@author/string()
                        }
                        </performer>,
                        <when>
                        {
                            format-dateTime($h/@date,'[Y0001]-[M01]-[D01] [H01]:[m01]','en',(),())
                        }
                        </when>
                    }
                    </history>
            )[position() <= 100]
        }
            <ids>
            {
                (:
                Add empty designation for language, otherwise you cannot edit the designation in the project form. TODO: fix empty designations before/on save 
                <id root="1.0.639.2">
                    <designation language="nl-NL" type="" displayName="ISO-639-2 Alpha 3" lastTranslated="" mimeType="">ISO-639-2 Alpha 3 Language</designation>
                </id>
                :)
                (:
                    Old style:
                        <baseId id="1.2.3" type="DS" prefix="xyz"/>
                        <defaultBaseId id="1.2.3" type="DS"/>
                    New style:
                        <baseId id="1.2.3" type="DS" prefix="xyz" default="true"/>
                        
                    Rewrite old style to new style.
                :)
                for $baseId in $decor/ids/baseId
                return
                    <baseId json:array="true">
                    {
                        $baseId/@*[string-length() gt 0]
                        ,
                        if ($baseId[@default]) then () else (
                            attribute default {$decor//defaultBaseId/@id = $baseId/@id}
                        )
                    }
                    </baseId>
                ,
                (: For now: keep old style so we can fix all dependent code later :)
                $decor/ids/defaultBaseId,
                for $identifier in $decor/ids/id
                return
                    element {name($identifier)} {
                        $identifier/@*,
                        (:create if not available in the language so the user may fill it out:)
                        if (string-length($language)>0 and not($identifier/designation[@language=$language]))
                        then (<designation language="{$language}" type="preferred" displayName=""/>)
                        else (),
                        (:retain anything that was not requested but still in there:)
                        for $designation in $identifier/designation
                        return
                        <designation language="{$designation/@language}" type="{$designation/@type}" displayName="{$designation/@displayName}">
                        {$designation/node()}
                        </designation>
                    }
            }
            </ids>
        {
           <issues>
           {
                $decor/issues/@*,
                $decor/issues/labels
           }
           </issues>
        }
        </project>
    ) else (
        <project/>
    )