   (:require-macros [cljs.core.async.macros :refer [go go-loop]])
   (:require [db-quiz.config :refer [config]]
             [db-quiz.state :refer [app-state]]
+            [db-quiz.modals :as modals]
+            [db-quiz.util :refer [number-of-fields]]
             [cljs-http.client :as http]
-            [reagent.core :as reagent :refer [atom]]
-            [cljs.core.async :refer [<! >! chan sliding-buffer]]
+            [cljs.core.async :refer [<! >! chan pipe]]
             [clojure.string :refer [join lower-case replace split trim]]
-            [cljsjs.mustache :as mustache]))
+            [cljsjs.mustache :as mustache]
+            [reagent-modals.modals :as reagent-modals]))
 
 ; ----- Private vars -----
 
-(def autocomplete-template
-  "PREFIX rdfs:    <http://www.w3.org/2000/01/rdf-schema#>
-
-  SELECT DISTINCT ?s (STR(?_label) AS ?label)
+(def load-labels-template
+  "PREFIX dbo:     <http://dbpedia.org/ontology/>
+  PREFIX rdfs:     <http://www.w3.org/2000/01/rdf-schema#>
+
+  SELECT ?label
+  WHERE {
+    {
+      SELECT (STR(?_label) AS ?label)
+      WHERE {
+        GRAPH <http://cs.dbpedia.org> {
+          VALUES ?class {
+            {{#classes}}
+            <{{{.}}}>
+            {{/classes}}
+          }
+          [] a ?class ;
+            rdfs:label ?_label ;
+            dbo:abstract ?_description .
+          FILTER (!REGEX(?_label, '^(\\\\p{Lu}\\\\.?)+$')
+                  &&
+                  !REGEX(?_label, '^.*\\\\d+.*$') 
+                  &&
+                  (STRLEN(?_description) > 140)
+                  &&
+                  langMatches(lang(?_label), 'cs')
+                  &&
+                  langMatches(lang(?_description), 'cs'))
+        }
+      }
+      ORDER BY ?label
+    }
+  }
+  LIMIT {{limit}}
+  OFFSET {{offset}}")
+
+(def load-labels-group-template
+  "PREFIX dbo:     <http://dbpedia.org/ontology/>
+  PREFIX rdfs:     <http://www.w3.org/2000/01/rdf-schema#>
+
+  SELECT (GROUP_CONCAT(STR(?_label); separator = '|') AS ?labels)
   WHERE {
     GRAPH <http://cs.dbpedia.org> {
-      VALUES ?fragment {
-        '{{fragment}}'
-      }
       VALUES ?class {
         {{#classes}}
         <{{{.}}}>
         {{/classes}}
       }
-      ?s a ?class ;
-        rdfs:label ?_label .
-      FILTER (lang(?_label) = '' || langMatches(lang(?_label), 'cs'))
-      BIND (LCASE(?_label) AS ?lcaseLabel)
-      FILTER CONTAINS(?lcaseLabel, ?fragment)
-      BIND (IF(STRSTARTS(?lcaseLabel, ?fragment), 1, 0) AS ?order)
+      [] a ?class ;
+        rdfs:label ?_label ;
+        dbo:abstract ?_description .
+      FILTER (!REGEX(?_label, '^(\\\\p{Lu}\\\\.?)+$')
+              &&
+              !REGEX(?_label, '^.*\\\\d+.*$') 
+              &&
+              (STRLEN(?_description) > 140)
+              &&
+              langMatches(lang(?_label), 'cs')
+              &&
+              langMatches(lang(?_description), 'cs'))
     }
-  }
-  ORDER BY DESC(?order)
-  LIMIT 10")
+  }")
 
 ; ----- Public functions -----
 
-(defn sparql-results-channel
+(def sparql-results-channel
   "Channel that extracts values from application/sparql-results+json format."
-  []
   (letfn [(extract-values [result] (into {} (map (fn [[k v]] [k (:value v)]) result)))]
-    (chan 1 (map (comp (partial map extract-values) :bindings :results :body)))))
+    (fn []
+      (chan 1 (map (comp (partial map extract-values) :bindings :results :body))))))
 
 (defn sparql-query-channel
   "Virtuoso-specific JSON-P request"
-  [query]
-  (http/jsonp (get-in config [:data :sparql :endpoint])
+  [endpoint query]
+  (http/jsonp endpoint
               {:query-params {; Since JSON-P does not allow setting Accept header,
                               ; we need to use Virtuoso-specific query parameter `format`.
                               :format "application/sparql-results+json"
-                              :query query}}))
+                              :query query
+                              :timeout 60000}}))
 
 (defn render-template
   "Render Mustache template with data."

    (go
      (swap! app-state #(assoc % :loading? true))
      (let [query (render-template (<! query-channel) :data data)]
        ;(.log js/console query)
        (>! sparql-results (<! (sparql-query-channel query))))
      (swap! app-state #(assoc % :loading? false)))
    sparql-results))

(defn sparql-autocomplete
  "Autocomplete from SPARQL endpoint.
  request: jQuery UI autocomplete's request object
  response: channel to put the response to"
  [request response]
  (let [fragment (lower-case (.-term request))
        sparql-results (sparql-results-channel)
        query (render-template autocomplete-template
                               :data {:classes (get-in config [:data :sparql :classes :default])
                                      :fragment fragment})
        sparql-query (sparql-query-channel query)]
    (go (let [results (map :label (<! (wrap-load sparql-query sparql-results)))]
          (response (clj->js results))))))

(defn wikipedia-autocomplete
  "Autocomplete from Wikipedia API.
  request: jQuery UI autocomplete's request object
  response: channel to put the response to"
  [request response]
  (let [fragment (lower-case (.-term request))
        query (http/jsonp "http://cs.wikipedia.org/w/api.php"
                          {:query-params {:action "opensearch"
                                          :format "json"
                                          :search fragment}})]
    (go (-> query <! :body second clj->js response))))

(defn load-gdocs-items
  "Load items from Google Spreadsheet's worksheet."
  [spreadsheet-id worksheet-id]
  (let [url (str "https://spreadsheets.google.com/feeds/list/"
                 spreadsheet-id "/" worksheet-id "/public/full")
        results (chan 1 (map (comp :entry :feed :body)))]
    (wrap-load (http/jsonp url {:query-params {:alt "json-in-script"}})
               results)))

                  (split description #"\.\s+"))
          :else description)))

(def clear-label
  (comp trim delete-parenthesized-parts))

(defn clear-tokens
  "Filter out roman numerals"
  [tokens]
  (filter (fn [token]
            (not (re-matches #"^M{0,4}(CM|CD|D?C{0,3})(XC|XL|L?X{0,3})(IX|IV|V?I{0,3})\.?$" token)))
          tokens))
