              :mouseup (add-event :mouseup)
              :mouseleave (add-event :mouseleave))
     (listen! [(sel1 :#puzzle-table) :.num]
-             :mousedown handle-number-click
+             :mousedown (add-event :number-click)
              :contextmenu disable-context-menu)
-    (listen! (sel1 :#button-done) :click done-handler)
-    (listen! (sel1 :#button-clear) :click clear-puzzle)))
-
-(defn handle-cell-events [event-chan]
+    (listen! (sel1 :#button-done) :click (add-event :done))
+    (listen! (sel1 :#button-clear) :click (add-event :clear))))
+
+(defn handle-cell-events [event-chan initial-state]
   (go-loop
    [[evt event-type] (<! event-chan)
-    state {:fill-style nil
-           :base-cell nil
-           :last-cell nil}]
+    state initial-state]
    (let [new-state (case event-type
-                     :mousedown (handle-mouse-down-on-cell evt)
+                     :mousedown (handle-mouse-down-on-cell evt state)
                      :contextmenu (disable-context-menu evt)
                      :mouseenter (handle-mouse-enter-on-cell evt state)
-                     :mouseup (stop-dragging state)
-                     :mouseleave (stop-dragging state))]
+                     :mouseup (stop-dragging initial-state state)
+                     :mouseleave (stop-dragging initial-state state)
+                     :number-click (handle-number-click evt)
+                     :done (if (done-handler evt) initial-state state)
+                     :clear (do (clear-puzzle) initial-state)
+                     (do (log (str "Unknown event: " event-type)) state))]
      (recur (<! event-chan) new-state))))
 
 (defn show [nono]
-  (init-board! board (count (:top nono)) (count (:left nono)))
   (dommy/replace! (sel1 :#puzzle-table) (create-template nono))
   (let [event-chan (chan 5)]
     (add-handlers event-chan)
-    (dommy/remove-class! (sel1 :.puzzle-container) "hidden")
-    (handle-cell-events event-chan))
+    (handle-cell-events event-chan
+                        {:fill-style nil
+                         :base-cell nil
+                         :last-cell nil
+                         :drag-type :rect
+                         :board (init-board (count (:top nono)) (count (:left nono)))}))
   (show-view :puzzle))
 
 (defn ^:export init []


;;; Board functions

(defn init-board! [board width height]
  (let [state (->> (repeat width :empty)
                   vec
                   (repeat height)
                   vec)]
    (reset! board state)))

(defn change-cell-in-board! [board x y new-state]
  (swap! board assoc-in [y x] new-state))

(defn update-region-of-board! [board [from-x from-y] [to-x to-y] new-state]
  (let [cells-to-change (for [x (range-inc from-x to-x)
                              y (range-inc from-y to-y)]
                          [y x])
        new-board (reduce #(assoc-in %1 %2 new-state) @board cells-to-change)]
    (reset! board new-board)))

(defn pad [nums beg end value]
  (vec (concat (repeat beg value)

    2 :right
    :unknown))

(defn handle-mouse-down-on-cell [evt state]
  (let [cell (.-target evt)
        style (new-cell-style-after-click cell (button evt))
        x (attr-int cell :data-x)
        y (attr-int cell :data-y)]
    (change-cell-style! cell style)
    (assoc state
      :fill-style style
      :base-cell [x y]
      :last-cell [x y]
      :board (change-cell-in-board (:board state) x y style))))

(defn handle-mouse-enter-on-cell [evt state]
  (if-not (:fill-style state)
    state
    (let [cell (.-target evt)
          x (attr-int cell :data-x)
          y (attr-int cell :data-y)
          {:keys [fill-style base-cell last-cell drag-type]} state]
      (if (= :rect drag-type)
        (do
          (update-cells-region-style! base-cell last-cell (fn [x y] (get-in state [:board y x])))
          (update-cells-region-style! base-cell [x y] (constantly fill-style))
          (assoc state :last-cell [x y]))
        (do
          (change-cell-style! cell fill-style)
          (assoc state
            :last-cell [x y]
            :board (change-cell-in-board (:board state) x y fill-style)))))))

(defn stop-dragging [initial-state {:keys [fill-style base-cell last-cell board drag-type]}]
  (assoc initial-state :board
    (if (and fill-style (= :rect drag-type))
      (update-region-of-board board base-cell last-cell fill-style)
      board)))

(defn disable-context-menu [evt]
  (.preventDefault evt)
