<commit_before>(ns ambly.repl.jsc
  (:require [clojure.string :as string]
            [clojure.java.io :as io]
            [cljs.analyzer :as ana]
            [cljs.util :as util]
            [cljs.compiler :as comp]
            [cljs.repl :as repl]
            [cljs.closure :as closure]
            [clojure.data.json :as json])
  (:import java.net.Socket
           java.lang.StringBuilder
           [java.io File BufferedReader BufferedWriter IOException]
           (javax.jmdns JmDNS ServiceListener)
           (java.net URI)))

(defn sh
  [& args]
  {:pre [(every? string? args)]}
  (.waitFor (.exec (Runtime/getRuntime) (string/join " " args))))

(defn set-logging-level [logger-name level]
  {:pre [(string? logger-name) (instance? java.util.logging.Level level)]}
  (.setLevel (java.util.logging.Logger/getLogger logger-name) level))

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

(defn is-ambly-bonjour-name? [bonjour-name]
  {:pre [(string? bonjour-name)]}
  (.startsWith bonjour-name ambly-bonjour-name-prefix))

(defn bonjour-name->display-name
  [bonjour-name]
  {:pre [(is-ambly-bonjour-name? bonjour-name)]
   :post [(string? %)]}
  (subs bonjour-name (count ambly-bonjour-name-prefix)))

(defn name-endpoint-map->choice-list [name-endpoint-map]
  {:pre [(map? name-endpoint-map)]}
  (map vector (iterate inc 1) name-endpoint-map))

(defn print-discovered-devices [name-endpoint-map opts]
  {:pre [(map? name-endpoint-map) (map? opts)]}
  (if (empty? name-endpoint-map)
    (println "(No devices)")
    (doseq [[choice-number [bonjour-name _]] (name-endpoint-map->choice-list name-endpoint-map)]
      (println (str "[" choice-number "] " (bonjour-name->display-name bonjour-name))))))

(defn discover-and-choose-device
  "Looks for Ambly WebDAV devices advertised via Bonjour and presents
  a simple command-line UI letting user pick one, unless
  choose-first-discovered? is set to true in which case the UI is bypassed"
  [choose-first-discovered? opts]
  {:pre [(map? opts)]}
  (let [reg-type "_http._tcp.local."
        name-endpoint-map (atom (sorted-map))
        mdns-service (JmDNS/create)
        service-listener
        (reify ServiceListener
          (serviceAdded [_ service-event]
            (let [type (.getType service-event)
                  name (.getName service-event)]
              (when (and (= reg-type type) (is-ambly-bonjour-name? name))
                (.requestServiceInfo mdns-service type name 1))))
          (serviceRemoved [_ service-event]
            (swap! name-endpoint-map dissoc (.getName service-event)))
          (serviceResolved [_ service-event]
            (let [type (.getType service-event)
                  name (.getName service-event)]
              (when (and (= reg-type type) (is-ambly-bonjour-name? name))
                (let [entry {name (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 opts)
        (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
        (.start
          (Thread.
            (fn []
              (.removeServiceListener mdns-service reg-type service-listener)
              (.close mdns-service))))))))

(defn socket
  [host port]
  {:pre [(string? host) (number? port)]}
  (let [socket (doto (Socket. host port) (.setKeepAlive true))
        in     (io/reader socket)
        out    (io/writer socket)]
    {:socket socket :in in :out out}))

(defn close-socket
  [s]
  {:pre [(map? s)]}
  (.close (:socket s)))

(defn write
  [out js]
  (:pre [(instance? BufferedWriter out) (string? js)])
  (.write out js)
  (.write out (int 0)) ;; terminator
  (.flush out))

(defn read-messages<commit_msg>Fix #75 Simulator not discovered<commit_after>(ns ambly.repl.jsc
  (:require [clojure.string :as string]
            [clojure.java.io :as io]
            [cljs.analyzer :as ana]
            [cljs.util :as util]
            [cljs.compiler :as comp]
            [cljs.repl :as repl]
            [cljs.closure :as closure]
            [clojure.data.json :as json])
  (:import java.net.Socket
           java.lang.StringBuilder
           [java.io File BufferedReader BufferedWriter IOException]
           (javax.jmdns JmDNS ServiceListener)
           (java.net URI)))

(defn sh
  [& args]
  {:pre [(every? string? args)]}
  (.waitFor (.exec (Runtime/getRuntime) (string/join " " args))))

(defn set-logging-level [logger-name level]
  {:pre [(string? logger-name) (instance? java.util.logging.Level level)]}
  (.setLevel (java.util.logging.Logger/getLogger logger-name) level))

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

(defn is-ambly-bonjour-name? [bonjour-name]
  {:pre [(string? bonjour-name)]}
  (.startsWith bonjour-name ambly-bonjour-name-prefix))

(defn bonjour-name->display-name
  [bonjour-name]
  {:pre [(is-ambly-bonjour-name? bonjour-name)]
   :post [(string? %)]}
  (subs bonjour-name (count ambly-bonjour-name-prefix)))

(defn name-endpoint-map->choice-list [name-endpoint-map]
  {:pre [(map? name-endpoint-map)]}
  (map vector (iterate inc 1) name-endpoint-map))

(defn print-discovered-devices [name-endpoint-map opts]
  {:pre [(map? name-endpoint-map) (map? opts)]}
  (if (empty? name-endpoint-map)
    (println "(No devices)")
    (doseq [[choice-number [bonjour-name _]] (name-endpoint-map->choice-list name-endpoint-map)]
      (println (str "[" choice-number "] " (bonjour-name->display-name bonjour-name))))))

(defn setup-mdns
  "Sets up mDNS to populate atom supplied in name-endpoint-map with discoveries.
  Returns a function that will tear down mDNS."
  [reg-type name-endpoint-map]
  {:pre [(string? reg-type)]
   :post [(fn? %)]}
  (let [mdns-service (JmDNS/create)
        service-listener
        (reify ServiceListener
          (serviceAdded [_ service-event]
            (let [type (.getType service-event)
                  name (.getName service-event)]
              (when (and (= reg-type type) (is-ambly-bonjour-name? name))
                (.requestServiceInfo mdns-service type name 1))))
          (serviceRemoved [_ service-event]
            (swap! name-endpoint-map dissoc (.getName service-event)))
          (serviceResolved [_ service-event]
            (let [type (.getType service-event)
                  name (.getName service-event)]
              (when (and (= reg-type type) (is-ambly-bonjour-name? name))
                (let [entry {name (let [info (.getInfo service-event)]
                                    {:address (.getAddress info)
                                     :port    (.getPort info)})}]
                  (swap! name-endpoint-map merge entry))))))]
    (.addServiceListener mdns-service reg-type service-listener)
    (fn []
      (.removeServiceListener mdns-service reg-type service-listener)
      (.close mdns-service))))

(defn discover-and-choose-device
  "Looks for Ambly WebDAV devices advertised via Bonjour and presents
  a simple command-line UI letting user pick one, unless
  choose-first-discovered? is set to true in which case the UI is bypassed"
  [choose-first-discovered? opts]
  {:pre [(map? opts)]}
  (let [reg-type "_http._tcp.local."
        name-endpoint-map (atom (sorted-map))
        tear-down-mdns
        (loop [count 0
               tear-down-mdns (setup-mdns reg-type name-endpoint-map)]
          (if (empty? @name-endpoint-map)
            (do
              (Thread/sleep 100)
              (when (= 20 count)
                (println "\nSearching for devices ..."))
              (if (zero? (rem (inc count) 100))
                (do
                  (tear-down-mdns)
                  (recur (inc count) (setup-mdns reg-type name-endpoint-map)))
                (recur (inc count) tear-down-mdns)))
            tear-down-mdns))]
    (try
      (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 opts)
        (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
        (.start (Thread. tear-down-mdns))))))

(defn socket
  [host port]
  {:pre [(string? host) (number? port)]}
  (let [socket (doto (Socket. host port) (.setKeepAlive true))
        in     (io/reader socket)
        out    (io/writer socket)]
    {:socket socket :in in :out out}))

(defn close-socket
  [s]
  {:pre [(map? s)]}
  (.close (:socket s)))

(defn write
  [out js]
  (:pre [(instance? BufferedWriter out) (string? js)])
  (.write out js)
  (.write out (int 0)) ;; terminator
  (.flush out))

(defn read-messages
{'fraction_non_alphanumeric': 0.14508172937421415, 'fraction_numerical': 0.003385240352064997, 'mean_word_length': 2.2556675062972293, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 13, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 8, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4798030', 'n_tokens_mistral': 3380, 'n_tokens_neox': 3217, 'n_words': 829}