 #_(read-tag-info (java.io.StringReader. "{ nums }}"))
 ;{:tag-value "nums", :tag-type :value}
 
-(defn tag-content [close-tag rdr]
+(defn tag-content [end-tag rdr]
   (let [content (transient [])
         buf (StringBuilder.)]
     (loop [ch (read-char rdr)]
-      (if (= \{ ch)
-        (let [{:keys [tag-name tag-type] :as tag} (read-tag-info rdr)]          
-          (when (not= close-tag tag-name)            
+      (if (= tag-open ch)
+        (let [{:keys [tag-name tag-type] :as tag} (read-tag-info rdr)]
+          (when (not= end-tag tag-name)
             (conj! content (.toString buf))
             (.setLength buf 0)
-            (conj! content (if (= :value tag-type)
-                             (value-tag tag)
+            (conj! content (if (= :filter tag-type)
+                             (filter-tag tag)
                              (expr-tag tag rdr)))
             (recur (read-char rdr))))
         (do

(ns selmer.test-parser)

(declare parse expr-tag)

(defn render [template args]  
  (let [buf (StringBuilder.)]
    (doseq [element template]       
      (.append buf (if (string? element) element (element args))))
    (.toString buf)))

(defn read-char [rdr]
  (let [ch (.read rdr)]
    (if-not (neg? ch) (char ch))))

(defn peek-stream [rdr size]
  (.mark rdr size)
  (let [result (loop [items []]
                 (let [ch (read-char rdr)]
                   (if (and ch (< (count items) size))
                     (recur (conj items ch))
                     items)))]
    (.reset rdr)
    result))

(defn value-tag [{:keys [tag-value]}]  
  (let [value (map keyword (.split tag-value "\\."))]
    (fn [args] (get-in args value))))

#_((value-tag {:tag-value "foo.bar.baz"}) {:foo {:bar {:baz "ok"}}})
;ok
#_((value-tag {:tag-value "foo"}) {:foo "ok"})
;ok

(defn read-tag-info [rdr]
  (let [buf (StringBuilder.)
        tag-type (if (= \{(read-char rdr)) :value :expr)]
    (loop [ch1 (read-char rdr)
           ch2 (read-char rdr)]            
      (when-not (and (or  (= \% ch1) (= \} ch1)) (= \} ch2))
        (.append buf ch1)        
        (recur ch2 (read-char rdr))))    
    (let [content (->>  (.split (.toString buf ) " ") (remove empty?) (map (memfn trim)))]
      (merge {:tag-type tag-type}
             (if (= :value tag-type)
               {:tag-value (first content)}
               {:tag-name (first content)
                :args (rest content)})))))

#_(read-tag-info (java.io.StringReader. "% for i in nums %}"))

#_(render (tag-content "endfor" (java.io.StringReader. "foo {{name.first}} bar {% endfor %}")) {:name {:first "Bob"}})
;"foo Bob bar "

(defn handle-tag [rdr]
  (let [tag (read-tag-info rdr)]
    (if (= :filter (:tag-type tag))
      (filter-tag tag)
      (expr-tag tag rdr))))

(defn parse [file]
  (with-open [rdr (clojure.java.io/reader file)]
      (let [template (transient [])
            buf      (StringBuilder.)]
        (loop [ch (read-char rdr)]
          (when ch
            (if (= tag-open ch)
              (do
                ;we hit a tag so we append the buffer content to the template
                ; and empty the buffer, then we proceed to parse the tag                  
                (conj! template (.toString buf))
                (.setLength buf 0)
                (conj! template (handle-tag rdr))
                (recur (read-char rdr)))
              (do
                ;default case, here we simply append the character and
                ;go to read the next one
                (.append buf ch)
                (recur (read-char rdr))))))
        ;add the leftover content of the buffer and return the template
        (conj! template (.toString buf))
        (persistent! template))))

#_(spit "out.html" (render (parse "home.html") {:name "Bob" :users [[{:name "test" }] [{:name "test1" }]]}))
#_(println (render (parse "home.html") {:name "Bob" :users [[{:name "test" }] [{:name "test1" }]]}))

