(:~
 : XIS - XML Information Service
 :)
module namespace xis = 'http://basex.org/xis';

declare variable $xis:REST-API-DESC as xs:string+ := file:read-text("doc/rest-api/xis-rest-api.yaml");

(: ------ REST/HTTP responses --- :)
declare %private function xis:response-200(
    $src as xs:string,
    $dbname as xs:string
  ) as item()+
{
  xis:response-200($src, $dbname, '')
};

declare %private function xis:response-200(
    $src as xs:string,
    $dbname as xs:string,
    $docname as xs:string
  ) as item()+
{
  let $msg :=
    switch($src)
    case 'db-create' return 'Datastore (' || $dbname || ') created.'
    case 'db-drop'   return 'Datastore (' || $dbname || ') deleted.'
    case 'db-add'    return 'Document (' || $docname || ') added to datastore (' || $dbname || ').'
    case 'db-delete' return 'Document (' || $docname || ') deleted from datastore (' || $dbname || ').'
    default          return $dbname || ' ' || $docname
  return
    xis:response-text(200, $msg)
};

(: Constructs REST response containing 404 HTTP response and a text message. :)
declare %private function xis:response-404(
    $dbname as xs:string
  ) as item()+
{
  xis:response-text(404, 'Datastore (' || $dbname || ') not found.')
};

declare %private function xis:response-404(
    $dbname as xs:string,
    $docname as xs:string
  ) as item()+
{
  xis:response-text(404, 'Document (' || $docname || ') not found in datastore (' || $dbname || ').')
};

declare %private function xis:response-409(
    $dbname as xs:string
  ) as item()+
{
  xis:response-text(409, 'Datastore (' || $dbname || ') already exists.')
};

declare %private function xis:response-409(
    $dbname as xs:string,
    $docname as xs:string
  ) as xs:string
{
  xis:response-text(409, 'Document (' || $docname || ') already exists in datastore (' || $dbname || ').')
};

declare %private function xis:response-text(
    $status as xs:integer,
    $msg as xs:string
  ) as item()+
{
  (
    <rest:response>
      <http:response status="{ $status }">
        <http:header name="Content-Language" value="en"/>
        <http:header name="Content-Type" value="text/plain; charset=utf-8"/>
      </http:response>
      <output:serialization-parameters>
        <output:media-type value='text/plain'/>
      </output:serialization-parameters>
    </rest:response>,
    $msg
  )
};

(: -------------------- Basic information services -------------------------- :)

(:~
 : This function generates the welcome page.
 : @return HTML page
 :)
declare
  %rest:GET
  %rest:path("/")
function xis:landing-page()
  as element(Q{http://www.w3.org/1999/xhtml}html)
{
  let $title := 'XIS – XML Information Service'
  return
    <html xmlns="http://www.w3.org/1999/xhtml">
      <head>
        <title>{ $title }</title>
        <link rel="stylesheet" type="text/css" href="static/style.css"/>
      </head>
      <body>
        <div class="right"><img src="static/basex.svg" width="96"/></div>
        <h2>{ $title }</h2>
        
        <ul>
          <li><a href="api/v1">XIS REST API</a></li>
          <li><a href="http://phobos103.inf.uni-konstanz.de/xml15/tutorial/xis/test/curl/curl.txt">cURL log</a></li>
        </ul>
      </body>
    </html>
};

declare
  %rest:GET
  %rest:path("/api/v1")
  %output:media-type("text/plain")
function xis:rest-api-description()
  as xs:string+
{
  $xis:REST-API-DESC
};

(: ------------------------- Database services ------------------------------ :)
declare
  %rest:GET
  %rest:path("/api/v1/db")
  %output:media-type("text/plain")  
function xis:create-db-info()
  as xs:string+
{
  $xis:REST-API-DESC
};

declare
  %updating
  %rest:POST
  %rest:path("/api/v1/db/{$dbname}")
function xis:db-post(
    $dbname as xs:string
  )
{
  if (db:exists($dbname))
  then
    (: Return 409 (CONFLICT) and text message. :)
    db:output(xis:response-409($dbname))
  else
    (
      db:create($dbname),
      db:output(xis:response-200('db-create', $dbname))
    )
};

declare
  %rest:GET
  %rest:path("/api/v1/db/{$dbname}")
function xis:db-get(
    $dbname as xs:string
  )
{ 
  if (db:exists($dbname))
  then
    element datastore {
      attribute name { $dbname },
      db:list($dbname) ! element document { attribute name { . } }
    }
  else
    xis:response-404($dbname)
};

declare
  %updating
  %rest:DELETE
  %rest:path("/api/v1/db/{$dbname}")
function xis:db-delete(
    $dbname as xs:string
  )
{
  if (db:exists($dbname))
  then (
    db:drop($dbname),
    db:output(xis:response-200('db-drop', $dbname))
  ) 
  else
    db:output(xis:response-404($dbname))
};

(: ----------------- Database / Document services ------------------------------ :)
declare
  %updating
  %rest:POST('{$content}')
  %rest:path("/api/v1/db/{$dbname}/{$docname}")
function xis:db-doc-post(
    $dbname as xs:string,
    $docname as xs:string,
    $content as document-node()
  )
{
  if (db:exists($dbname))
  then
    if (db:exists($dbname, $docname))
    then
      db:output(xis:response-409($dbname, $docname))
    else
      (
        db:add($dbname, $content, $docname),
        db:output(xis:response-200('db-add', $dbname, $docname))
      )
  else
    db:output(xis:response-404($dbname))
};

declare
  %updating
  %rest:DELETE
  %rest:path("/api/v1/db/{$dbname}/{$docname}")
function xis:db-doc-delete(
    $dbname as xs:string,
    $docname as xs:string
  )
{
  if (db:exists($dbname))
  then
    if (db:exists($dbname, $docname))
    then
      (
        db:delete($dbname, $docname),
        db:output(xis:response-200('db-delete', $dbname, $docname))
      )
    else
        db:output(xis:response-404($docname, $dbname))
  else
    db:output(xis:response-404($dbname))
};

declare
  %rest:GET
  %rest:path("/api/v1/db/{$dbname}/{$docname}")
function xis:db-doc-get(
    $dbname as xs:string,
    $docname as xs:string
  )
{
  if (db:exists($dbname))
  then
    if (db:exists($dbname, $docname))
    then
      doc(concat($dbname, '/', $docname))
    else
      xis:response-404($docname, $dbname)
  else
    xis:response-404($dbname)
};
