(ns generating-sparql.spin-jsonld
  (:require [generating-sparql.data :as data]
            [cheshire.core :as json]
            [clojure.java.io :as io])
  (:import [org.apache.jena.rdf.model ModelFactory]
           [org.apache.jena.riot Lang RDFDataMgr]
           [org.apache.jena.query DatasetFactory]
           [org.topbraid.spin.model SPINFactory]
           [org.topbraid.spin.arq ARQFactory]))

(defn clj->jsonld
  [data]
  (json/generate-string data {:pretty true}))

(defn jsonld->model
  [jsonld]
  (let [dataset (DatasetFactory/create)]
    (RDFDataMgr/read dataset (io/input-stream (.getBytes jsonld)) Lang/JSONLD)
    (.getDefaultModel dataset)))

(defn query
  [properties limit offset]
  (let [context {"@vocab" "http://spinrdf.org/sp#"
                 "foaf" "http://xmlns.com/foaf/0.1/"
                 "rdf" "http://www.w3.org/1999/02/22-rdf-syntax-ns#"
                 "rdfs" "http://www.w3.org/2000/01/rdf-schema#"
                 "elements" {"@container" "@list"}
                 "groupBy" {"@container" "@list"}
                 "orderBy" {"@container" "@list"}
                 "predicate" {"@type" "@id"}
                 "resultVariables" {"@container" "@list"}
                 "subject" {"@type" "@id"}
                 "where" {"@container" "@list"}}
        person [{"@id" "_:person"}]
        subquery-1 {"@type" "SubQuery"
                    "query" {"@type" "Select"
                             "resultVariables" person
                             "distinct" true
                             "where" (into [{"subject" "_:person"
                                             "predicate" "rdf:type"
                                             "object" {"@id" "foaf:Person"}}]
                                           (mapv (fn [{:keys [property]}]
                                                   {"subject" "_:person"
                                                    "predicate" property
                                                    "object" {}})
                                                 (filter :required? properties)))
                             "orderBy" person}}
        subquery-2 {"@type" "SubQuery"
                    "query" {"@type" "Select"
                             "resultVariables" person
                             "where" [subquery-1]
                             "limit" limit
                             "offset" offset}}
        query' {"@context" context 
                "@graph" [{"@id" (data/ex "query")
                           "@type" "Select"
                           "rdfs:comment" {"@value" "Get persons from DBpedia"
                                           "@language" "en"}
                           "resultVariables" (into person
                                                   (mapv (fn [{:keys [varname]}]
                                                           {"@type" "Sample"
                                                            "expression" {"varName" (str "_" varname)}
                                                            "as" {"varName" varname}})
                                                         properties))
                           "where" (into [subquery-2]
                                         (mapv (fn [{:keys [property varname required?]}]
                                                 (let [triple-pattern {"subject" "_:person"
                                                                       "predicate" property
                                                                       "object" {"varName" (str "_" varname)}}]
                                                   (if required?
                                                     triple-pattern
                                                     {"@type" "Optional"
                                                      "elements" [triple-pattern]})))
                                               properties))
                           "groupBy" person}
                          {"@id" "_:person"
                           "varName" "person"}]}
        model (-> query'
                  clj->jsonld
                  jsonld->model)
        query-resource (.getResource model (data/ex "query"))]
    (str (.createQuery (ARQFactory.) (SPINFactory/asQuery query-resource)))))

#_(println (query data/properties data/limit data/offset))