 
 (defn create-attributes [simple-attributes & {:keys [names]}]
   (let [attribute-names (or names (repeatedly simple-attributes #(random-word 5)))
-        generators (cycle attribute-generators)
+        generators (cycle *attribute-generators*)
         attributes (map #(vector %1 %2) attribute-names generators)]
     attributes))
 
+(defn generate-smile-stream
+  "Returns a SMILE-encoded writer for the given Clojure object.
+  Takes an optional date format string that Date objects will be encoded with.
+
+  The default date format (in UTC) is: yyyy-MM-dd'T'HH:mm:ss'Z'"
+  ([obj ^OutputStream os]
+   (generate-smile-stream obj nil os))
+  ([obj opt-map ^OutputStream os]
+   (let [generator (.createGenerator ^SmileFactory
+                                     cheshire.factory/smile-factory
+                                     ^OutputStream os)]
+     (cheshire.generate/generate generator obj (or (:date-format opt-map)
+                                     cheshire.factory/default-date-format)
+                   (:ex opt-map)
+                   (:key-fn opt-map))
+     (.flush generator)
+     os)))
+
+(defn generate-smile [obj]
+  (let [baos (ByteArrayOutputStream.)]
+    (.toByteArray (generate-smile-stream obj baos))))
+
+
+(defn texify-feature [feature]
+  (walk/postwalk
+    (fn [e]
+      (cond (instance? LocalDateTime e)
+            (texify-moment e)
+            (instance? LocalDate e)
+            (texify-date e)
+            (and (instance? MapEntry e) (= :_validity (first e)))
+            (MapEntry. :_validity (texify-validity (second e)))
+            :else e))
+    feature))
+
+(add-encoder LocalDate
+             (fn [c ^SmileGenerator smile-generator]
+               (doto smile-generator
+                 (.writeStartArray 2)
+                 (.writeString "D")
+                 (.writeNumber (to-long c))
+                 (.writeEndArray))))
+(add-encoder LocalDateTime
+             (fn [c ^SmileGenerator smile-generator]
+               (doto smile-generator
+                 (.writeStartArray 2)
+                 (.writeString "M")
+                 (.writeNumber (to-long c))
+                 (.writeEndArray))))
+
 (defn random-json-features [out-stream dataset collection total & args]
-  (let [{:keys [change? close? delete? start-with-delete? nested geometry? ids]
+  (let [{:keys [change? close? delete? start-with-delete? nested geometry? ids smile?]
          :or {change? false close? false delete? false
               start-with-delete? false nested 0 geometry? true
               ids (repeatedly #(random-word 10))}} args

        new-features (map #(random-new-feature collection attributes %) ids)
        with-nested (map #(combine-to-nested-feature % "nested" geometry?) (partition (+ 1 nested) new-features))
        with-extra (mapcat #(followed-by % change? close? delete? start-with-delete?) with-nested)
        package {:_meta {}
                 :dataset dataset
                 :features (take total with-extra)}]
    (json/generate-stream package out-stream)))

(defn- random-json-feature-stream* [out-stream dataset collection total & args]
  (with-open [writer (clojure.java.io/writer out-stream)]
    (apply random-json-features writer dataset collection total args)))

(defn random-json-feature-stream
  (^java.io.InputStream [dataset collection total & args]
   (let [pipe-in (PipedInputStream.)
         pipe-out (PipedOutputStream. pipe-in)]
     (future (apply random-json-feature-stream* pipe-out dataset collection total args))
     pipe-in)))

(defn generate-test-files []
  (doseq [c [10 100 1000 10000 100000]]
    (with-open [w (clojure.java.io/writer (str ".test-files/new-features-single-collection-" c ".json"))]
      (random-json-features w "newset" "collection1" c))))

(defn generate-test-files-with-changes []
  (doseq [c [10 100 1000 10000 100000]]
    (with-open [w (clojure.java.io/writer (str ".test-files/change-features-single-collection-" c ".json"))]
      (random-json-features w "changeset" "collection1" c :change? true))))

(defn generate-test-files-with-closes []
  (doseq [c [10 100 1000 10000 100000]]
    (with-open [w (clojure.java.io/writer (str ".test-files/close-features-single-collection-" c ".json"))]
      (random-json-features w "closeset" "collection1" c :close? true))))

(defn generate-test-files-with-deletes []
  (doseq [c [2 10 100 1000 10000 100000]]
    (with-open [w (clojure.java.io/writer (str ".test-files/delete-features-single-collection-" c ".json"))]
      (random-json-features w "deleteset" "collection1" c :delete? true))))

(defn generate-test-files-with-nested-feature []
  (doseq [c [5 50 500 5000 50000]]
    (with-open [w (clojure.java.io/writer (str ".test-files/new-features-nested-feature-" c ".json"))]
      (random-json-features w "nestedset" "collection1" c :nested 1))))

(defn generate-test-files-with-nested-features []
  (doseq [c [3 33 333 3333 33333]]
    (with-open [w (clojure.java.io/writer (str ".test-files/new-features-nested-features-" c ".json"))]
      (random-json-features w "nestedset" "collection1" c :nested 2))))

(defn generate-test-files-with-nested-feature-no-top-geometry []
  (doseq [c [10 100 1000 10000 100000]]
    (with-open [w (clojure.java.io/writer (str ".test-files/new-features-nested-feature-no-top-geometry-" c ".json"))]
      (random-json-features w "nestedset" "collection1" c :nested 1 :geometry? false))))

(defn generate-test-files-with-nested-features-no-top-geometry []
  (doseq [c [5 50 500 5000 50000]]
    (with-open [w (clojure.java.io/writer (str ".test-files/new-features-nested-features-no-top-geometry-" c ".json"))]
      (random-json-features w "nestedset" "collection1" c :nested 2 :geometry? false))))

(ns pdok.featured.generator
  (:require [cheshire
             [core :as json]
             [generate :refer [add-encoder]]]
            [clj-time
             [core :as t]
             [format :as tf]
             [local :refer [local-now]]
             [coerce :refer [to-long to-date-time to-local-date to-local-date-time]]]
            [clojure.walk :as walk])
  (:import [java.io PipedInputStream PipedOutputStream OutputStream InputStream ByteArrayOutputStream]
           (com.fasterxml.jackson.dataformat.smile SmileFactory SmileGenerator)
           (org.joda.time LocalDateTime LocalDate)
           (clojure.lang MapEntry)))

(def ^:dynamic *difficult-geometry?* false)

