 ;   You must not remove this notice, or any other, from this software.
 
 (ns cljs.browser-repl
+  (:require [clojure.string :as str]
+            [cljs.compiler :as comp])
   (:import java.io.BufferedReader
            java.io.BufferedWriter
            java.io.InputStreamReader
            java.io.OutputStreamWriter
            java.net.Socket
-           java.net.ServerSocket)
-  (:require [clojure.string :as str]))
+           java.net.ServerSocket))
+
+;; The main purpose of this namespace is to implement the server side
+;; of the browser-connected repl. This server will push data to the
+;; browser to be evaluated and receive return values. Server push is
+;; implemented through long polling. Requests sent from the browser
+;; are expected to be HTTP Post requests.
 
 (defonce server-state (atom {:socket nil
-                             :port nil
-                             :connection nil}))
-
-(defonce pending-forms (atom []))
+                             :connection nil
+                             :promised-conn nil
+                             :return-value-fn nil}))
+
+(defn connection
+  "Promise to return a connection when one is available. If a
+  connection is not available, store the promise in server-state."
+  []
+  (let [p (promise)
+        conn (:connection @server-state)]
+    (if (and conn (not (.isClosed conn)))
+      (do (deliver p conn)
+          p)
+      (do (swap! server-state (fn [old] (assoc old :promised-conn p)))
+          p))))
+
+(defn set-connection
+  "Given a new available connection, either use it to deliver the
+  connection which was promised or store the connection for later
+  use."
+  [conn]
+  (if-let [promised-conn (:promised-conn @server-state)]
+    (do (swap! server-state (fn [old] (-> old
+                                         (assoc :connection nil)
+                                         (assoc :promised-conn nil))))
+        (deliver promised-conn conn))
+    (swap! server-state (fn [old] (assoc old :connection conn)))))
+
+(defn set-return-value-fn
+  "Save the return value function which will be called when the next
+  return value is received."
+  [f]
+  (swap! server-state (fn [old] (assoc old :return-value-fn f))))
 
 (defn send-and-close
-  "Send this message on the connection and close it."
-  [conn message]
-  (println "the message is: " message)
+  "Use the passed connection to send a form to the browser."
+  [conn form]
   (with-open [writer (BufferedWriter.
                       (OutputStreamWriter. (.getOutputStream conn)))]
-    (do (.write writer message)
-        (.write writer "\r\n")
+    (do (.write writer form)
+        (.write writer "\r\n") ;; remove this
         (.flush writer)
         (.close conn))))
 
 (defn send-for-eval
-  "If a connection is available then send this form."
-  [message]
-  (when-let [conn (:connection @server-state)]
-    (println "sending for eval")
-    (send-and-close conn message)))
-
-(defn process-form []
-  (when-let [first-form (ffirst @pending-forms)]
-    (println "first-form: " first-form)
-    (send-for-eval first-form)))
-
-(defn return [return-value]
-  (let [[[_ first-promise]] @pending-forms]
-    (swap! pending-forms drop 1)
-    (deliver first-promise return-value)
-    (process-form)))
-
-(defn send-forms [forms]
-  (let [pending (reset! pending-forms
-                        (map #(vector % (promise)) forms))]
-    (println "pending forms: " pending)
-    (process-form)
-    ;; TODO: (map second pending) should be consumed by a real REPL
-    (doseq [return-value (map second pending)]
-      (println @return-value))))
-
-;; POST /login.jsp HTTP/1.1
-;; Host: www.mysite.com
-;; User-Agent: Mozilla/4.0
-;; Content-Length: 27
-;; Content-Type: application/x-www-form-urlencoded
-;;
-;; userid=joe&password=guessme
+  "Given a form and a return value function, send the form to the
+  browser for evaluation. The return value function will be called
+  when the return value is received."
+  [form return-value-fn]
+  (do (set-return-value-fn return-value-fn)
+      (send-and-close @(connection) form)))
+
+(defn return-value
+  "Called by the server when a return value is received."
+  [val]
+  (when-let [f (:return-value-fn @server-state)]
+    (f val)))
+
+(defn parse-headers
+  "Parse the headers of an HTTP POST request."
+  [header-lines]
+  (apply hash-map
+   (mapcat
+    (fn [line]
+      (let [[k v] (str/split line #":" 2)]
+        [(keyword (str/lower-case k)) (str/triml v)]))
+    header-lines)))
 
 (comment
 


(defn- handle-connection
  [conn]
  (let [reader (BufferedReader. (InputStreamReader. (.getInputStream conn)))]
    (if-let [message (read-request reader)]
      (do (when (not= message "ready")
            (return message))
          (swap! server-state (fn [old] (assoc old :connection conn)))))))

(defn- server-loop
  [server-socket]

    "Content-Length: 27"
    "Content-Type: application/x-www-form-urlencoded"])
)

;;; assumes first line already consumed
(defn read-headers [rdr]
