:books: Add doc.

:books: Add doc.

jsoniq version "1.0";
module namespace data-source-wrapper = "http://28.io/modules/xbrl/data-source-wrapper";

import module namespace config = "http://apps.28.io/config";
import module namespace mongo-wrapper = "http://28.io/modules/xbrl/mongo-wrapper";

(:~
 :
Count how many objects a collection contains.

 :
 : @param $collection the name of a collection.
 :
 : @return the number of objects in this collection.
 :)
declare function data-source-wrapper:count(
    $collection as string
) as integer {
    switch($config:query-db.category)
    case "MongoDB" return mongo-wrapper:count($collection)
    default return ()
};

(:~
 :
Filters the objects in a collection.

 :
The filter format is as follows.

 :
A filter query consists of a hierarchy of nested objects, the keys of
 : which MUST NOT contain the dollar symbol.

 :
Each leaf performs a filter on the path with which it is associated.

 :
The leaves of this object hierarchy MUST be one of the three:

 :

 :
An atomic ("exact match").
 :
An array of atomics ("one-of-these-values match").
 :
The exact object { "$exists" : true } ("must-exist match").
 :

 :
The semantics of the entire query is that it performs a
 : conjunction on the filters done on all possible paths.

 :
For example, with only atomics as leafs to keep it simple:

 :

 : {
 :   "Aspects" : {
 :     "xbrl:Concept" : "us-gaap:Assets",
 :     "xbrl:Entity" : "4962"
 :   },
 :   "Value" : 3
 : }
 :

 :
 :
filters for facts $f that satisfy ALL of the following:

 :

 :
$f.Aspects."xbrl:Concept" eq "us-gaap:Assets" and
 :
$f.Aspects."xbrl:Entity" eq "4962" and
 :
$f.Value eq 3
 :

 :
If an atomic $a is used as a leaf, with path A.B.C, the filter of this
 : path is: <emph>$f.A.B.C eq $a</emph>

 :
If an array $a is used as a leaf, with path A.B.C, the filter of this path
 : is: <emph>$f.A.B.C = $a[]</emph> (that's a $in query in MongoDB)

 :
If { "$exists" : true } is used as a leaf, with path A.B.C, the filter of
 : this path is: <emph>exists($f.A.B.C)</emph>

 :
For example:

 :

 : {
 :   "Aspects" : {
 :     "xbrl:Concept" : "us-gaap:Assets",
 :     "xbrl:Entity" : [ "4962", "31415" ]
 :   },
 :   "Value" : { "$exists" : true }
 : }
 :

 :
 :
filters for facts $f that satisfy ALL of the following:

 :

 :
$f.Aspects."xbrl:Concept" eq "us-gaap:Assets" and
 :
$f.Aspects."xbrl:Entity" = ("4962", "31415") and
 :
exists($f.Value)
 :

 :
 :
If an array $a contains the value null, then a fact with an absent value
 : will be accepted; the filter is then
 : <emph>$f.A.B.C = $a[][not $$ instance of null] or empty($f.A.B.C)</emph>
 :

 :
 :
[deprecated, for backwards compatibility] Finally, it also allowed to wrap
 : several filtering queries as
 : specified above in a top-level disjunctive clause, in an array associated
 : with the key "$or":

 :

 : {
 :   "$or" : [ <query1>, <query2>, ... ]
 : }
 :

 :
 : @param $collection the name of a collection.
 : @param $query a filter query.
 :
 : @return the number of objects in this collection.
 :)
declare function data-source-wrapper:find(
    $collection as string,
    $query as object
) as object()*
{
  switch($config:query-db.category)
  case "MongoDB" return mongo-wrapper:find($collection, $query)
  default return ()
};

(:~
 :
Filters and projects the objects in a collection.

 :
The filter format is as described in the arity-2 variant of this
 : function.

 :
 : @param $collection the name of a collection.
 : @param $query a filter query.
 : @param $projection a projection (MongoDB-like).
 :
 : @return the number of objects in this collection.
 :)
declare function data-source-wrapper:find(
    $collection as string,
    $query as object,
    $projection as object
) as object()*
{
  switch($config:query-db.category)
  case "MongoDB" return mongo-wrapper:find($collection, $query, $projection)
  default return ()
};

declare function data-source-wrapper:text-search(
    $collection as string,
    $query as object) as object*
{
  switch($config:query-db.category)
  case "MongoDB" return mongo-wrapper:run-cmd-deterministic(
    {|
      { "text" : $collection },
      $query
    |}).results[].obj
  default return ()
};

declare function data-source-wrapper:connection() as anyURI?
{
  switch($config:query-db.category)
  case "MongoDB" return mongo-wrapper:connection()
  default return ()
};

jsoniq version "1.0";
module namespace data-source-wrapper = "http://28.io/modules/xbrl/data-source-wrapper";

import module namespace config = "http://apps.28.io/config";
import module namespace mongo-wrapper = "http://28.io/modules/xbrl/mongo-wrapper";

(:~
 :
Count how many objects a collection contains.

 :
 : @param $collection the name of a collection.
 :
 : @return the number of objects in this collection.
 :)
declare function data-source-wrapper:count(
    $collection as string
) as integer {
    switch($config:query-db.category)
    case "MongoDB" return mongo-wrapper:count($collection)
    default return ()
};

(:~
 :
Filters the objects in a collection.

 :
The filter format is as follows.

 :
A filter query consists of a hierarchy of nested objects, the keys of
 : which MUST NOT contain the dollar symbol.

 :
The leaves of this object hierarchy MUST be one of the tree:

 :

 :
An atomic ("exact match").
 :
An array of atomics ("one-of-these-values match").
 :
The exact object { "$exists" : true } ("must-exist match").
 :

 :
The semantics of a filter query is that it performs a
 : conjunction on the filters done on all possible paths.

 :
For example, with only atomics as leafs to keep it simple:

 :

 : {
 :   "Aspects" : {
 :     "xbrl:Concept" : "us-gaap:Assets",
 :     "xbrl:Entity" : "4962"
 :   },
 :   "Value" : 3
 : }
 :

 :
 :
filters for facts $f that satisfy all of the following:

 :

 :
$f.Aspects."xbrl:Concept" eq "us-gaap:Assets"
 :
$f.Aspects."xbrl:Entity" eq "4962"
 :
$f.Value eq 3
 :

 :
If an atomic $a is used as a leaf, with path A.B.C, the filter of this
 : path is: $f.A.B.C eq $a

 :
If an array $a is used as a leaf, with path A.B.C, the filter of this path
 : is: $f.A.B.C = $a[]

 :
If { "$exists" : true } is used as a leaf, with path A.B.C, the filter of
 : this path is: exists($f.A.B.C)

 :
 :
Finally, it also allowed to wrap several filtering queries as specified
 : above in a disjunctive close, in an array associated with the key "$or":

 :

 : {
 :   "$or" : [ <query1>, <query2>, ... ]
 : }
 :

 :
 : @param $collection the name of a collection.
 : @param $query a filter query.
 :
 : @return the number of objects in this collection.
 :)
declare function data-source-wrapper:find(
    $collection as string,
    $query as object
) as object()*
{
  switch($config:query-db.category)
  case "MongoDB" return mongo-wrapper:find($collection, $query)
  default return ()
};

(:~
 :
Filters and projects the objects in a collection.

 :
The filter format is as described in the arity-2 variant of this
 : function.

 :
 : @param $collection the name of a collection.
 : @param $query a filter query.
 : @param $projection a projection (MongoDB-like).
 :
 : @return the number of objects in this collection.
 :)
declare function data-source-wrapper:find(
    $collection as string,
    $query as object,
    $projection as object
) as object()*
{
  switch($config:query-db.category)
  case "MongoDB" return mongo-wrapper:find($collection, $query, $projection)
  default return ()
};

declare function data-source-wrapper:text-search(
    $collection as string,
    $query as object) as object*
{
  switch($config:query-db.category)
  case "MongoDB" return mongo-wrapper:run-cmd-deterministic(
    {|
      { "text" : $collection },
      $query
    |}).results[].obj
  default return ()
};

declare function data-source-wrapper:connection() as anyURI?
{
  switch($config:query-db.category)
  case "MongoDB" return mongo-wrapper:connection()
  default return ()
};


:lipstick: Change export extension to .xbrlx.

jsoniq version "1.0";

(:
 : Copyright 2012-2015 28msec Inc.
 :)

(:~
 :
This module provides functionality for exporting archives to the XBRL format.

 :
 : @author Dennis Knochenwefel
 : @author Ghislain Fourny
 :)
module namespace xbrl-export = "http://28.io/modules/xbrl/export";

import module namespace archives = "http://28.io/modules/xbrl/archives";
import module namespace concepts = "http://28.io/modules/xbrl/concepts";
import module namespace components = "http://28.io/modules/xbrl/components";
import module namespace networks = "http://28.io/modules/xbrl/networks";
import module namespace labels = "http://28.io/modules/xbrl/labels";

import module namespace archive = "http://zorba.io/modules/archive";
import module namespace credentials = "http://www.28msec.com/modules/credentials";
import schema namespace mongos = "http://www.28msec.com/modules/mongodb/types";

declare variable $xbrl-export:serialization-parameters :=
    <serialization-parameters xmlns="http://www.w3.org/2010/xslt-xquery-serialization">
      <method value="xml"/>
      <indent value="yes"/>
      <encoding value="UTF-8"/>
    </serialization-parameters>;

declare %private function xbrl-export:comment() as comment()
{
  comment { "
    Generated: " || current-dateTime() || " (" || credentials:credentials("MongoDB", "xbrl").db || ")
    Designed by XBRL Switzerland Working Group
    Generated by 28msec (28.io)
  " }
};

declare function xbrl-export:schema(
    $components as object*,
    $taxonomyName as string)
as node()*
{
  xbrl-export:comment(),
  <schema targetNamespace="http://www.xbrl-ch.ch/ch/fr/co/gp/2015-02-26"
      elementFormDefault="qualified"
      xmlns="http://www.w3.org/2001/XMLSchema"
      xmlns:xlink="http://www.w3.org/1999/xlink"
      xmlns:link="http://www.xbrl.org/2003/linkbase"
      xmlns:xbrli="http://www.xbrl.org/2003/instance"
      xmlns:xbrldt="http://xbrl.org/2005/xbrldt"
      xmlns:ch-co="http://www.xbrl-ch.ch/ch/fr/co/gp/2015-02-26"
      xmlns:nonnum="http://www.xbrl.org/dtr/type/non-numeric">
    <import namespace="http://www.xbrl.org/2003/instance" schemaLocation="http://www.xbrl.org/2003/xbrl-instance-2003-12-31.xsd"/>
    <import namespace="http://xbrl.org/2005/xbrldt" schemaLocation="http://www.xbrl.org/2005/xbrldt-2005.xsd" />
    <import namespace="http://www.xbrl.org/dtr/type/non-numeric" schemaLocation="http://www.xbrl.org/dtr/type/nonNumeric-2009-12-16.xsd" />
    {
      components:to-xbrl-annotation($components, $taxonomyName),
      for $concept as element() in concepts:to-xbrl($components.Concepts[])
      group by $name := $concept/@name
      return $concept[1]
    }
  </schema>
};

declare function xbrl-export:labels(
    $components as object*,
    $taxonomyName as string)
as node()*
{
  xbrl-export:comment(),
  <linkbase
      xmlns="http://www.xbrl.org/2003/linkbase"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xmlns:ch-co="http://www.xbrl-ch.ch/ch/fr/co/gp/2015-02-26"
      xmlns:xlink="http://www.w3.org/1999/xlink"
      xmlns:xbrli="http://www.xbrl.org/2003/instance"
      xsi:schemaLocation="http://www.xbrl.org/2003/linkbase http://www.xbrl.org/2003/xbrl-linkbase-2003-12-31.xsd">
  {
    labels:to-xbrl($components.Concepts[], $taxonomyName)
  }
  </linkbase>
};

declare function xbrl-export:presentation(
    $components as object*,
    $taxonomyName as string)
as node()*
{
  xbrl-export:comment(),
  <linkbase
      xmlns="http://www.xbrl.org/2003/linkbase"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xmlns:ch-co="http://www.xbrl-ch.ch/ch/fr/co/gp/2015-02-26"
      xmlns:xlink="http://www.w3.org/1999/xlink"
      xmlns:xbrli="http://www.xbrl.org/2003/instance"
      xsi:schemaLocation="http://www.xbrl.org/2003/linkbase http://www.xbrl.org/2003/xbrl-linkbase-2003-12-31.xsd">
  {
    components:to-xbrl-roleRefs($components, $taxonomyName),
    for $component in $components
    let $presentationNetwork as object? := networks:networks-for-components-and-short-names($component, $networks:PRESENTATION_NETWORK)
    where exists($presentationNetwork) and $component.Role ne null
    return networks:to-xbrl($presentationNetwork, $component, $taxonomyName)
  }
  </linkbase>
};

declare function xbrl-export:calculation(
    $components as object*,
    $taxonomyName as string)
as node()*
{
  xbrl-export:comment(),
  <linkbase
      xmlns="http://www.xbrl.org/2003/linkbase"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xmlns:ch-co="http://www.xbrl-ch.ch/ch/fr/co/gp/2015-02-26"
      xmlns:xlink="http://www.w3.org/1999/xlink"
      xmlns:xbrli="http://www.xbrl.org/2003/instance"
      xsi:schemaLocation="http://www.xbrl.org/2003/linkbase http://www.xbrl.org/2003/xbrl-linkbase-2003-12-31.xsd">
  {
    components:to-xbrl-roleRefs($components, $taxonomyName),
    for $component in $components
    let $calculationNetwork as object? := networks:networks-for-components-and-short-names($component, $networks:CALCULATION_NETWORK)
    where exists($calculationNetwork) and $component.Role ne null
    return networks:to-xbrl($calculationNetwork, $component, $taxonomyName)
  }
  </linkbase>
};

declare function xbrl-export:definition(
    $components as object*,
    $taxonomyName as string)
as node()*
{
  xbrl-export:comment(),
  <linkbase
      xmlns="http://www.xbrl.org/2003/linkbase"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xmlns:ch-co="http://www.xbrl-ch.ch/ch/fr/co/gp/2015-02-26"
      xmlns:xlink="http://www.w3.org/1999/xlink"
      xmlns:xbrli="http://www.xbrl.org/2003/instance"
      xsi:schemaLocation="http://www.xbrl.org/2003/linkbase http://www.xbrl.org/2003/xbrl-linkbase-2003-12-31.xsd">
    <arcroleRef arcroleURI="http://xbrl.org/int/dim/arcrole/all" xlink:type="simple" xlink:href="http://www.xbrl.org/2005/xbrldt-2005.xsd#all"/>
    <arcroleRef arcroleURI="http://xbrl.org/int/dim/arcrole/dimension-domain" xlink:type="simple" xlink:href="http://www.xbrl.org/2005/xbrldt-2005.xsd#dimension-domain"/>
    <arcroleRef arcroleURI="http://xbrl.org/int/dim/arcrole/domain-member" xlink:type="simple" xlink:href="http://www.xbrl.org/2005/xbrldt-2005.xsd#domain-member"/>
    <arcroleRef arcroleURI="http://xbrl.org/int/dim/arcrole/hypercube-dimension" xlink:type="simple" xlink:href="http://www.xbrl.org/2005/xbrldt-2005.xsd#hypercube-dimension"/>
    <arcroleRef arcroleURI="http://xbrl.org/int/dim/arcrole/notAll" xlink:type="simple" xlink:href="http://www.xbrl.org/2005/xbrldt-2005.xsd#notAll"/>
    <arcroleRef arcroleURI="http://xbrl.org/int/dim/arcrole/dimension-default" xlink:type="simple" xlink:href="http://www.xbrl.org/2005/xbrldt-2005.xsd#dimension-default"/>
    {
      components:to-xbrl-roleRefs($components, $taxonomyName),
      for $component in $components
      for $hypercube in values($component.Hypercubes)
      where $component.Role ne null
      return networks:to-xbrl($hypercube, $component, $taxonomyName)
    }
  </linkbase>
};

declare function xbrl-export:archive(
    $archive as item
) as item()*
{
  let $archive as object := archives:archives($archive)
  return xbrl-export:archive($archive, $archive._id)
};

declare function xbrl-export:archive(
    $archive as item,
    $taxonomyName as string)
as item()*
{
  let $archive as object := archives:archives($archive)
  let $components as object* := components:components-for-archives($archive)
  let $schema as string := serialize(xbrl-export:schema($components, $taxonomyName), $xbrl-export:serialization-parameters)
  let $labels as string := serialize(xbrl-export:labels($components, $taxonomyName), $xbrl-export:serialization-parameters)
  let $presentation as string := serialize(xbrl-export:presentation($components, $taxonomyName), $xbrl-export:serialization-parameters)
  let $calculation as string := serialize(xbrl-export:calculation($components, $taxonomyName), $xbrl-export:serialization-parameters)
  let $definition as string := serialize(xbrl-export:definition($components, $taxonomyName), $xbrl-export:serialization-parameters)
  let $archive as xs:base64Binary := archive:create(
    (
      { "type" : "directory", "name" : $taxonomyName },
      { "encoding" : "utf-8", "type" : "regular", "name" : $taxonomyName || "/" || $taxonomyName || ".xsd" },
      { "encoding" : "utf-8", "type" : "regular", "name" : $taxonomyName || "/" || $taxonomyName || "_lab.xml" },
      { "encoding" : "utf-8", "type" : "regular", "name" : $taxonomyName || "/" || $taxonomyName || "_pre.xml" },
      { "encoding" : "utf-8", "type" : "regular", "name" : $taxonomyName || "/" || $taxonomyName || "_cal.xml" },
      { "encoding" : "utf-8", "type" : "regular", "name" : $taxonomyName || "/" || $taxonomyName || "_def.xml" }
    ),
    (
      $schema,
      $labels,
      $presentation,
      $calculation,
      $definition
    )
  )
  return
    (
      {
        "content-type": "application/zip",
        "serialization":
        {
          "method": "binary"
        },
        "headers": {
          "Pragma": "public",
          "Expires": "0",
          "Cache-Control": "must-revalidate, post-check=0, pre-check=0",
          "Content-Disposition": "attachment; filename='" || $taxonomyName || ".xbrlx';",
          "Content-Transfer-Encoding": "binary"
        }
      },
      $archive
    )
};

jsoniq version "1.0";

(:
 : Copyright 2012-2015 28msec Inc.
 :)

(:~
 :
This module provides functionality for exporting archives to the XBRL format.

 :
 : @author Dennis Knochenwefel
 : @author Ghislain Fourny
 :)
module namespace xbrl-export = "http://28.io/modules/xbrl/export";

import module namespace archives = "http://28.io/modules/xbrl/archives";
import module namespace concepts = "http://28.io/modules/xbrl/concepts";
import module namespace components = "http://28.io/modules/xbrl/components";
import module namespace networks = "http://28.io/modules/xbrl/networks";
import module namespace labels = "http://28.io/modules/xbrl/labels";

import module namespace archive = "http://zorba.io/modules/archive";
import module namespace credentials = "http://www.28msec.com/modules/credentials";
import schema namespace mongos = "http://www.28msec.com/modules/mongodb/types";

declare variable $xbrl-export:serialization-parameters :=
    <serialization-parameters xmlns="http://www.w3.org/2010/xslt-xquery-serialization">
      <method value="xml"/>
      <indent value="yes"/>
      <encoding value="UTF-8"/>
    </serialization-parameters>;

declare %private function xbrl-export:comment() as comment()
{
  comment { "
    Generated: " || current-dateTime() || " (" || credentials:credentials("MongoDB", "xbrl").db || ")
    Designed by XBRL Switzerland Working Group
    Generated by 28msec (28.io)
  " }
};

declare function xbrl-export:schema(
    $components as object*,
    $taxonomyName as string)
as node()*
{
  xbrl-export:comment(),
  <schema targetNamespace="http://www.xbrl-ch.ch/ch/fr/co/gp/2015-02-26"
      elementFormDefault="qualified"
      xmlns="http://www.w3.org/2001/XMLSchema"
      xmlns:xlink="http://www.w3.org/1999/xlink"
      xmlns:link="http://www.xbrl.org/2003/linkbase"
      xmlns:xbrli="http://www.xbrl.org/2003/instance"
      xmlns:xbrldt="http://xbrl.org/2005/xbrldt"
      xmlns:ch-co="http://www.xbrl-ch.ch/ch/fr/co/gp/2015-02-26"
      xmlns:nonnum="http://www.xbrl.org/dtr/type/non-numeric">
    <import namespace="http://www.xbrl.org/2003/instance" schemaLocation="http://www.xbrl.org/2003/xbrl-instance-2003-12-31.xsd"/>
    <import namespace="http://xbrl.org/2005/xbrldt" schemaLocation="http://www.xbrl.org/2005/xbrldt-2005.xsd" />
    <import namespace="http://www.xbrl.org/dtr/type/non-numeric" schemaLocation="http://www.xbrl.org/dtr/type/nonNumeric-2009-12-16.xsd" />
    {
      components:to-xbrl-annotation($components, $taxonomyName),
      for $concept as element() in concepts:to-xbrl($components.Concepts[])
      group by $name := $concept/@name
      return $concept[1]
    }
  </schema>
};

declare function xbrl-export:labels(
    $components as object*,
    $taxonomyName as string)
as node()*
{
  xbrl-export:comment(),
  <linkbase
      xmlns="http://www.xbrl.org/2003/linkbase"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xmlns:ch-co="http://www.xbrl-ch.ch/ch/fr/co/gp/2015-02-26"
      xmlns:xlink="http://www.w3.org/1999/xlink"
      xmlns:xbrli="http://www.xbrl.org/2003/instance"
      xsi:schemaLocation="http://www.xbrl.org/2003/linkbase http://www.xbrl.org/2003/xbrl-linkbase-2003-12-31.xsd">
  {
    labels:to-xbrl($components.Concepts[], $taxonomyName)
  }
  </linkbase>
};

declare function xbrl-export:presentation(
    $components as object*,
    $taxonomyName as string)
as node()*
{
  xbrl-export:comment(),
  <linkbase
      xmlns="http://www.xbrl.org/2003/linkbase"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xmlns:ch-co="http://www.xbrl-ch.ch/ch/fr/co/gp/2015-02-26"
      xmlns:xlink="http://www.w3.org/1999/xlink"
      xmlns:xbrli="http://www.xbrl.org/2003/instance"
      xsi:schemaLocation="http://www.xbrl.org/2003/linkbase http://www.xbrl.org/2003/xbrl-linkbase-2003-12-31.xsd">
  {
    components:to-xbrl-roleRefs($components, $taxonomyName),
    for $component in $components
    let $presentationNetwork as object? := networks:networks-for-components-and-short-names($component, $networks:PRESENTATION_NETWORK)
    where exists($presentationNetwork) and $component.Role ne null
    return networks:to-xbrl($presentationNetwork, $component, $taxonomyName)
  }
  </linkbase>
};

declare function xbrl-export:calculation(
    $components as object*,
    $taxonomyName as string)
as node()*
{
  xbrl-export:comment(),
  <linkbase
      xmlns="http://www.xbrl.org/2003/linkbase"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xmlns:ch-co="http://www.xbrl-ch.ch/ch/fr/co/gp/2015-02-26"
      xmlns:xlink="http://www.w3.org/1999/xlink"
      xmlns:xbrli="http://www.xbrl.org/2003/instance"
      xsi:schemaLocation="http://www.xbrl.org/2003/linkbase http://www.xbrl.org/2003/xbrl-linkbase-2003-12-31.xsd">
  {
    components:to-xbrl-roleRefs($components, $taxonomyName),
    for $component in $components
    let $calculationNetwork as object? := networks:networks-for-components-and-short-names($component, $networks:CALCULATION_NETWORK)
    where exists($calculationNetwork) and $component.Role ne null
    return networks:to-xbrl($calculationNetwork, $component, $taxonomyName)
  }
  </linkbase>
};

declare function xbrl-export:definition(
    $components as object*,
    $taxonomyName as string)
as node()*
{
  xbrl-export:comment(),
  <linkbase
      xmlns="http://www.xbrl.org/2003/linkbase"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xmlns:ch-co="http://www.xbrl-ch.ch/ch/fr/co/gp/2015-02-26"
      xmlns:xlink="http://www.w3.org/1999/xlink"
      xmlns:xbrli="http://www.xbrl.org/2003/instance"
      xsi:schemaLocation="http://www.xbrl.org/2003/linkbase http://www.xbrl.org/2003/xbrl-linkbase-2003-12-31.xsd">
    <arcroleRef arcroleURI="http://xbrl.org/int/dim/arcrole/all" xlink:type="simple" xlink:href="http://www.xbrl.org/2005/xbrldt-2005.xsd#all"/>
    <arcroleRef arcroleURI="http://xbrl.org/int/dim/arcrole/dimension-domain" xlink:type="simple" xlink:href="http://www.xbrl.org/2005/xbrldt-2005.xsd#dimension-domain"/>
    <arcroleRef arcroleURI="http://xbrl.org/int/dim/arcrole/domain-member" xlink:type="simple" xlink:href="http://www.xbrl.org/2005/xbrldt-2005.xsd#domain-member"/>
    <arcroleRef arcroleURI="http://xbrl.org/int/dim/arcrole/hypercube-dimension" xlink:type="simple" xlink:href="http://www.xbrl.org/2005/xbrldt-2005.xsd#hypercube-dimension"/>
    <arcroleRef arcroleURI="http://xbrl.org/int/dim/arcrole/notAll" xlink:type="simple" xlink:href="http://www.xbrl.org/2005/xbrldt-2005.xsd#notAll"/>
    <arcroleRef arcroleURI="http://xbrl.org/int/dim/arcrole/dimension-default" xlink:type="simple" xlink:href="http://www.xbrl.org/2005/xbrldt-2005.xsd#dimension-default"/>
    {
      components:to-xbrl-roleRefs($components, $taxonomyName),
      for $component in $components
      for $hypercube in values($component.Hypercubes)
      where $component.Role ne null
      return networks:to-xbrl($hypercube, $component, $taxonomyName)
    }
  </linkbase>
};

declare function xbrl-export:archive(
    $archive as item
) as item()*
{
  let $archive as object := archives:archives($archive)
  return xbrl-export:archive($archive, $archive._id)
};

declare function xbrl-export:archive(
    $archive as item,
    $taxonomyName as string)
as item()*
{
  let $archive as object := archives:archives($archive)
  let $components as object* := components:components-for-archives($archive)
  let $schema as string := serialize(xbrl-export:schema($components, $taxonomyName), $xbrl-export:serialization-parameters)
  let $labels as string := serialize(xbrl-export:labels($components, $taxonomyName), $xbrl-export:serialization-parameters)
  let $presentation as string := serialize(xbrl-export:presentation($components, $taxonomyName), $xbrl-export:serialization-parameters)
  let $calculation as string := serialize(xbrl-export:calculation($components, $taxonomyName), $xbrl-export:serialization-parameters)
  let $definition as string := serialize(xbrl-export:definition($components, $taxonomyName), $xbrl-export:serialization-parameters)
  let $archive as xs:base64Binary := archive:create(
    (
      { "type" : "directory", "name" : $taxonomyName },
      { "encoding" : "utf-8", "type" : "regular", "name" : $taxonomyName || "/" || $taxonomyName || ".xsd" },
      { "encoding" : "utf-8", "type" : "regular", "name" : $taxonomyName || "/" || $taxonomyName || "_lab.xml" },
      { "encoding" : "utf-8", "type" : "regular", "name" : $taxonomyName || "/" || $taxonomyName || "_pre.xml" },
      { "encoding" : "utf-8", "type" : "regular", "name" : $taxonomyName || "/" || $taxonomyName || "_cal.xml" },
      { "encoding" : "utf-8", "type" : "regular", "name" : $taxonomyName || "/" || $taxonomyName || "_def.xml" }
    ),
    (
      $schema,
      $labels,
      $presentation,
      $calculation,
      $definition
    )
  )
  return
    (
      {
        "content-type": "application/zip",
        "serialization":
        {
          "method": "binary"
        },
        "headers": {
          "Pragma": "public",
          "Expires": "0",
          "Cache-Control": "must-revalidate, post-check=0, pre-check=0",
          "Content-Disposition": "attachment; filename='" || $taxonomyName || ".zip';",
          "Content-Transfer-Encoding": "binary"
        }
      },
      $archive
    )
};


:green_heart: Fix warning.

:green_heart: Fix warning.

import module namespace hypercubes = "http://28.io/modules/xbrl/hypercubes";
import module namespace reports = "http://28.io/modules/xbrl/reports";
import module namespace components = "http://28.io/modules/xbrl/components";

import module namespace multiplexer = "http://28.io/modules/xbrl/profiles/multiplexer";
import module namespace fiscal-core = "http://28.io/modules/xbrl/profiles/sec/fiscal/core";

import module namespace config = "http://apps.28.io/config";
import module namespace session = "http://apps.28.io/session";
import module namespace api = "http://apps.28.io/api";

declare option rest:response "first-item";

(: Query parameters :)
declare  %rest:case-insensitive                 variable $token         as string? external;
declare  %rest:env                              variable $request-uri   as string  external;
declare  %rest:case-insensitive                 variable $format        as string? external;
declare  %rest:case-insensitive %rest:distinct  variable $cik           as string* external;
declare  %rest:case-insensitive %rest:distinct  variable $edinetcode    as string* external;
declare  %rest:case-insensitive %rest:distinct  variable $tag           as string* external;
declare  %rest:case-insensitive %rest:distinct  variable $ticker        as string* external;
declare  %rest:case-insensitive %rest:distinct  variable $sic           as string* external;
declare  %rest:case-insensitive %rest:distinct  variable $fiscalYear    as string* external;
declare  %rest:case-insensitive %rest:distinct  variable $fiscalPeriod  as string* external;
declare  %rest:case-insensitive %rest:distinct  variable $fiscalPeriodType  as string* external;
declare  %rest:case-insensitive %rest:distinct  variable $eid           as string* external;
declare  %rest:case-insensitive %rest:distinct  variable $aid           as string* external;
declare  %rest:case-insensitive                 variable $validate      as boolean external := false;
declare  %rest:case-insensitive                 variable $eliminate     as boolean external := false;
declare  %rest:case-insensitive                 variable $elimination-threshold as integer external := 0;
declare  %rest:case-insensitive                 variable $report        as string? external;
declare  %rest:case-insensitive                 variable $profile-name  as string  external := $config:profile-name;
declare  %rest:case-insensitive                 variable $language      as string  external := "en-US";
declare  %rest:case-insensitive                 variable $debug         as boolean external := false;

(: Post-processing :)
let $format as string? := api:preprocess-format($format, $request-uri)
let $fiscalYear as integer* := api:preprocess-fiscal-years($fiscalYear)
let $fiscalPeriod as string* := api:preprocess-fiscal-periods($fiscalPeriod)
let $fiscalPeriodType as string* := api:preprocess-fiscal-period-types($fiscalPeriodType)
let $tag as string* := api:preprocess-tags($tag)

let $cik as string* :=
    switch($profile-name)
    case "sec" return $cik
    case "japan" return $edinetcode
    default return ()

(: Entity resolution :)
let $entities := multiplexer:entities(
  $profile-name,
  $eid,
  $cik,
  $tag,
  $ticker,
  $sic,
  (),
  (),
  (),
  ())

let $entities-not-found as boolean :=
  exists(($eid, $cik, $tag, $ticker, $sic)) and empty($entities)

let $report-id as string? := $report
let $report as object? := reports:reports($report)

return
if(empty($report))
then
{
      { status: 404 },
      session:error("report with id '" || $report-id || "' does not exist.", $format)
} else

    (: Fact resolution :)
    let $filter-override as object? :=
        if($profile-name eq "sec")
        then fiscal-core:filter-override(
            $entities,
            $fiscalYear,
            $fiscalPeriod,
            $fiscalPeriodType,
            $aid)
        else components:filter-override($entities, $aid)

    (: Fact resolution :)
    let $hypercube := hypercubes:hypercubes-for-components($report)[1]
    let $filtered-aspects := values($hypercube.Aspects)[exists(($$.Members, $$.DomainRestriction))]
    let $spreadsheet as object? :=
        if(count($filtered-aspects) lt $config:filtered-aspects and not exists(($filter-override)))
        then {
              { status: 403 },
              session:error("The report filters are too weak, which leads to too big an output.", $format)
        } else
            components:spreadsheet(
                $report,
                {|
                    { FilterOverride: $filter-override}[exists($filter-override)],
                    {
                        FlattenRows: true,
                        Eliminate: $eliminate,
                        EliminationThreshold: double($elimination-threshold) div 100,
                        Validate: $validate,
                        Language: $language,
                        Debug: $debug
                    }
                |}
            )
    let $results :=
        {
            { serialization: { method: "json", indent : true } },
            $spreadsheet
        }
    return if($entities-not-found)
           then api:not-found("entity")
           else api:check-and-return-results($token, $results, $format)

import module namespace hypercubes = "http://28.io/modules/xbrl/hypercubes";
import module namespace reports = "http://28.io/modules/xbrl/reports";
import module namespace components = "http://28.io/modules/xbrl/components";

import module namespace multiplexer = "http://28.io/modules/xbrl/profiles/multiplexer";
import module namespace fiscal-core = "http://28.io/modules/xbrl/profiles/sec/fiscal/core";

import module namespace config = "http://apps.28.io/config";
import module namespace session = "http://apps.28.io/session";
import module namespace api = "http://apps.28.io/api";

declare option rest:response "first-item";

(: Query parameters :)
declare  %rest:case-insensitive                 variable $token         as string? external;
declare  %rest:env                              variable $request-uri   as string  external;
declare  %rest:case-insensitive                 variable $format        as string? external;
declare  %rest:case-insensitive %rest:distinct  variable $cik           as string* external;
declare  %rest:case-insensitive %rest:distinct  variable $edinetcode    as string* external;
declare  %rest:case-insensitive %rest:distinct  variable $tag           as string* external;
declare  %rest:case-insensitive %rest:distinct  variable $ticker        as string* external;
declare  %rest:case-insensitive %rest:distinct  variable $sic           as string* external;
declare  %rest:case-insensitive %rest:distinct  variable $fiscalYear    as string* external;
declare  %rest:case-insensitive %rest:distinct  variable $fiscalPeriod  as string* external;
declare  %rest:case-insensitive %rest:distinct  variable $fiscalPeriodType  as string* external;
declare  %rest:case-insensitive %rest:distinct  variable $eid           as string* external;
declare  %rest:case-insensitive %rest:distinct  variable $aid           as string* external;
declare  %rest:case-insensitive                 variable $validate      as boolean external := false;
declare  %rest:case-insensitive                 variable $eliminate     as boolean external := false;
declare  %rest:case-insensitive                 variable $elimination-threshold as integer external := 0;
declare  %rest:case-insensitive                 variable $report        as string? external;
declare  %rest:case-insensitive                 variable $profile-name  as string  external := $config:profile-name;
declare  %rest:case-insensitive                 variable $language      as string  external := "en-US";
declare  %rest:case-insensitive                 variable $debug         as boolean external := false;
declare  %rest:case-insensitive                 variable $count         as boolean external := false;

(: Post-processing :)
let $format as string? := api:preprocess-format($format, $request-uri)
let $fiscalYear as integer* := api:preprocess-fiscal-years($fiscalYear)
let $fiscalPeriod as string* := api:preprocess-fiscal-periods($fiscalPeriod)
let $fiscalPeriodType as string* := api:preprocess-fiscal-period-types($fiscalPeriodType)
let $tag as string* := api:preprocess-tags($tag)

let $cik as string* :=
    switch($profile-name)
    case "sec" return $cik
    case "japan" return $edinetcode
    default return ()

(: Entity resolution :)
let $entities := multiplexer:entities(
  $profile-name,
  $eid,
  $cik,
  $tag,
  $ticker,
  $sic,
  (),
  (),
  (),
  ())

let $entities-not-found as boolean :=
  exists(($eid, $cik, $tag, $ticker, $sic)) and empty($entities)

let $report-id as string? := $report
let $report as object? := reports:reports($report)

return
if(empty($report))
then
{
      { status: 404 },
      session:error("report with id '" || $report-id || "' does not exist.", $format)
} else

    (: Fact resolution :)
    let $filter-override as object? :=
        if($profile-name eq "sec")
        then fiscal-core:filter-override(
            $entities,
            $fiscalYear,
            $fiscalPeriod,
            $fiscalPeriodType,
            $aid)
        else components:filter-override($entities, $aid)

    (: Fact resolution :)
    let $hypercube := hypercubes:hypercubes-for-components($report)[1]
    let $filtered-aspects := values($hypercube.Aspects)[exists(($$.Members, $$.DomainRestriction))]
    let $spreadsheet as object? :=
        if(count($filtered-aspects) lt $config:filtered-aspects and not exists(($filter-override)))
        then {
              { status: 403 },
              session:error("The report filters are too weak, which leads to too big an output.", $format)
        } else
            components:spreadsheet(
                $report,
                {|
                    { FilterOverride: $filter-override}[exists($filter-override)],
                    {
                        FlattenRows: true,
                        Eliminate: $eliminate,
                        EliminationThreshold: double($elimination-threshold) div 100,
                        Validate: $validate,
                        Language: $language,
                        Debug: $debug
                    }
                |}
            )
    let $results :=
        {
            { serialization: { method: "json", indent : true } },
            $spreadsheet
        }
    return if($entities-not-found)
           then api:not-found("entity")
           else api:check-and-return-results($token, $results, $format)


removed superfluous unboxing.

removed superfluous unboxing.

import module namespace config = "http://apps.28.io/config";
import module namespace api = "http://apps.28.io/api";
import module namespace session = "http://apps.28.io/session";

import module namespace conversion = "http://28.io/modules/xbrl/conversion";
import module namespace hypercubes = "http://28.io/modules/xbrl/hypercubes";
import module namespace entities = "http://28.io/modules/xbrl/entities";
import module namespace reports = "http://28.io/modules/xbrl/reports";
import module namespace concepts = "http://28.io/modules/xbrl/concepts";
import module namespace facts = "http://28.io/modules/xbrl/facts";
import module namespace rules = "http://28.io/modules/xbrl/rules";
import module namespace components = "http://28.io/modules/xbrl/components";

import module namespace sec = "http://28.io/modules/xbrl/profiles/sec/core";
import module namespace companies = "http://28.io/modules/xbrl/profiles/sec/companies";
import module namespace fiscal-core = "http://28.io/modules/xbrl/profiles/sec/fiscal/core";
import module namespace multiplexer = "http://28.io/modules/xbrl/profiles/multiplexer";

import module namespace request = "http://www.28msec.com/modules/http-request";

declare function local:param-values($name as string) as string*
{
    switch(true)
     case $name eq "xbrl:Concept"
        return (request:param-values("concept"), request:param-values("xbrl:Concept"))

     case $name eq "sec:FiscalYear" and $profile-name eq "sec"
        return ($fiscalYear, request:param-values("sec:FiscalYear"))[$$ ne "LATEST"]
     case $name eq "sec:FiscalYear::type" and $profile-name eq "sec"
        return "integer"
     case $name eq "sec:FiscalPeriod" and $profile-name eq "sec"
        return ($fiscalPeriod, request:param-values("sec:FiscalPeriod"))
     case $name eq "sec:FiscalPeriodType" and $profile-name eq "sec"
        return ($fiscalPeriodType, request:param-values("sec:FiscalPeriodType"))
     case $name eq "dei:LegalEntityAxis" and $profile-name eq "sec"
        return
         if(empty((request:param-values("sec:LegalEntityAxis"), request:param-values("sec:LegalEntityAxis::default"))))
         then "sec:DefaultLegalEntity"
         else request:param-values("sec:LegalEntityAxis")
     case $name eq "dei:LegalEntityAxis::default" return
         if(empty((request:param-values("sec:LegalEntityAxis"), request:param-values("sec:LegalEntityAxis::default"))))
         then "sec:DefaultLegalEntity"
         else request:param-values("sec:LegalEntityAxis::default")
     case $name eq "xbrl:Entity" and $profile-name eq "sec" return (
         let $companies := multiplexer:entities(
            $profile-name,
            $eid,
            $cik,
            api:preprocess-tags($tag),
            $ticker,
            $sic)._id
        return if(empty(($cik,$tag,$ticker,$sic)) or exists($companies))
               then $companies
               else "dummy",
        request:param-values("xbrl:Entity"))
     case $name eq "xbrl28:Archive" and $profile-name eq "sec" return (
            let $fiscalYears := ($fiscalYear, request:param-values("sec:FiscalYear"))
            let $fiscalPeriods := local:param-values("sec:FiscalPeriod")
            let $entities := entities:entities(local:param-values("xbrl:Entity"))
            return
                if($fiscalYears = "LATEST")
                then fiscal-core:latest-filings($entities, $fiscalPeriods)._id
                else (),
            $aid,
            request:param-values("xbrl28:Archive")
        )
     default return request:param-values($name)
};

declare function local:param-names() as string*
{
    let $names := request:param-names()
    return distinct-values((
        $names[contains($$, ":")],
        "xbrl:Concept"[$names = "concept"],

        "sec:Accepted"[$profile-name eq "sec"],
        "sec:FiscalPeriod"[$profile-name eq "sec"],
        "sec:FiscalPeriodType"[$profile-name eq "sec"],
        "sec:FiscalYear"[$profile-name eq "sec"],
        "xbrl:Entity"[$profile-name eq "sec" and $names = ("cik", "tag", "ticker", "sic")],
        "dei:LegalEntityAxis"[$profile-name eq "sec"],
        "dei:LegalEntityAxis::default"[$profile-name eq "sec"],
        "xbrl28:Archive"[$profile-name eq "sec"]))
};

declare function local:cast-sequence($values as atomic*, $type as string) as atomic*
{
  for $value in $values
  return
      switch ($type)
      case "integer" return $value cast as integer
      case "string" return $value cast as string
      default return error(xs:QName("local:unsupported-type"), $type || ": unsupported type")
};

declare function local:hypercube() as object
{
    let $hypercube-spec :=
    {|
        for $parameter in local:param-names()
        group by $dimension-name :=
            switch(true)
            case ends-with(lower-case($parameter), "::default")
                return substring-before($parameter, "::default")
            case ends-with(lower-case($parameter), ":default")
                return substring-before($parameter, ":default")
            case ends-with(lower-case($parameter), "::type")
                return substring-before($parameter, "::type")
            default
                return $parameter
        let $all as boolean :=
            (local:param-values($dimension-name) ! upper-case($$)) = "ALL"
        let $type as string? :=
            (local:param-values($dimension-name || "::type"), local:param-values($dimension-name || ":type"))[1]

        let $values := local:param-values($dimension-name)
        let $typed-values := if (exists($type)) then local:cast-sequence($values[$$ ne "ALL"], $type) else $values

        let $has-default := ($parameter = $dimension-name || "::default") or ($parameter = $dimension-name || ":default")
        let $default-value := (local:param-values($dimension-name || "::default"), local:param-values($dimension-name || ":default"))[1]
        let $typed-default-value := if (exists($type)) then local:cast-sequence($default-value, $type) else $default-value

        return
        {
            $dimension-name : {|
                { "Type" : $type }[exists($type)],
                if(not($all))
                then { "Domain" : [ $typed-values ] }[exists($typed-values)]
                else (),
                { "Default" : $typed-default-value }[$has-default]
            |}
        }
    |}
    return hypercubes:user-defined-hypercube($hypercube-spec)
};

(: Query parameters :)
declare  %rest:case-insensitive                 variable $token             as string? external;
declare  %rest:case-insensitive                 variable $profile-name      as string  external := $config:profile-name;
declare  %rest:env                              variable $request-uri       as string  external;
declare  %rest:case-insensitive                 variable $format            as string? external;
declare  %rest:case-insensitive %rest:distinct  variable $cik               as string* external;
declare  %rest:case-insensitive %rest:distinct  variable $tag               as string* external;
declare  %rest:case-insensitive %rest:distinct  variable $ticker            as string* external;
declare  %rest:case-insensitive %rest:distinct  variable $sic               as string* external;
declare  %rest:case-insensitive %rest:distinct  variable $fiscalYear        as string* external := "LATEST";
declare  %rest:case-insensitive %rest:distinct  variable $fiscalPeriod      as string* external := "FY";
declare  %rest:case-insensitive %rest:distinct  variable $fiscalPeriodType  as string* external := ("instant", "YTD");
declare  %rest:case-insensitive %rest:distinct  variable $eid               as string* external;
declare  %rest:case-insensitive %rest:distinct  variable $aid               as string* external;
declare  %rest:case-insensitive                 variable $map               as string? external;
declare  %rest:case-insensitive                 variable $rule              as string? external;
declare  %rest:case-insensitive                 variable $report            as string? external;
declare  %rest:case-insensitive                 variable $validate          as boolean external := false;
declare  %rest:case-insensitive                 variable $labels            as boolean external := false;
declare  %rest:case-insensitive                 variable $additional-rules  as string? external;
declare  %rest:case-insensitive                 variable $debug             as boolean external := false;

session:audit-call($token);

(: Post-processing :)
let $format as string? := api:preprocess-format($format, $request-uri)
let $tag as string* := api:preprocess-tags($tag)

(: Object resolution :)
let $entities as object* :=
    companies:companies(
        $cik,
        $tag,
        $ticker,
        $sic,
        $eid,
        $aid)
let $report as object? := reports:reports($report)
let $map as item* :=
    if(exists($report))
    then reports:concept-map($report)
    else $map
let $rule as item* :=
    (
        if(exists($report))
        then reports:rules($report)
        else rules:rules($rule),
        if(exists($additional-rules))
        then rules:rules($additional-rules)
        else ()
    )

let $hypercube := local:hypercube()

let $facts :=
    let $options := {|
      {
        Hypercube : $hypercube,
        Validate: $validate
      },
      { "ConceptMaps" : $map }[exists($map)],
      { "Rules" : [ $rule ] }[exists($rule)],
      { "Concepts" : $report.Concepts }[exists($report) and exists($report.Concepts)]
    |}
    return if($profile-name eq "sec")
    then sec:facts-for($options)
    else facts:facts-for($options)

let $facts :=
  if(not $labels)
  then $facts
  else
    let $archives as string* := distinct-values($facts.Aspects."xbrl28:Archive")
    let $concept-names as string* := distinct-values($facts.Aspects."xbrl:Concept")
    let $concepts as object* :=
      (
          concepts:concepts($concept-names, $archives, $concepts:ANY_COMPONENT_LINK_ROLE),
          (reports:concepts(($report,$map)))[$$.Name = $concept-names]
      )
    let $language as string := ( $report.$components:DEFAULT-LANGUAGE , $concepts:AMERICAN_ENGLISH )[1]
    let $roles as string* := ( $report.Role, $concepts:ANY_COMPONENT_LINK_ROLE )
    for $fact as object in $facts
    let $entityName as string := $entities[$$._id eq $fact.Aspects."xbrl:Entity"].Profiles.SEC.CompanyName
    return
    {|
      $fact,
      let $concept-labels as object? := facts:labels($fact, $roles, $concepts:STANDARD_LABEL_ROLE, $language, $concepts, ())
      let $standard-labels as object := conversion:get-standard-labels($fact, $entityName)
      return { Labels : {| $concept-labels, $standard-labels |} }
    |}

let $facts :=
  if($profile-name eq "sec")
  then api:normalize-facts($facts)
  else $facts

let $result := {
    NetworkIdentifier : "http://bizql.io/facts",
    TableName : "xbrl:Facts",
    FactTable : [ $facts ]
}
let $comment :=
{
    NumFacts: count($facts),
    TotalNumFacts: session:num-facts(),
    TotalNumArchives: session:num-archives(),
    TotalNumEntities: session:num-entities()
}
let $comment :=
    if($debug)
    then {|
        $comment,
        { DebugInfo: { Hypercube: $hypercube } }
    |}
    else $comment
let $serializers := {
    to-xml : function($res as object) as node()* {
           <FactTable NetworkIdentifier="http://bizql.io/facts"
            TableName="xbrl:Facts">{
                conversion:facts-to-xml($res.FactTable[], { Caller: "Report"})
        }</FactTable>
    },
    to-csv : function($res as object) as string {
        (conversion:facts-to-csv($res.FactTable[], { Caller: "Report" }), "")[1]
    }
}

let $results := api:serialize($result, $comment, $serializers, $format, "facts")
return api:check-and-return-results($token, $results, $format)

import module namespace config = "http://apps.28.io/config";
import module namespace api = "http://apps.28.io/api";
import module namespace session = "http://apps.28.io/session";

import module namespace conversion = "http://28.io/modules/xbrl/conversion";
import module namespace hypercubes = "http://28.io/modules/xbrl/hypercubes";
import module namespace entities = "http://28.io/modules/xbrl/entities";
import module namespace reports = "http://28.io/modules/xbrl/reports";
import module namespace concepts = "http://28.io/modules/xbrl/concepts";
import module namespace facts = "http://28.io/modules/xbrl/facts";
import module namespace rules = "http://28.io/modules/xbrl/rules";
import module namespace components = "http://28.io/modules/xbrl/components";

import module namespace sec = "http://28.io/modules/xbrl/profiles/sec/core";
import module namespace companies = "http://28.io/modules/xbrl/profiles/sec/companies";
import module namespace fiscal-core = "http://28.io/modules/xbrl/profiles/sec/fiscal/core";
import module namespace multiplexer = "http://28.io/modules/xbrl/profiles/multiplexer";

import module namespace request = "http://www.28msec.com/modules/http-request";

declare function local:param-values($name as string) as string*
{
    switch(true)
     case $name eq "xbrl:Concept"
        return (request:param-values("concept"), request:param-values("xbrl:Concept"))

     case $name eq "sec:FiscalYear" and $profile-name eq "sec"
        return ($fiscalYear, request:param-values("sec:FiscalYear"))[$$ ne "LATEST"]
     case $name eq "sec:FiscalYear::type" and $profile-name eq "sec"
        return "integer"
     case $name eq "sec:FiscalPeriod" and $profile-name eq "sec"
        return ($fiscalPeriod, request:param-values("sec:FiscalPeriod"))
     case $name eq "sec:FiscalPeriodType" and $profile-name eq "sec"
        return ($fiscalPeriodType, request:param-values("sec:FiscalPeriodType"))
     case $name eq "dei:LegalEntityAxis" and $profile-name eq "sec"
        return
         if(empty((request:param-values("sec:LegalEntityAxis"), request:param-values("sec:LegalEntityAxis::default"))))
         then "sec:DefaultLegalEntity"
         else request:param-values("sec:LegalEntityAxis")
     case $name eq "dei:LegalEntityAxis::default" return
         if(empty((request:param-values("sec:LegalEntityAxis"), request:param-values("sec:LegalEntityAxis::default"))))
         then "sec:DefaultLegalEntity"
         else request:param-values("sec:LegalEntityAxis::default")
     case $name eq "xbrl:Entity" and $profile-name eq "sec" return (
         let $companies := multiplexer:entities(
            $profile-name,
            $eid,
            $cik,
            api:preprocess-tags($tag),
            $ticker,
            $sic)._id
        return if(empty(($cik,$tag,$ticker,$sic)) or exists($companies))
               then $companies
               else "dummy",
        request:param-values("xbrl:Entity"))
     case $name eq "xbrl28:Archive" and $profile-name eq "sec" return (
            let $fiscalYears := ($fiscalYear, request:param-values("sec:FiscalYear"))
            let $fiscalPeriods := local:param-values("sec:FiscalPeriod")
            let $entities := entities:entities(local:param-values("xbrl:Entity"))
            return
                if($fiscalYears = "LATEST")
                then fiscal-core:latest-filings($entities, $fiscalPeriods)._id
                else (),
            $aid,
            request:param-values("xbrl28:Archive")
        )
     default return request:param-values($name)
};

declare function local:param-names() as string*
{
    let $names := request:param-names()
    return distinct-values((
        $names[contains($$, ":")],
        "xbrl:Concept"[$names = "concept"],

        "sec:Accepted"[$profile-name eq "sec"],
        "sec:FiscalPeriod"[$profile-name eq "sec"],
        "sec:FiscalPeriodType"[$profile-name eq "sec"],
        "sec:FiscalYear"[$profile-name eq "sec"],
        "xbrl:Entity"[$profile-name eq "sec" and $names = ("cik", "tag", "ticker", "sic")],
        "dei:LegalEntityAxis"[$profile-name eq "sec"],
        "dei:LegalEntityAxis::default"[$profile-name eq "sec"],
        "xbrl28:Archive"[$profile-name eq "sec"]))
};

declare function local:cast-sequence($values as atomic*, $type as string) as atomic*
{
  for $value in $values
  return
      switch ($type)
      case "integer" return $value cast as integer
      case "string" return $value cast as string
      default return error(xs:QName("local:unsupported-type"), $type || ": unsupported type")
};

declare function local:hypercube() as object
{
    let $hypercube-spec :=
    {|
        for $parameter in local:param-names()
        group by $dimension-name :=
            switch(true)
            case ends-with(lower-case($parameter), "::default")
                return substring-before($parameter, "::default")
            case ends-with(lower-case($parameter), ":default")
                return substring-before($parameter, ":default")
            case ends-with(lower-case($parameter), "::type")
                return substring-before($parameter, "::type")
            default
                return $parameter
        let $all as boolean :=
            (local:param-values($dimension-name) ! upper-case($$)) = "ALL"
        let $type as string? :=
            (local:param-values($dimension-name || "::type"), local:param-values($dimension-name || ":type"))[1]

        let $values := local:param-values($dimension-name)
        let $typed-values := if (exists($type)) then local:cast-sequence($values[$$ ne "ALL"], $type) else $values

        let $has-default := ($parameter = $dimension-name || "::default") or ($parameter = $dimension-name || ":default")
        let $default-value := (local:param-values($dimension-name || "::default"), local:param-values($dimension-name || ":default"))[1]
        let $typed-default-value := if (exists($type)) then local:cast-sequence($default-value, $type) else $default-value

        return
        {
            $dimension-name : {|
                { "Type" : $type }[exists($type)],
                if(not($all))
                then { "Domain" : [ $typed-values ] }[exists($typed-values)]
                else (),
                { "Default" : $typed-default-value }[$has-default]
            |}
        }
    |}
    return hypercubes:user-defined-hypercube($hypercube-spec)
};

(: Query parameters :)
declare  %rest:case-insensitive                 variable $token             as string? external;
declare  %rest:case-insensitive                 variable $profile-name      as string  external := $config:profile-name;
declare  %rest:env                              variable $request-uri       as string  external;
declare  %rest:case-insensitive                 variable $format            as string? external;
declare  %rest:case-insensitive %rest:distinct  variable $cik               as string* external;
declare  %rest:case-insensitive %rest:distinct  variable $tag               as string* external;
declare  %rest:case-insensitive %rest:distinct  variable $ticker            as string* external;
declare  %rest:case-insensitive %rest:distinct  variable $sic               as string* external;
declare  %rest:case-insensitive %rest:distinct  variable $fiscalYear        as string* external := "LATEST";
declare  %rest:case-insensitive %rest:distinct  variable $fiscalPeriod      as string* external := "FY";
declare  %rest:case-insensitive %rest:distinct  variable $fiscalPeriodType  as string* external := ("instant", "YTD");
declare  %rest:case-insensitive %rest:distinct  variable $eid               as string* external;
declare  %rest:case-insensitive %rest:distinct  variable $aid               as string* external;
declare  %rest:case-insensitive                 variable $map               as string? external;
declare  %rest:case-insensitive                 variable $rule              as string? external;
declare  %rest:case-insensitive                 variable $report            as string? external;
declare  %rest:case-insensitive                 variable $validate          as boolean external := false;
declare  %rest:case-insensitive                 variable $labels            as boolean external := false;
declare  %rest:case-insensitive                 variable $additional-rules  as string? external;
declare  %rest:case-insensitive                 variable $debug             as boolean external := false;

session:audit-call($token);

(: Post-processing :)
let $format as string? := api:preprocess-format($format, $request-uri)
let $tag as string* := api:preprocess-tags($tag)

(: Object resolution :)
let $entities as object* :=
    companies:companies(
        $cik,
        $tag,
        $ticker,
        $sic,
        $eid,
        $aid)
let $report as object? := reports:reports($report)
let $map as item* :=
    if(exists($report))
    then reports:concept-map($report)
    else $map
let $rule as item* :=
    (
        if(exists($report))
        then reports:rules($report)
        else rules:rules($rule),
        if(exists($additional-rules))
        then rules:rules($additional-rules)
        else ()
    )

let $hypercube := local:hypercube()

let $facts :=
    let $options := {|
      {
        Hypercube : $hypercube,
        Validate: $validate
      },
      { "ConceptMaps" : $map }[exists($map)],
      { "Rules" : [ $rule ] }[exists($rule)],
      { "Concepts" : [ $report.Concepts[] ] }[exists($report) and exists($report.Concepts)]
    |}
    return if($profile-name eq "sec")
    then sec:facts-for($options)
    else facts:facts-for($options)

let $facts :=
  if(not $labels)
  then $facts
  else
    let $archives as string* := distinct-values($facts.Aspects."xbrl28:Archive")
    let $concept-names as string* := distinct-values($facts.Aspects."xbrl:Concept")
    let $concepts as object* :=
      (
          concepts:concepts($concept-names, $archives, $concepts:ANY_COMPONENT_LINK_ROLE),
          (reports:concepts(($report,$map)))[$$.Name = $concept-names]
      )
    let $language as string := ( $report.$components:DEFAULT-LANGUAGE , $concepts:AMERICAN_ENGLISH )[1]
    let $roles as string* := ( $report.Role, $concepts:ANY_COMPONENT_LINK_ROLE )
    for $fact as object in $facts
    let $entityName as string := $entities[$$._id eq $fact.Aspects."xbrl:Entity"].Profiles.SEC.CompanyName
    return
    {|
      $fact,
      let $concept-labels as object? := facts:labels($fact, $roles, $concepts:STANDARD_LABEL_ROLE, $language, $concepts, ())
      let $standard-labels as object := conversion:get-standard-labels($fact, $entityName)
      return { Labels : {| $concept-labels, $standard-labels |} }
    |}

let $facts :=
  if($profile-name eq "sec")
  then api:normalize-facts($facts)
  else $facts

let $result := {
    NetworkIdentifier : "http://bizql.io/facts",
    TableName : "xbrl:Facts",
    FactTable : [ $facts ]
}
let $comment :=
{
    NumFacts: count($facts),
    TotalNumFacts: session:num-facts(),
    TotalNumArchives: session:num-archives(),
    TotalNumEntities: session:num-entities()
}
let $comment :=
    if($debug)
    then {|
        $comment,
        { DebugInfo: { Hypercube: $hypercube } }
    |}
    else $comment
let $serializers := {
    to-xml : function($res as object) as node()* {
           <FactTable NetworkIdentifier="http://bizql.io/facts"
            TableName="xbrl:Facts">{
                conversion:facts-to-xml($res.FactTable[], { Caller: "Report"})
        }</FactTable>
    },
    to-csv : function($res as object) as string {
        (conversion:facts-to-csv($res.FactTable[], { Caller: "Report" }), "")[1]
    }
}

let $results := api:serialize($result, $comment, $serializers, $format, "facts")
return api:check-and-return-results($token, $results, $format)


:walking: Add back.

:walking: Add back.

import module namespace config = "http://apps.28.io/config";
import module namespace api = "http://apps.28.io/api";
import module namespace session = "http://apps.28.io/session";

import module namespace conversion = "http://28.io/modules/xbrl/conversion";
import module namespace hypercubes = "http://28.io/modules/xbrl/hypercubes";
import module namespace reports = "http://28.io/modules/xbrl/reports";
import module namespace concepts = "http://28.io/modules/xbrl/concepts";
import module namespace facts = "http://28.io/modules/xbrl/facts";
import module namespace labels = "http://28.io/modules/xbrl/labels";
import module namespace rules = "http://28.io/modules/xbrl/rules";
import module namespace components = "http://28.io/modules/xbrl/components";
import module namespace entities = "http://28.io/modules/xbrl/entities";
import module namespace archives = "http://28.io/modules/xbrl/archives";

import module namespace multiplexer = "http://28.io/modules/xbrl/profiles/multiplexer";

import module namespace request = "http://www.28msec.com/modules/http-request";

declare option rest:response "first-item";

declare variable $local:additional-concepts as object* := (
  {
    Name: "sec:DefaultLegalEntity",
    Labels : [ {
        Role : "http://www.xbrl.org/2003/role/label",
        Language : "en-us",
        Value : "Default Legal Entity [Member]"
    } ]
  },
  {
    Name: "xbrl:NonNumeric",
    Labels : [ {
        Role : "http://www.xbrl.org/2003/role/label",
        Language : "en-us",
        Value : "Non Numeric"
    } ]
  }
);

declare function local:param-values(
    $name as string,
    $entities as object*) as string*
{
    switch(true)
     case $name eq "xbrl:Concept"
        return (request:param-values("concept"), request:param-values("xbrl:Concept"))

    (: sec profile :)
     case $name eq "sec:FiscalYear" and $profile-name eq "sec"
        return ($fiscalYear, request:param-values("sec:FiscalYear"))[$$ ne "LATEST"]
     case $name eq "sec:FiscalYear::type" and $profile-name eq "sec"
        return "integer"
     case $name eq "sec:FiscalPeriod" and $profile-name eq "sec"
        return ($fiscalPeriod, request:param-values("sec:FiscalPeriod"))
     case $name eq "sec:FiscalPeriod::type" and $profile-name eq "sec"
        return "string"
     case $name eq "sec:FiscalPeriodType" and $profile-name eq "sec"
        return ($fiscalPeriodType, request:param-values("sec:FiscalPeriodType"))
     case $name eq "sec:FiscalPeriodType::type" and $profile-name eq "sec"
        return "string"
     case $name eq "dei:LegalEntityAxis" and $profile-name eq "sec"
        return
         if(empty((request:param-values("sec:LegalEntityAxis"), request:param-values("sec:LegalEntityAxis::default"))))
         then "sec:DefaultLegalEntity"
         else request:param-values("sec:LegalEntityAxis")
     case $name eq "dei:LegalEntityAxis::default" return
         if(empty((request:param-values("sec:LegalEntityAxis"), request:param-values("sec:LegalEntityAxis::default"))))
         then "sec:DefaultLegalEntity"
         else request:param-values("sec:LegalEntityAxis::default")

     case $name eq "xbrl:Entity" and $profile-name = ("sec", "japan")
         return (
                if(empty(($cik,$tag,$ticker,$sic)) or exists($entities))
                then entities:eid($entities)
                else "dummy",
                request:param-values("xbrl:Entity")
            )
     case $name eq "xbrl28:Archive" and $profile-name = ("sec", "japan") return (
            let $prefix as string :=
                switch($profile-name)
                case "sec" return "sec"
                case "japan" return "fsa"
                default return (: not reachable :) ()
            let $fiscalYears :=
                let $years := request:param-values( $prefix || ":FiscalYear")
                return if ($years) then $years else $fiscalYear
            let $fiscalPeriods := local:param-values($prefix || ":FiscalPeriod", $entities)
            return
                if($fiscalYears = "LATEST")
                then archives:aid(multiplexer:latest-filings($profile-name, $entities, $fiscalPeriods))
                else (),
            $aid,
            request:param-values("xbrl28:Archive")
        )

     (: japan profile :)
     case $name eq "fsa:FiscalYear" and $profile-name eq "japan"
     return ($fiscalYear, request:param-values("fsa:FiscalYear"))[$$ ne "LATEST"]
     case $name eq "fsa:FiscalYear::type" and $profile-name eq "japan"
     return "integer"
     case $name eq "fsa:FiscalPeriod" and $profile-name eq "japan"
     return ($fiscalPeriod, request:param-values("fsa:FiscalPeriod"))
     case $name eq "fsa:FiscalPeriod::type" and $profile-name eq "japan"
     return "string"
     case $name eq "fsa:FiscalPeriodType" and $profile-name eq "japan"
     return ($fiscalPeriodType, request:param-values("fsa:FiscalPeriodType"))
     case $name eq "fsa:FiscalPeriodType::type" and $profile-name eq "japan"
     return "string"
     case $name eq "fsa:ArchiveFiscalYear::type" and $profile-name eq "japan"
     return "integer"
     case $name eq "fsa:ArchiveFiscalPeriod::type" and $profile-name eq "japan"
     return "string"

     default return request:param-values($name)
};

declare function local:param-names() as string*
{
    let $names := request:param-names()
    return distinct-values((
        (: generic xbrl :)
        $names[contains($$, ":")],
        "xbrl:Entity"[$names = ("eid")],
        "xbrl28:Archive"[$profile-name = ("sec", "japan")],
        "xbrl:Concept"[$names = "concept"],

        (: sec profile :)
        "sec:Accepted"[$profile-name eq "sec"],
        "sec:FiscalPeriod"[$profile-name eq "sec"],
        "sec:FiscalPeriodType"[$profile-name eq "sec"],
        "sec:FiscalYear"[$profile-name eq "sec"],
        "xbrl:Entity"[$profile-name eq "sec" and
                      $names = ("cik", "tag", "ticker", "sic")],
        "dei:LegalEntityAxis"[$profile-name eq "sec"],
        "dei:LegalEntityAxis::default"[$profile-name eq "sec"],

        (: japan profile :)
        "fsa:Submitted"[$profile-name eq "japan"],
        "fsa:FiscalPeriod"[$profile-name eq "japan" and
                           exists($fiscalPeriod)],
        "fsa:FiscalPeriodType"[$profile-name eq "japan" and
                               exists($fiscalPeriodType)],
        "fsa:FiscalYear"[$profile-name eq "japan" and
                         exists($fiscalYear)],
        "xbrl:Entity"[$profile-name eq "japan" and
                      $names = ("edinetcode", "tag", "ticker")]
    ))
};

declare function local:cast-sequence($values as atomic*, $type as string) as atomic*
{
  for $value in $values
  return
      switch ($type)
      case "integer" return $value cast as integer
      case "string" return $value cast as string
      default return error(xs:QName("local:unsupported-type"), $type || ": unsupported type")
};

declare function local:hypercube($entities as object*) as object
{
    let $hypercube-spec :=
    {|
        for $parameter in local:param-names()
        group by $dimension-name :=
            switch(true)
            case ends-with(lower-case($parameter), "::default")
                return substring-before($parameter, "::default")
            case ends-with(lower-case($parameter), ":default")
                return substring-before($parameter, ":default")
            case ends-with(lower-case($parameter), "::type")
                return substring-before($parameter, "::type")
            default
                return $parameter
        let $all as boolean :=
            (local:param-values($dimension-name, $entities) ! upper-case($$)) = "ALL"
        let $type as string? :=
            (local:param-values($dimension-name || "::type", $entities),
             local:param-values($dimension-name || ":type", $entities))[1]

        let $values := local:param-values($dimension-name, $entities)
        let $typed-values := if (exists($type)) then local:cast-sequence($values[$$ ne "ALL"], $type) else $values

        let $has-default := ($parameter = $dimension-name || "::default") or ($parameter = $dimension-name || ":default")
        let $default-value := (local:param-values($dimension-name || "::default", $entities),
                               local:param-values($dimension-name || ":default", $entities))[1]
        let $typed-default-value := if (exists($type)) then local:cast-sequence($default-value, $type) else $default-value

        return
        {
            $dimension-name : {|
                { "Type" : $type }[exists($type)],
                { "Domain" : [ $typed-values ] }
                    [exists($typed-values) and not($all)],
                { "Default" : $typed-default-value }[$has-default],
                { "Default" : null }
                    [$all and (not $has-default) and exists($type)],
                { "Default" : "xbrl28:Domain" }
                    [$all and (not $has-default) and empty($type)]
            |}
        }
    |}
    return hypercubes:user-defined-hypercube($hypercube-spec, { IsOpen: $open })
};

(: Query parameters :)
declare  %rest:case-insensitive                 variable $token             as string? external;
declare  %rest:case-insensitive                 variable $profile-name      as string  external := $config:profile-name;
declare  %rest:env                              variable $request-uri       as string  external;
declare  %rest:case-insensitive                 variable $format            as string? external;
declare  %rest:case-insensitive %rest:distinct  variable $cik               as string* external;
declare  %rest:case-insensitive %rest:distinct  variable $edinetcode        as string* external;
declare  %rest:case-insensitive %rest:distinct  variable $tag               as string* external;
declare  %rest:case-insensitive %rest:distinct  variable $ticker            as string* external;
declare  %rest:case-insensitive %rest:distinct  variable $sic               as string* external;
declare  %rest:case-insensitive %rest:distinct  variable $fiscalYear        as string* external := ();
declare  %rest:case-insensitive %rest:distinct  variable $fiscalPeriod      as string* external := ();
declare  %rest:case-insensitive %rest:distinct  variable $fiscalPeriodType  as string* external := ();
declare  %rest:case-insensitive %rest:distinct  variable $eid               as string* external;
declare  %rest:case-insensitive %rest:distinct  variable $aid               as string* external;
declare  %rest:case-insensitive                 variable $map               as string? external;
declare  %rest:case-insensitive                 variable $rule              as string? external;
declare  %rest:case-insensitive                 variable $report            as string? external;
declare  %rest:case-insensitive                 variable $validate          as boolean external := false;
declare  %rest:case-insensitive                 variable $open              as boolean external := false;
declare  %rest:case-insensitive                 variable $labels            as boolean external := true;
declare  %rest:case-insensitive                 variable $additional-rules  as string? external;
declare  %rest:case-insensitive                 variable $debug             as boolean external := false;
declare  %rest:case-insensitive                 variable $language          as string  external := "en-US";
declare  %rest:case-insensitive                 variable $count             as boolean external := false;

(: Post-processing :)
let $format as string? := api:preprocess-format($format, $request-uri)
let $tag as string* := api:preprocess-tags($tag)

let $cik as string* :=
    switch($profile-name)
    case "sec" return $cik
    case "japan" return $edinetcode
    default return ()

(: Entity resolution :)
let $entities := multiplexer:entities(
  $profile-name,
  $eid,
  $cik,
  api:preprocess-tags($tag),
  $ticker,
  $sic,
  (),
  (),
  $aid,
  ())

let $entities-not-found as boolean :=
  exists(($eid, $cik, $tag, $ticker, $sic)) and empty($entities)

let $report as object? := reports:reports($report)
let $map as item* :=
    if(exists($report))
    then reports:concept-map($report)
    else $map
let $rule as item* :=
    (
        if(exists($report))
        then reports:rules($report)
        else rules:rules($rule),
        if(exists($additional-rules))
        then rules:rules($additional-rules)
        else ()
    )

let $hypercube := local:hypercube($entities)

let $facts :=
    let $options := {|
      {
        Hypercube : $hypercube,
        Validate: $validate
      },
      { "ConceptMaps" : $map }[exists($map)],
      { "Rules" : [ $rule ] }[exists($rule)],
      { "Concepts" : $report.Concepts }[exists($report) and exists($report.Concepts)],
      { AggregateAspectMax: "sec:Accepted" }[$profile-name eq "sec"]
    |}
    return facts:facts-for($options)

let $facts :=
  if(not $labels)
  then $facts
  else
    let $archives as string* := distinct-values($facts.Aspects."xbrl28:Archive")
    let $concept-names as string* := (
      distinct-values((keys($facts.Aspects), values($facts.Aspects)[$$ instance of string]))
    )
    let $concepts as object* :=
      (
          concepts:concepts($concept-names, $archives, $concepts:ANY_COMPONENT_LINK_ROLE),
          $report.Concepts[][$$.Name = $concept-names],
          if($profile-name eq "sec") then $local:additional-concepts else ()
      )
    let $language as string := ( $language, $report.$components:DEFAULT-LANGUAGE , $labels:AMERICAN_ENGLISH )[1]
    let $nonFetchedEntities as string* := request:param-values("xbrl:Entity")[not $$ = entities:eid($entities)]
    let $entities as object* := ($entities, entities:entities($nonFetchedEntities))
    for $fact as object in $facts
    return
    {|
      $fact,
      let $concept-labels as object? := labels:labels-for-facts(
        $fact,
        $labels:STANDARD_LABEL_ROLE,
        $language,
        $concepts,
        $entities,
        ()
      )
      return { Labels : $concept-labels }
    |}

let $facts := api:normalize-facts($facts)

let $result := {
                    NetworkIdentifier : "http://bizql.io/facts",
                    TableName : "xbrl:Facts",
                    FactTable : [ if($count) then () else $facts ]
               }
let $comment :=
{
    NumFacts: count($facts),
    TotalNumFacts: session:num-facts(),
    TotalNumArchives: session:num-archives(),
    TotalNumEntities: session:num-entities()
}
let $comment :=
    if($debug)
    then {|
        $comment,
        { DebugInfo: { Hypercube: $hypercube } }
    |}
    else $comment
let $serializers := {
    to-xml : function($res as object) as node()* {
           <FactTable NetworkIdentifier="http://bizql.io/facts"
            TableName="xbrl:Facts">{
                conversion:facts-to-xml($res.FactTable[], { Caller: "Report"})
        }</FactTable>
    },
    to-csv : function($res as object) as string {
        (conversion:facts-to-csv($res.FactTable[], { Caller: "Report" }), "")[1]
    }
}
let $results := api:serialize($result, $comment, $serializers, $format, "facts")
return if($entities-not-found)
       then api:not-found("entity")
       else api:check-and-return-results($token, $results, $format)

import module namespace config = "http://apps.28.io/config";
import module namespace api = "http://apps.28.io/api";
import module namespace session = "http://apps.28.io/session";

import module namespace conversion = "http://28.io/modules/xbrl/conversion";
import module namespace hypercubes = "http://28.io/modules/xbrl/hypercubes";
import module namespace reports = "http://28.io/modules/xbrl/reports";
import module namespace concepts = "http://28.io/modules/xbrl/concepts";
import module namespace facts = "http://28.io/modules/xbrl/facts";
import module namespace labels = "http://28.io/modules/xbrl/labels";
import module namespace rules = "http://28.io/modules/xbrl/rules";
import module namespace components = "http://28.io/modules/xbrl/components";
import module namespace entities = "http://28.io/modules/xbrl/entities";
import module namespace archives = "http://28.io/modules/xbrl/archives";

import module namespace multiplexer = "http://28.io/modules/xbrl/profiles/multiplexer";

import module namespace request = "http://www.28msec.com/modules/http-request";

declare option rest:response "first-item";

declare variable $local:additional-concepts as object* := (
  {
    Name: "sec:DefaultLegalEntity",
    Labels : [ {
        Role : "http://www.xbrl.org/2003/role/label",
        Language : "en-us",
        Value : "Default Legal Entity [Member]"
    } ]
  },
  {
    Name: "xbrl:NonNumeric",
    Labels : [ {
        Role : "http://www.xbrl.org/2003/role/label",
        Language : "en-us",
        Value : "Non Numeric"
    } ]
  }
);

declare function local:param-values(
    $name as string,
    $entities as object*) as string*
{
    switch(true)
     case $name eq "xbrl:Concept"
        return (request:param-values("concept"), request:param-values("xbrl:Concept"))

    (: sec profile :)
     case $name eq "sec:FiscalYear" and $profile-name eq "sec"
        return ($fiscalYear, request:param-values("sec:FiscalYear"))[$$ ne "LATEST"]
     case $name eq "sec:FiscalYear::type" and $profile-name eq "sec"
        return "integer"
     case $name eq "sec:FiscalPeriod" and $profile-name eq "sec"
        return ($fiscalPeriod, request:param-values("sec:FiscalPeriod"))
     case $name eq "sec:FiscalPeriod::type" and $profile-name eq "sec"
        return "string"
     case $name eq "sec:FiscalPeriodType" and $profile-name eq "sec"
        return ($fiscalPeriodType, request:param-values("sec:FiscalPeriodType"))
     case $name eq "sec:FiscalPeriodType::type" and $profile-name eq "sec"
        return "string"
     case $name eq "dei:LegalEntityAxis" and $profile-name eq "sec"
        return
         if(empty((request:param-values("sec:LegalEntityAxis"), request:param-values("sec:LegalEntityAxis::default"))))
         then "sec:DefaultLegalEntity"
         else request:param-values("sec:LegalEntityAxis")
     case $name eq "dei:LegalEntityAxis::default" return
         if(empty((request:param-values("sec:LegalEntityAxis"), request:param-values("sec:LegalEntityAxis::default"))))
         then "sec:DefaultLegalEntity"
         else request:param-values("sec:LegalEntityAxis::default")

     case $name eq "xbrl:Entity" and $profile-name = ("sec", "japan")
         return (
                if(empty(($cik,$tag,$ticker,$sic)) or exists($entities))
                then entities:eid($entities)
                else "dummy",
                request:param-values("xbrl:Entity")
            )
     case $name eq "xbrl28:Archive" and $profile-name = ("sec", "japan") return (
            let $prefix as string :=
                switch($profile-name)
                case "sec" return "sec"
                case "japan" return "fsa"
                default return (: not reachable :) ()
            let $fiscalYears :=
                let $years := request:param-values( $prefix || ":FiscalYear")
                return if ($years) then $years else $fiscalYear
            let $fiscalPeriods := local:param-values($prefix || ":FiscalPeriod", $entities)
            return
                if($fiscalYears = "LATEST")
                then archives:aid(multiplexer:latest-filings($profile-name, $entities, $fiscalPeriods))
                else (),
            $aid,
            request:param-values("xbrl28:Archive")
        )

     (: japan profile :)
     case $name eq "fsa:FiscalYear" and $profile-name eq "japan"
     return ($fiscalYear, request:param-values("fsa:FiscalYear"))[$$ ne "LATEST"]
     case $name eq "fsa:FiscalYear::type" and $profile-name eq "japan"
     return "integer"
     case $name eq "fsa:FiscalPeriod" and $profile-name eq "japan"
     return ($fiscalPeriod, request:param-values("fsa:FiscalPeriod"))
     case $name eq "fsa:FiscalPeriod::type" and $profile-name eq "japan"
     return "string"
     case $name eq "fsa:FiscalPeriodType" and $profile-name eq "japan"
     return ($fiscalPeriodType, request:param-values("fsa:FiscalPeriodType"))
     case $name eq "fsa:FiscalPeriodType::type" and $profile-name eq "japan"
     return "string"
     case $name eq "fsa:ArchiveFiscalYear::type" and $profile-name eq "japan"
     return "integer"
     case $name eq "fsa:ArchiveFiscalPeriod::type" and $profile-name eq "japan"
     return "string"

     default return request:param-values($name)
};

declare function local:param-names() as string*
{
    let $names := request:param-names()
    return distinct-values((
        (: generic xbrl :)
        $names[contains($$, ":")],
        "xbrl:Entity"[$names = ("eid")],
        "xbrl28:Archive"[$profile-name = ("sec", "japan")],
        "xbrl:Concept"[$names = "concept"],

        (: sec profile :)
        "sec:FiscalPeriod"[$profile-name eq "sec"],
        "sec:FiscalPeriodType"[$profile-name eq "sec"],
        "sec:FiscalYear"[$profile-name eq "sec"],
        "xbrl:Entity"[$profile-name eq "sec" and
                      $names = ("cik", "tag", "ticker", "sic")],
        "dei:LegalEntityAxis"[$profile-name eq "sec"],
        "dei:LegalEntityAxis::default"[$profile-name eq "sec"],

        (: japan profile :)
        "fsa:Submitted"[$profile-name eq "japan"],
        "fsa:FiscalPeriod"[$profile-name eq "japan" and
                           exists($fiscalPeriod)],
        "fsa:FiscalPeriodType"[$profile-name eq "japan" and
                               exists($fiscalPeriodType)],
        "fsa:FiscalYear"[$profile-name eq "japan" and
                         exists($fiscalYear)],
        "xbrl:Entity"[$profile-name eq "japan" and
                      $names = ("edinetcode", "tag", "ticker")]
    ))
};

declare function local:cast-sequence($values as atomic*, $type as string) as atomic*
{
  for $value in $values
  return
      switch ($type)
      case "integer" return $value cast as integer
      case "string" return $value cast as string
      default return error(xs:QName("local:unsupported-type"), $type || ": unsupported type")
};

declare function local:hypercube($entities as object*) as object
{
    let $hypercube-spec :=
    {|
        for $parameter in local:param-names()
        group by $dimension-name :=
            switch(true)
            case ends-with(lower-case($parameter), "::default")
                return substring-before($parameter, "::default")
            case ends-with(lower-case($parameter), ":default")
                return substring-before($parameter, ":default")
            case ends-with(lower-case($parameter), "::type")
                return substring-before($parameter, "::type")
            default
                return $parameter
        let $all as boolean :=
            (local:param-values($dimension-name, $entities) ! upper-case($$)) = "ALL"
        let $type as string? :=
            (local:param-values($dimension-name || "::type", $entities),
             local:param-values($dimension-name || ":type", $entities))[1]

        let $values := local:param-values($dimension-name, $entities)
        let $typed-values := if (exists($type)) then local:cast-sequence($values[$$ ne "ALL"], $type) else $values

        let $has-default := ($parameter = $dimension-name || "::default") or ($parameter = $dimension-name || ":default")
        let $default-value := (local:param-values($dimension-name || "::default", $entities),
                               local:param-values($dimension-name || ":default", $entities))[1]
        let $typed-default-value := if (exists($type)) then local:cast-sequence($default-value, $type) else $default-value

        return
        {
            $dimension-name : {|
                { "Type" : $type }[exists($type)],
                { "Domain" : [ $typed-values ] }
                    [exists($typed-values) and not($all)],
                { "Default" : $typed-default-value }[$has-default],
                { "Default" : null }
                    [$all and (not $has-default) and exists($type)],
                { "Default" : "xbrl28:Domain" }
                    [$all and (not $has-default) and empty($type)]
            |}
        }
    |}
    return hypercubes:user-defined-hypercube($hypercube-spec, { IsOpen: $open })
};

(: Query parameters :)
declare  %rest:case-insensitive                 variable $token             as string? external;
declare  %rest:case-insensitive                 variable $profile-name      as string  external := $config:profile-name;
declare  %rest:env                              variable $request-uri       as string  external;
declare  %rest:case-insensitive                 variable $format            as string? external;
declare  %rest:case-insensitive %rest:distinct  variable $cik               as string* external;
declare  %rest:case-insensitive %rest:distinct  variable $edinetcode        as string* external;
declare  %rest:case-insensitive %rest:distinct  variable $tag               as string* external;
declare  %rest:case-insensitive %rest:distinct  variable $ticker            as string* external;
declare  %rest:case-insensitive %rest:distinct  variable $sic               as string* external;
declare  %rest:case-insensitive %rest:distinct  variable $fiscalYear        as string* external := ();
declare  %rest:case-insensitive %rest:distinct  variable $fiscalPeriod      as string* external := ();
declare  %rest:case-insensitive %rest:distinct  variable $fiscalPeriodType  as string* external := ();
declare  %rest:case-insensitive %rest:distinct  variable $eid               as string* external;
declare  %rest:case-insensitive %rest:distinct  variable $aid               as string* external;
declare  %rest:case-insensitive                 variable $map               as string? external;
declare  %rest:case-insensitive                 variable $rule              as string? external;
declare  %rest:case-insensitive                 variable $report            as string? external;
declare  %rest:case-insensitive                 variable $validate          as boolean external := false;
declare  %rest:case-insensitive                 variable $open              as boolean external := false;
declare  %rest:case-insensitive                 variable $labels            as boolean external := true;
declare  %rest:case-insensitive                 variable $additional-rules  as string? external;
declare  %rest:case-insensitive                 variable $debug             as boolean external := false;
declare  %rest:case-insensitive                 variable $language          as string  external := "en-US";
declare  %rest:case-insensitive                 variable $count             as boolean external := false;

(: Post-processing :)
let $format as string? := api:preprocess-format($format, $request-uri)
let $tag as string* := api:preprocess-tags($tag)

let $cik as string* :=
    switch($profile-name)
    case "sec" return $cik
    case "japan" return $edinetcode
    default return ()

(: Entity resolution :)
let $entities := multiplexer:entities(
  $profile-name,
  $eid,
  $cik,
  api:preprocess-tags($tag),
  $ticker,
  $sic,
  (),
  (),
  $aid,
  ())

let $entities-not-found as boolean :=
  exists(($eid, $cik, $tag, $ticker, $sic)) and empty($entities)

let $report as object? := reports:reports($report)
let $map as item* :=
    if(exists($report))
    then reports:concept-map($report)
    else $map
let $rule as item* :=
    (
        if(exists($report))
        then reports:rules($report)
        else rules:rules($rule),
        if(exists($additional-rules))
        then rules:rules($additional-rules)
        else ()
    )

let $hypercube := local:hypercube($entities)

let $facts :=
    let $options := {|
      {
        Hypercube : $hypercube,
        Validate: $validate
      },
      { "ConceptMaps" : $map }[exists($map)],
      { "Rules" : [ $rule ] }[exists($rule)],
      { "Concepts" : $report.Concepts }[exists($report) and exists($report.Concepts)],
      { AggregateAspectMax: "sec:Accepted" }[$profile-name eq "sec"]
    |}
    return facts:facts-for($options)

let $facts :=
  if(not $labels)
  then $facts
  else
    let $archives as string* := distinct-values($facts.Aspects."xbrl28:Archive")
    let $concept-names as string* := (
      distinct-values((keys($facts.Aspects), values($facts.Aspects)[$$ instance of string]))
    )
    let $concepts as object* :=
      (
          concepts:concepts($concept-names, $archives, $concepts:ANY_COMPONENT_LINK_ROLE),
          $report.Concepts[][$$.Name = $concept-names],
          if($profile-name eq "sec") then $local:additional-concepts else ()
      )
    let $language as string := ( $language, $report.$components:DEFAULT-LANGUAGE , $labels:AMERICAN_ENGLISH )[1]
    let $nonFetchedEntities as string* := request:param-values("xbrl:Entity")[not $$ = entities:eid($entities)]
    let $entities as object* := ($entities, entities:entities($nonFetchedEntities))
    for $fact as object in $facts
    return
    {|
      $fact,
      let $concept-labels as object? := labels:labels-for-facts(
        $fact,
        $labels:STANDARD_LABEL_ROLE,
        $language,
        $concepts,
        $entities,
        ()
      )
      return { Labels : $concept-labels }
    |}

let $facts := api:normalize-facts($facts)

let $result := {
                    NetworkIdentifier : "http://bizql.io/facts",
                    TableName : "xbrl:Facts",
                    FactTable : [ if($count) then () else $facts ]
               }
let $comment :=
{
    NumFacts: count($facts),
    TotalNumFacts: session:num-facts(),
    TotalNumArchives: session:num-archives(),
    TotalNumEntities: session:num-entities()
}
let $comment :=
    if($debug)
    then {|
        $comment,
        { DebugInfo: { Hypercube: $hypercube } }
    |}
    else $comment
let $serializers := {
    to-xml : function($res as object) as node()* {
           <FactTable NetworkIdentifier="http://bizql.io/facts"
            TableName="xbrl:Facts">{
                conversion:facts-to-xml($res.FactTable[], { Caller: "Report"})
        }</FactTable>
    },
    to-csv : function($res as object) as string {
        (conversion:facts-to-csv($res.FactTable[], { Caller: "Report" }), "")[1]
    }
}
let $results := api:serialize($result, $comment, $serializers, $format, "facts")
return if($entities-not-found)
       then api:not-found("entity")
       else api:check-and-return-results($token, $results, $format)


:bug: Some more fixes.

:bug: Some more fixes.

jsoniq version "1.0";
module namespace ew = "http://28.io/modules/xbrl/elasticsearch-wrapper";

import module namespace config = "http://apps.28.io/config";
import module namespace es = "http://28.io/modules/elasticsearch";

declare %private function ew:connection() as object?
{
  try {
    es:connection($config:search-db.name)
  } catch es:* {
    error(QName("ew:CONNECTION-FAILED"), $err:description)
  }
};

declare function ew:search(
    $collection as string,
    $full-text as string,
    $full-text-fields as string*,
    $full-text-offset as integer?,
    $full-text-limit as integer?
) as object*
{
    let $conn := ew:connection()
    return es:search(
        $conn,
        $config:query-db.db,
        $collection,
        {
          "from" : ($full-text-offset, 0)[1],
          "size" : ($full-text-limit, 10)[1],
          "query" : {
            "multi_match" : {
              "query": $full-text,
              "type": "most_fields",
              "fields": [
                  for $f in $full-text-fields
                  return tokenize($f, ".")[position() eq last()]
              ]
            }
          }
        }
      ).hits.hits[]._source
};

jsoniq version "1.0";
module namespace ew = "http://28.io/modules/xbrl/elasticsearch-wrapper";

import module namespace config = "http://apps.28.io/config";
import module namespace es = "http://28.io/modules/elasticsearch";

declare %private function ew:connection() as object?
{
  try {
    es:connection($config:search-db.name)
  } catch es:* {
    error(QName("ew:CONNECTION-FAILED"), $err:description)
  }
};

declare function ew:search(
    $collection as string,
    $full-text as string,
    $full-text-fields as string*,
    $full-text-offset as integer?,
    $full-text-limit as integer?
) as object*
{
    let $conn := ew:connection()
    return es:search(
        $conn,
        $config:search-db.db,
        $collection,
        {
          "from" : ($full-text-offset, 0)[1],
          "size" : ($full-text-limit, 10)[1],
          "query" : {
            "multi_match" : {
              "query": $full-text,
              "type": "most_fields",
              "fields": [
                  for $f in $full-text-fields
                  return tokenize($f, ".")[position() eq last()]
              ]
            }
          }
        }
      ).hits.hits[]._source
};