 (defn setup
   [repl-env opts]
   (let [_ (set-logging-level "javax.jmdns" java.util.logging.Level/SEVERE)
-        [webdav-endpoint-name webdav-endpoint] (discover-and-pick-ambly-instance (:choose-first-discovered repl-env))
-        _ (println "\nConnecting to" (service-name->display-name webdav-endpoint-name) "...\n")
-        ; Assuming IPv4 for now
-        endpoint-address (.getHostAddress (:address webdav-endpoint))
-        endpoint-port (:port webdav-endpoint)
+        [bonjour-name endpoint] (discover-and-choose-device (:choose-first-discovered repl-env))
+        endpoint-address (.getHostAddress (:address endpoint))
+        endpoint-port (:port endpoint)
         webdav-mount-point (str "/Volumes/Ambly-" endpoint-address)
         output-dir (io/file webdav-mount-point)
         _ (.mkdirs output-dir)
         env (ana/empty-env)
         core (io/resource "cljs/core.cljs")]
+    (println "\nConnecting to" (bonjour-name->display-name bonjour-name) "...\n")
     (reset! (:webdav-mount-point repl-env) webdav-mount-point)
     (shell/sh "mount_webdav" (str "http://" endpoint-address ":" endpoint-port) webdav-mount-point)
     (reset! (:socket repl-env)


(def ambly-bonjour-name-prefix "Ambly ")

(defn service-name->display-name [service-name]
  (subs service-name (count ambly-bonjour-name-prefix)))

(defn service-map->choice-list [service-map]
  (map vector (iterate inc 1) service-map))

(defn print-services [service-map]
  (doseq [[choice-number [service-name _]] (service-map->choice-list service-map)]
    (println (str "[" choice-number "] " (service-name->display-name service-name)))))

(defn discover-and-pick-ambly-instance
  "Looks for Ambly WebDAV services advertised via Bonjour and presents
  a simple command-line UI letting user pick one."
  [choose-first-discovered?]
  (let [reg-type "_http._tcp.local."
        discovered-services (atom (sorted-map))
        mdns-service (JmDNS/create)
        service-listener
        (reify ServiceListener

              (when (.startsWith name ambly-bonjour-name-prefix)
                (.requestServiceInfo mdns-service (.getType service-event) (.getName service-event) 1))))
          (serviceRemoved [_ service-event]
            (swap! name-endpoint-map dissoc (.getName service-event)))
          (serviceResolved [_ service-event]
            (let [entry {(.getName service-event)
                         (let [info (.getInfo service-event)]
                           {:address (.getAddress info)
                            :port    (.getPort info)})}]
              (swap! name-endpoint-map merge entry))))]
    (try
      (.addServiceListener mdns-service reg-type service-listener)
      (loop [count 0]
        (when (empty? @name-endpoint-map)
          (Thread/sleep 100)
          (when (= 20 count)
            (println "\nSearching for devices ..."))
          (recur (inc count))))
      (Thread/sleep 500)                                    ;; Sleep a little more to catch stragglers
      (loop [current-name-endpoint-map @name-endpoint-map]
        (println)
        (print-discovered-devices current-name-endpoint-map)
        (when-not choose-first-discovered?
          (println "\n[R] Refresh\n")
          (print "Choice: ")
          (flush))
        (let [choice (if choose-first-discovered? "1" (read-line))]
          (if (= "r" (.toLowerCase choice))
            (recur @name-endpoint-map)
            (let [choices (name-endpoint-map->choice-list current-name-endpoint-map)
                  choice-ndx (try (dec (Long/parseLong choice)) (catch NumberFormatException _ -1))]
              (if (< -1 choice-ndx (count choices))
                (second (nth choices choice-ndx))
                (recur current-name-endpoint-map))))))
      (finally
        (future
          (.removeServiceListener mdns-service reg-type service-listener)
