             [cljs.reader :as reader]
             [clojure.string :as string]
             [dato.lib.core :as dato]
+            [dato.datascript-utils :as dsu]
             [dato.debug.components.keyboard-listener :as keyboard]
+            [goog.dom.classes :as gclass]
             [om-bootstrap.panel :as bs-p]
             [om-bootstrap.grid :as g]
             [om.core :as om :include-macros true]

                        :ns      'dato.debug}
                    cb))))

(defn history-com [data owner opts]
  (reify
    om/IDisplayName

          (when (om/get-state owner [:open?])
            (bs-p/panel
             {:header (dom/div "["
                               (dom/button {:style    {:cursor "pointer"}
                                            :on-click #(om/set-state! owner [:tool] :timeline)} "Timeline")
                               "]  ["
                               (dom/button {:style    {:cursor "pointer"}
                                            :on-click #(om/set-state! owner [:tool] :components)} "Components")
                               "]  ["
                               (dom/button {:style    {:cursor "pointer"}
                                            :on-click #(om/set-state! owner [:tool] :states)} "States")
                               "]  ["
                               (dom/button {:style    {:cursor "pointer"}
                                            :on-click #(om/set-state! owner [:tool] :query)} "Expression Watch")
                               "] - Dato Debugger " (dom/small "(" log-count " states)"))}
             (case (om/get-state owner [:tool])
               :states     (dom/div
                            (dom/h3 "Click a slot to restore a state")
                            (dom/ul
                             (for [slot-idx (range 0 10)]
                               (let [local-storage-name (str "dato_save_state_" slot-idx)
                                     existing-db        (js/localStorage.getItem local-storage-name)]
                                 (dom/li {:style    {:cursor "pointer"}
                                          :on-click #(restore-save-state! owner slot-idx)} (if existing-db
                                          (dom/strong "Slot " slot-idx)
                                          "No state saved"))))))
               :query      (dom/div
                            (g/grid
                             {}
                             (g/row
                              {}
                              (g/col
                               {:xs 0 :sm 2 :md 2 :lg 2}
                               (g/row
                                {}
                                (g/col
                                 {:xs 12 :sm 12 :md 12 :lg 12}
                                 (dom/input {:placeholder "Search Expressions"})))
                               (g/row
                                {}
                                (g/col
                                 {:xs 12 :sm 12 :md 12 :lg 12}
                                 (dom/ul
                                  (for [[idx query] (map-indexed vector (om/get-state owner [:query]))
                                        :let [current-query? (= (:title query) (om/get-state owner [:current-query]))
                                              title (str idx ". " (:title query))]]
                                    (dom/li {:style    {:cursor "pointer"}
                                             :on-click #(om/set-state! owner [:current-query] idx)}
                                            (if current-query?
                                              (dom/strong title)
                                              title)))))))
                              (g/col {:xs 3 :sm 3 :md 3 :lg 3}
                                     (let [current-query (om/get-state owner [:query (om/get-state owner [:current-query])])]
                                       (dom/div
                                        (when (false? (:valid? current-query))
                                          (dom/pre "Invalid code"))
                                        (dom/input {:placeholder "Query title"
                                                    :value "Current Session"})
                                        (dom/textarea {:placeholder "Expression (cljs code)"
                                                       :value       (:str current-query)
                                                       :on-change   (fn [event]
                                                                      (let [str-value (.. event -target -value)
                                                                            valid? (try (compile-expr str-value identity)
                                                                                        true
                                                                                        (catch js/Error e
                                                                                          false))]
                                                                        (om/update-state! owner [:queries (om/get-state owner [:current-query])] (fn [s]
                                                                                                                                                   (merge s
                                                                                                                                                          {:str    str-value
                                                                                                                                                           :valid? valid?})))))
                                                       :style       {:width  "100%"
                                                                     :height "100%"}}))))
                              (g/col {:xs 12 :sm 7 :md 7 :lg 7}
                                     (when-let [current-query (om/get-state owner [:query (om/get-state owner [:current-query])])]
                                       (dom/pre
                                        (binding [*print-length* 10]
                                          (let [query-results ((:fn current-query) db)]
                                            (with-out-str (pp/pprint query-results))))))))))
               :editor     (dom/div
                            "Code editor"
                            (dom/link {:rel      "stylesheet"
                                       :property "stylesheet"
                                       :href     "/css/vendor/codemirror/codemirror.css"})
                            (dom/div {:className "editor"}))
               :components (dom/div
                            "Component States"
                            (dom/link {:rel      "stylesheet"
                                       :property "stylesheet"
                                       :href     "/css/vendor/codemirror/codemirror.css"})
                            (dom/div {:className "state"}
                                     (let [components        (state-watcher-coms @state-watchers)
                                           inspected-com-key (om/get-state owner [:inspected-com-key])]
                                       (dom/div
                                        (dom/input {:on-change   #(om/set-state! owner [:com-name-filter] (.. % -target -value))
                                                    :value        (om/get-state owner [:com-name-filter])
                                                    :placeholder "Component Display Name"})
                                        (dom/div
                                         {:style {:float "right"
                                                  :width "50%"}}
                                         (if inspected-com-key
                                           (dom/h4 (com-name @state-watchers inspected-com-key) "(" inspected-com-key ")")
                                           (dom/h4 "No component"))
                                         (when-let [inspected-com-key (om/get-state owner [:inspected-com-key])]
                                           (let [com-states  (com-state @state-watchers inspected-com-key)
                                                 dato-state* (:dato com-states)
                                                 dato-state  (when dato-state*
                                                               (dsu/touch+ dato-state*))
                                                 om-state    (:om com-states)]
                                             (dom/div
                                              "Dato/Recorded"
                                              (dom/pre (with-out-str (pp/pprint dato-state)))
                                              "Om/Transient"
                                              (dom/pre (with-out-str (pp/pprint (->
                                                                                 om-state
                                                                                 (dissoc :dato.lib.db/listener-key)
                                                                                 (dissoc :dato.lib.db/listener-keys)))))))))
                                        (dom/ul
                                         (for [com-key components
                                               :let [com-name (com-name @state-watchers com-key)]
                                               :when (and com-name
                                                          (let [filter (om/get-state owner [:com-name-filter])]
                                                            (if (string/blank? filter)
                                                              true
                                                              (.match (string/lower-case com-name) filter))))]
                                           (let [com-owner  (com-owner @state-watchers com-key)
                                                 owner-node (when com-owner (om/get-node com-owner))]
                                             (dom/li {:style    {:cursor "pointer"}
                                                      ;; Someday we'll figure this out...
                                                      :on-mouse-enter #(when owner-node (highlight-node! owner-node))
                                                      :on-mouse-leave #(when owner-node (unhighlight-node! owner-node))
                                                      :on-click #(om/set-state! owner [:inspected-com-key] com-key)} com-name "(" com-key ")"))))))))
               :timeline   (dom/div
                            (dom/h3 (pr-str (get-in log [selected-state-idx :tx/intent])))
                            (dom/div
                             (dom/input {:type      "range"
                                         :min       0
                                         :max       (max 0 (dec (count (:log data))))
                                         :value     (om/get-state owner [:states :selected])
                                         :on-change #(let [idx (reader/read-string (.. % -target -value))]
                                                       (om/set-state! owner [:states :selected] idx)
                                                       (set-app-state! idx))})
                             (dom/button {:on-click #(play-all-states!)
                                          :disabled (not (pos? log-count))} "> Play All")
                             (dom/div {:style {:float "right"
                                               :width "50%"}}
                                      (dom/pre (pr-str (get-in selected-state [:tx :tx-data]))))
                             (dom/ul
                              {:className "event-history"}
                              (let [int (->> (:log data)
                                             (map-indexed vector)
                                             (reduce (fn [run [idx next]]
                                                       (let [cnt        (count run)
                                                             last-event (:tx/intent (nth run (dec cnt)))
                                                             selected?  (= idx selected-state-idx)]
                                                         (if (= last-event (:tx/intent next))
                                                           (-> run
                                                               (update-in [(dec cnt) :count] inc)
                                                               (update-in [(dec cnt) :selected?] #(or selected? %)))
                                                           (conj run (-> next
                                                                         (assoc :count 1)
                                                                         (assoc :selected? selected?)))))) [{}]))]
                                (for [tx int]
                                  (dom/li (if (:selected? tx)
                                            (dom/strong (pr-str (:tx/intent tx)))
                                            (pr-str (:tx/intent tx)))
                                          (when (pos? (dec (:count tx))) (str " x " (:count tx))))))))))))))))))
