 (defn bootstrapped? [db]
   (dsu/qe-by db :dato.meta/bootrapped? true))
 
-(defn new-dato [dato-host dato-path app-db cs-schema]
+(defmulti ws-message-handler (fn [dato-ch msg] (:op msg)))
+(defmethod ws-message-handler :default
+  [dato-ch msg]
+  ;; TODO replace the dato-ch handler with this
+  (put! dato-ch msg))
+
+(defn new-dato [dato-host dato-port dato-path app-db cs-schema]
   (let [cast-bootstrapped? (atom false)
         ws-ch              (async/chan)
         dato-ch            (async/chan)
+        ;; XXX: can we get rid of this?
         ss-cast!           #(put! ws-ch %)
+        ;; XXX: can we get rid of this?
         pending-rpc        (atom {})
-        ws                 (ws/build dato-host dato-path {:on-open    (fn [event] (js/console.log ":on-open :" event))
-                                                          :on-close   (fn [event] (js/console.log ":on-close :" event))
-                                                          :on-message (fn [event]
-                                                                        (let [fr (js/FileReader. (.-data event))]
-                                                                          (aset fr "onloadend" (fn [e]
-                                                                                                 (let [data (transit/read transit-reader (.. e -target -result))]
-                                                                                                   (if-let [cb-id (:server/rpc-id data)]
-                                                                                                     (let [cb-or-ch (get @pending-rpc cb-id)]
-                                                                                                       (swap! pending-rpc dissoc cb-id)
-                                                                                                       ;; Should we just pass the whole payload through, or just the data/results?
-                                                                                                       (cond
-                                                                                                         (chan? cb-or-ch) (put! cb-or-ch (:data data))
-                                                                                                         (fn? cb-or-ch)   (cb-or-ch (:data data))
-                                                                                                         :else            (ss-cast! (:data data))))
-                                                                                                     (ss-cast! data)))))
-                                                                          (.readAsText fr (.-data event))))
-                                                          :on-error   (fn [event] (js/console.log ":on-error :" event))})
+        tal-state          (tal/init {:port dato-port
+                                      :transit-reader transit-reader
+                                      :transit-writer transit-writer})
+        _                  (tal/start-message-consumer tal-state (fn [msg]
+                                                                   (ws-message-handler dato-ch msg)))
         dato-send!         (fn [event data]
-                             (.send ws (transit/write transit-writer [event data])))
+                             (tal/queue-msg! tal-state {:op event :data data}))
         controls-ch        (async/chan)
         comms              {:controls controls-ch
                             :stop     (async/chan)

                               (dato-send! event payload)
                               true))
                :bootstrap! (fn []
                              (go
                                (js/console.log "Dato bootstrapping")
                                ;; Bootstrap us first
                                (loop []
                                  (when-not (= (.-readyState ws) 1)
                                    (<! (async/timeout 250))
                                    (js/console.log "Dato WebSocket not ready (" (.-readyState ws) "), waiting...")
                                    (recur)))
                                (dato-send! :ss/bootstrap {})
                                (js/console.log "1. Bootstrap")
                                (let [{:keys [data] :as -rpc-response} (loop [msg (<! ws-ch)]
                                                                         (if (= :ss/bootstrap-succeeded (get-in msg [:data :event]))
                                                                           msg
                                                                           ;; Not the
                                                                           ;; message
                                                                           ;; we're
                                                                           ;; looking for
                                                                           ;; (could be
                                                                           ;; something
                                                                           ;; during
                                                                           ;; refreshing
                                                                           ;; with
                                                                           ;; figwheel. Put
                                                                           ;; the message
                                                                           ;; back on,
                                                                           ;; wait a few
                                                                           ;; ms, and try
                                                                           ;; again. Still very racy.
                                                                           (do
                                                                             (put! ws-ch msg)
                                                                             (<! (async/timeout 16))
                                                                             (recur (<! ws-ch)))))
                                      data                             (:data data)
                                      _                                (js/console.log "1.5. -Bootstrap: " -rpc-response)
                                      _                                (js/console.log "2. Bootstrap; " data)
                                      {:keys [session-id schema]}      data]
                                  (swap! ss-data merge (:ss data))
                                  (reset! ss-api (->> (:ss data)
                                                      (map (fn [[k v]]
                                                             {(keyword (name k)) (fn-desc->rpc k v)}))
                                                      (reduce merge {})))
                                  (js/console.log "3. bootstrap: " app-db)
                                  (js/console.log "4. " @ss-api)
                                  ;; Do something with data
                                  (let [db (let [bootstrapped? (bootstrapped? @app-db)
                                                 conn          (if bootstrapped?
                                                                 app-db
                                                                 (d/create-conn (merge schema db/cs-schema cs-schema)))]
                                             (def --fconn conn)
                                             ;; Check if we're reloading
                                             ;; with a pre-existing db
                                             (when-not bootstrapped?
                                               ;;(js/console.log (pr-str schema))
                                               ;;(js/console.log (pr-str (vals schema)))
                                               ;;(js/console.log "conn: " (pr-str conn))
                                               (remove-watch app-db :global-listener)
                                               (reset! app-db @conn)
                                               ;;(js/console.log "me")
                                               (doto app-db
                                                 (db/setup-listener! :global-listener))
                                               (let [request-transaction! (get-in @ss-api [:tx-requested])]
                                                 (d/listen! app-db :server-tx-report
                                                            (fn [tx-report]
                                                              (let [datoms (tx-report->transaction tx-report)]
                                                                ;;(js/console.log "Listened TX: " tx-report)
                                                                (when (or (get-in tx-report [:tx-meta :tx/broadcast?])
                                                                          (get-in tx-report [:tx-meta :tx/persist?]))
                                                                  (let [prepped (db/prep-broadcastable-tx-report tx-report)]
                                                                    (when @network-enabled?
                                                                      (request-transaction! prepped))))))))
                                               (let [local-session-id (d/tempid :db.part/user)
                                                     local-session-tx [{:db/id                 (d/tempid :db.part/user)
                                                                        :dato.meta/bootrapped? true}
                                                                       ;; TODO: This can likely be moved out to a user-land handler
                                                                       (assoc (:session data) :db/id local-session-id)
                                                                       {:local/current-session {:db/id local-session-id}}]]
                                                 (d/transact! app-db (vals schema))
                                                 (d/transact! app-db local-session-tx))
                                               (reset! cast-bootstrapped? true))
                                             conn)]
                                    (put! dato-ch [:dato/bootstrap-succeeded session-id])
                                    (loop []
                                      (let [msg (<! ws-ch)]
                                        ;; Should this be more sophisticated?
                                        (when (:event msg)
                                          (put! dato-ch msg))
                                        (recur)))))))}}))

;; TODO: Probably convert to cljs Record to reify this stuff + avoid the apply shortcut
(defn bootstrap! [dato & args]

            [dato.lib.db :as db]
            [dato.lib.transit :as dato-transit]
            [om.core :as om]
            [goog.Uri]
            [talaria.api :as tal])
  (:require-macros [cljs.core.async.macros :as async :refer [alt! go go-loop]])
  (:import [goog Uri]))

