      (merge event-chans)))
   ([fbref close-chan event]
    (let [event-chan (chan)
-         close-chan (chan)
          handle-event (fn [snapshot]
                         (put! event-chan
                               [event
                                (keyword (.key snapshot))
                                (js->clj (.val snapshot))]))]
      (.on fbref (clojure.core/name event) handle-event)
-     (go
-       (let [msg (<! close-chan)]
+     (go (<! close-chan)
          (.off fbref (clojure.core/name event)
-               handle-event)))
+               handle-event))
      event-chan)))
 
-(defn- id->fb-chan [id]
-  (fb->chan (id->fbref id)))
+(defn- id->fb-chan
+  ([id] (id->fb-chan id (chan)))
+  ([id close-chan]
+   (fb->chan (id->fbref id) close-chan)))
 
 (defn id->atom
   ([id] (id->atom (atom {}) id))

(ns hacker-agent.hacker-base
  (:require-macros [cljs.core.async.macros :refer [go go-loop]])
  (:require [reagent.core :as reagent :refer [atom]]
            [cljs.core.async :as async :refer [put! chan <! >! close! merge]]
            [pani.cljs.core :as p]))

(defonce url "https://hacker-news.firebaseio.com/v0")

(defonce root (js/Firebase. url))


(defn- walk-root [r keys]
  (let [[k & ks] keys]


(defn init-item-sync!
  "Given vector PATH and atom DATA, create callbacks on channel
  created from item ID to update PATH in DATA.  Returns a channel that
  accepts any input to remove the callbacks created."
  ([data id] (init-item-sync! data id [:entry]))
  ([data id path]
   (let [close-chan (chan)]
     (init-item-sync! data id (conj path :item) (mult close-chan))
     close-chan))
  ([data id path close-mult]
   ;; mult to multiplex reading the close channel
   ;; tap to create a channel from the mult
   (let [fbc (id->fb-chan id (tap close-mult (chan)))
         close-loop-tap (tap close-mult (chan))]
     (letfn [(handle-kid [k v child-path]
               (let [items (doall (map #(hash-map % {}) v))]
                 (swap! data assoc-in child-path (into {} items))
                 (doseq [id v]
                   (init-item-sync! data id (conj child-path id) close-mult))))]
       ;; spawn a go loop that will update the data
       (go-loop []
         (when-let [msg (<! fbc)]
           (let [[event key val] msg
                 child-path (conj path key)]
             (case event
               :child_added (if (= key :kids)
                              (handle-kid key val child-path)
                              (swap! data assoc-in child-path val))
               :child_changed (if (= key :kids)
                                (handle-kid key val child-path)
                                (swap! data assoc-in child-path val))
               :child_removed (swap! data update-in path dissoc key)
               (.log js/console (clj->js [event key val]))))
           (recur)))
       ;; The go loop still processes messages even after using .off
       ;; on the source. As a workaround for now, create another tap
       ;; on the close-mult and manually close the channel here.
       (go (<! close-loop-tap)
           (close! fbc))
       close-mult))))

(defn reset-item-sync! [id data path]
  (let [identifier (cons data path)]
    (when-let [close-chan (get-in @channels identifier)]
      (put! close-chan :close))
    (swap! data update-in path dissoc :item)
    (swap! channels assoc-in identifier
           (init-item-sync! data id path))))
