xquery version "3.1";

(:~
 : This module is responsible for retrieving the variants of a given text on a
 : given page. For this, it leverages the tokenization of the text that takes
 : place during commons:get-page-fragment.
 : 
 : The output of the main function, vars:get-variants, is a sequence of maps of
 : the following format:
 :
 : map {
 :  "body" 
 :  map {
 :      "x-content-type" : "Variant",
 :      "value" : [ map {
 :          "entry" : "${text-of-respective-witness}",
 :          "witness" : "${ms-id}"
 :      }],
 :      "format" : "text/plain",
 :      "type" : "TextualBody"
 :    },
 :    "target" :array { map {
 :      "format" : "text/xml",
 :      "language" : "syc",
 :      "id" : "http://ahikar.sub.uni-goettingen.de/ns/annotations/${teixml-uri}/${token-id}"
 :    } },
 :    "type" : "Annotation",
 :    "id" : "http://ahikar.sub.uni-goettingen.de/ns/annotations/${teixml-uri}/annotation-variants-${token-id}"
 : }
 : 
 :)

module namespace vars="http://ahiqar.uni-goettingen.de/ns/annotations/variants";

declare namespace fn="http://www.w3.org/2005/xpath-functions";
declare namespace tei="http://www.tei-c.org/ns/1.0";

import module namespace commons="http://ahiqar.uni-goettingen.de/ns/commons" at "../commons.xqm";
import module namespace console="http://exist-db.org/xquery/console"; (: required for garbage collection :)
import module namespace functx="http://www.functx.com";

declare variable $vars:ns := "http://ahiqar.uni-goettingen.de/ns/annotations";
(:~
    Main function to create variant annotations for a given page. The page has to be delivered including token IDs.
    These IDs will be looked up in the collation results returning a sequence of rows (since we are dealing with
    kind of tabular data coming from CollateX).
:)
declare function vars:get-variants($pages as element(tei:TEI)+,
    $teixml-uri as xs:string,
    $pb-n as xs:string) as item()* {
    for $page in $pages
    (: before we begin, we trigger a garbage collection. what a terrible pitty. 
    let $garbageCollection := doc( 'http://localhost:8080/exist/status?operation=gc&amp;mbean=java.lang:type=Memory&amp;token=' || console:jmx-token() )
    :)
    let $ms-id := vars:get-ms-id-from-idno($teixml-uri)
    let $ms-id-in-collation := "t_" || $ms-id
    
    let $collations := collection("/data/collation-results-xml")
    let $tokenIds-on-page := $page//tei:w/@xml:id
    let $collations-for-page :=  $collations//string[. = $tokenIds-on-page]/parent::map/parent::array/parent::array (: = row :)
    let $witnesses := $collations-for-page[1]/root()/map/array[@key="witnesses"]/string/text()
    let $log := util:log-system-out("👾👾👾")
    let $log := util:log-system-out("creating variant annotations for " || $ms-id || " (tei: " || $teixml-uri || ", page " || $pb-n || ")")
    let $log := util:log-system-out("with " || count($tokenIds-on-page) || " tokens on page found in " || count($collations-for-page) || " rows with " || count($witnesses) - 1 || " variants per row")
    let $log := util:log-system-out("resulting in " || count($collations-for-page) * (count($witnesses) - 1)  || " variants to prepare." )
    return (
        (: for every token on page we create an annotation of its PHRASE!!! :)
        for $row in $collations-for-page
        where $row//string[@key="id"]/starts-with(., $ms-id-in-collation) = true()
            (: re-assigns the var to go one with a more suitable format :)
            let $row := vars:parse-row($row, $witnesses)
            
            (: create the target first, as it is the same for all variants representing a token or phrase :)
            let $target :=
                array {
                    map {
                        "selector": map {
                            "type": "CssSelector",
                            (: creates a comma-separated list of IDREFs :)
                            "value": ($row?($ms-id-in-collation)?ids?* ! ("#" || .)) => string-join(",")
                        }
                    },
                    map {
                        "id": $vars:ns || "/" || $teixml-uri || "/"|| $row?($ms-id-in-collation)?ids?*[1] || "-to-" || $row?($ms-id-in-collation)?ids?*[last()],
                        "format": "text/xml",
                        "language": $page//tei:text[@xml:lang]/@xml:lang => string()
                        }, map{
                        "source": "http://example.org/page1", (: todo: source URL :)
                        "selector": map{
                        "type": "TextQuoteSelector",
                        "exact": ($row?($ms-id-in-collation)?strings?*) => string-join(" ")
                        }
                    }
                }
            let $distinct := (for $variant in map:keys($row)[. != $ms-id-in-collation]
                    let $cell   := $row?($variant)
                    let $string := $cell?strings?* => string-join(" ")
                    return if ($string eq "") then "omisit" else $string) => distinct-values()

            (: iterate over the cells in the row, but omit the cell representing current document :)
            for $val in $distinct
                let $variants :=
                    array {for $variant in map:keys($row)[. != $ms-id-in-collation]
                        let $cell   := $row?($variant)
                        let $string := $cell?strings?* => string-join(" ")
                        return if ($string eq $val or ($val = "omisit" and $string = "")) then $variant else ()
                    }
                let $idVs   := string-join($variants, "-")
                    
                return  map{
                            "id": $vars:ns || "/" || $teixml-uri || "/annotation-variants-" || $row?($ms-id-in-collation)?ids?*[1] || "-vs-" || $idVs,
                            "type": "Annotation",
                            "body": map {
                                "type": "TextualBody",
                                "value": map{
                                    "witnesses": $variants,
                                    "entry": $val
                                    },
                                "format": "text/plain",
                                "x-content-type": "Variant"
                                },
                            "target": $target
                        }, 
        map {
            (: and add the refs finally :)
            "refs": array {
                for $witness in $witnesses[. != $ms-id-in-collation] (: exclude self :)
                let $witnessInfo := 
                    (util:binary-doc("/db/data/collation-results/map.json")
                                => util:binary-to-string()
                                => parse-json()
                                )?*[?id = $witness]
                return
                    map {
                        "idno" : $witness,
                        "idnoAlt" : $commons:idno-to-sigils-map(substring-after($witness, "t_")),
                        "title" : $witnessInfo?title,
                        "tguri": $witnessInfo?tei
                    }
                }
            }
)};

(:~
   Create a custom format for a row from CollateX output.
:)
declare function vars:parse-row($row as element(array), $witnesses as xs:string+) as map()+ {
    map:merge(
        for $witness at $pos in $witnesses
        let $ids := $row/array[$pos]/map/string[@key eq "id"]/string()
        return
            map{
                $witness: if(not($ids[1])) then () else map {
                    "strings": array{ $row/array[$pos]/map/string[@key eq "t"]/string() },
                    "ids": array{ $ids }
                }
            }
    )
};

declare function vars:get-ms-id-from-idno($teixml-uri as xs:string)
as xs:string {
    let $TEI := commons:open-tei-xml($teixml-uri)//tei:TEI
    return
        if (matches($TEI//tei:monogr/tei:editor, "Salhani")) then
            "Salhani"
        else
            commons:make-id-from-idno($TEI)
            => substring-after("t_")
};
