 ;; to process all queued events, or else something like a bursty websocket
 ;; (producing events) might overwhelm the queue. So there's a balance.
 ;;
-;; The original implementation of this router loop used core.async. It was
-;; fairly simple, and it mostly worked, but it did not give enough
-;; control. So now we hand-roll our own, finite-state-machine and all.
+;; The processing/handling of an event happens "asynchronously" sometime after
+;; that event was enqueued via "dispatch". The original implementation of this router loop
+;; used core.async. It was fairly simple, and it mostly worked, but it did not
+;; give enough control. So now we hand-roll our own, finite-state-machine and all.
 ;;
 ;; The strategy is this:
 ;;   - maintain a FIFO queue of `dispatched` events.
 ;;   - when a new event arrives, "schedule" processing of this queue using
 ;;     goog.async.nextTick, which means it will happen "very soon".
-;;   - when processing events, do ALL the those currently queued. Don't stop.
-;;     Don't yield to the browser. Hog that CPU.
+;;   - when processing events, one after the other, do ALL the those currently
+;;     queued. Don't stop. Don't yield to the browser. Hog that CPU.
 ;;   - but if any new events are dispatched during this cycle of processing,
 ;;     don't do them immediately. Leave them queued. Yield first to the browser,
 ;;     and do these new events in the next processing cycle. That way we drain

;;     particular, we handle the case where handling one event will beget
;;     another event. The freshly begat event will be handled next cycle,
;;     with yielding in-between.
;;   - In some cases, an event should not be run until after the GUI has been
;;     updated, i.e., after the next Reagent animation frame. In such a case,
;;     the event should be dispatched with :flush-dom metadata like this:
;;       (dispatch ^:flush-dom [:event-id other params])
;;     Such an event will temporarily block all further processing because
;;     events are processed sequentially: we handle each event before we
;;     handle the ones behind it.
;;
;; Implementation
;;   - queue processing can be in a number of states: scheduled, running, paused
;;     etc. So it is modeled below as a Finite State Machine.
;;     See "-fsm-trigger" (below) for the states and transitions.
;;   - the scheduling is done via "goog.async.nextTick" which is pretty quick
;;   - when the event has :flush-dom metadata we schedule via
;;       "reagent.impl.batching.do-later"
;;     which will run event processing after the next reagent animation frame.
;;

;; event metadata -> "run later" functions
(def later-fns
  {:flush-dom (fn [f] ((.-after-render reagent.core) #(goog.async.nextTick f)))   ;; one tick after the end of the next annimation frame
   :yield     goog.async.nextTick})           ;; almost immediately

(defprotocol IEventQueue
  (enqueue [this event])

  (add-post-event-callback [this f])

  ;; Finite State Machine transitions
  (-fsm-trigger [this trigger arg])

  ;; Finite State Machine (FSM) actions
  (-add-event [this event])
  (-process-1st-event [this])
  (-run-next-tick [this])

;;

(defn dispatch
  "Queue an event to be processed by the registered handler function.

  Usage example:
     (dispatch [:delete-item 42])"
