(ns hoplon.app-pages._index_DOT_html (:require [cljs.pprint :refer [pprint]] [weathergen.ipc :as ipc] [weathergen.ui :as ui] [weathergen.ui.buttons :as buttons] [weathergen.ui.common :as comm :refer [inl pct px]] [weathergen.ui.trees :as trees] [taoensso.timbre :as log :refer-macros ^{:line 11, :column 28} (log trace debug info warn error fatal report logf tracef debugf infof warnf errorf fatalf reportf spy get-env log-env)] [javelin.core :refer [->Cell input? cell cell? destroy-cell! lift lens? set-formula! constant? cell-doseq* deref* set-cell! lens formula? alts! dosync* cell-map formula]] [hoplon.jquery :refer [set-attributes! check-val! set-styles! text-val!]] [hoplon.core :refer [form audio input menuitem hgroup do! timeout $text base h1 set-attributes! embed shadow h3 body keygen progress main cite on-page-load i p nav ruby a menu blockquote img $comment span track data u dl select html thead del fieldset aside figure figcaption q on! bdi append-child! video address caption dd rp hr tbody table html-var add-initfn! pre ul replace-child! html-time html-map sup dfn sub mark script button wbr insert-before! strong normalize-class li dt rtc td tr section th optgroup bust-cache iframe remove-child! legend em kbd spliced html-object article abbr template multicol prerendering? <!-- source output set-styles! route-cell header datalist tfoot s ins footer title h5 canvas param div option summary samp small style textarea loop-tpl* h4 head ol details col label picture rt when-dom h6 link page-load colgroup meter html-meta static-elements bdo --> b attribute? code dialog noframes do-watch noscript safe-nth h2 area br]]) (:require-macros [weathergen.cljs.macros :refer [with-bbox with-time formula-of]] [javelin.core :refer [with-let mx2 dosync cell= set-cell!= prop-cell cell-doseq formulet defc defc= macroexpand-all mx formula-of cell-let]] [hoplon.core :refer [definterval text elem+ cache-key with-timeout defelem+ when-tpl static sexp defelem elem def-values if-tpl cond-tpl with-page-load for-tpl with-dom case-tpl loop-tpl with-interval with-init!]]))















(def electron (js/require "electron"))

;; A vector of step maps, each of which has
;; keys :state, :id, :name, :parent, and :messages
(defc load-status
  [])

(defc loading? false)

(defn- update-for-id
  "Updates the entry in `status` whose id is `id`, by replacing it with
  the result of calling `f` on it."
  [status id f]
  (->> status
       (mapv #(if (= (:id %) id)
                (f %)
                %))))

(defmethod ipc/on-message "load-step-started"
  [_ _ {:keys [id name parent]}]
  (log/debug "load-step-started" id name parent)
  (when @loading?
    (swap! load-status conj {:state    :started
                             :id       id
                             :name     name
                             :messages []
                             :parent   (or parent ::root)})))

(defmethod ipc/on-message "load-step-succeeded"
  [_ _ {:keys [id]}]
  (log/debug "load-step-succeeded" id)
  (when @loading?
    (swap! load-status update-for-id id #(assoc % :state :succeeded))))

(defmethod ipc/on-message "load-step-warning"
  [_ _ {:keys [id message]}]
  (log/debug "load-step-warning" id message)
  (when @loading?
    (swap! load-status update-for-id id #(-> %
                                             (assoc :state :warning)
                                             (update :messages conj {:type :warning
                                                                     :text message})))))

(defmethod ipc/on-message "load-step-failed"
  [_ _ {:keys [id message]}]
  (log/debug "load-step-failed" id message)
  (when @loading?
    (swap! load-status update-for-id id #(-> %
                                             (assoc :state :failed)
                                             (update :messages (fn [messages]
                                                                 (if message
                                                                   (conj messages {:type :error
                                                                                   :text message})
                                                                   messages)))))))


(defmethod ipc/on-message "load-complete"
  [_ event]
  (log/debug "load-complete")
  (dosync
   (reset! loading? false)
   (swap! load-status update-for-id ::root #(-> %
                                                (assoc :state :succeeded)))))

(defmethod ipc/on-message "load-failed"
  [_ _ err]
  (log/debug "load-failed" err)
  (dosync
   (reset! loading? false)
   (swap! load-status #(->> %
                            (map (fn [step]
                                   (if (= (:state step) :started)
                                     (assoc step :state :failed)
                                     step)))))
   (swap! load-status update-for-id ::root #(-> %
                                                (assoc :state :failed)
                                                (update :messages (fn [messages]
                                                                    (if err
                                                                      (conj messages {:type :error
                                                                                      :text err})
                                                                      messages)))))))

(defn load-mission
  []
  (when-let [[path] (-> electron
                        .-remote
                        .-dialog
                        (.showOpenDialog
                         (clj->js {:title      "Select a campaign or tactical engagement file"
                                   :properties ["openFile"]
                                   :filters    [{:name       "Campaign file"
                                                 :extensions ["cam"]}
                                                {:name       "Tactical engagement file"
                                                 :extensions ["tac"]}]})))]
    (dosync
     (reset! loading? true)
     (reset! load-status [{:id    ::root
                           :state :started
                           :name  "Loading mission"}]))
    (ipc/send-to-main "open-mission" path)))

(defn load-briefing
  []
  (when-let [[path] (-> electron
                        .-remote
                        .-dialog
                        (.showOpenDialog
                         #js {:title "Select a campaign or tactical engagement file"
                              :openFile true
                              :filters #js [#js {:name "VMT Briefing"
                                                 :extensions #js ["vmtb"]}]}))]
    (dosync
     (reset! loading? true)
     (reset! load-status [{:id    ::root
                           :state :started
                           :name  "Loading briefing"}]))
    (ipc/send-to-main "open-briefing" path)))

(defn inline
  [props & args]
  [:div
   (assoc-in props [:style :display] "inline-block")
   args])

(defn- tree-item
  "Returns UI for an item in the tree."
  [{:keys [id messages state name] :as item}]
  (inline
   {}
   (inline
    {:style {:verticalAlign "top"}}
    (condp = state
      :succeeded (inline {:style {:font-weight    "bold"
                                  :color          "green"
                                  :width          (px 24)
                                  :vertical-align "top"}}
                         "✓")
      :failed    (inline {:style {:font-weight    "bold"
                                  :color          "red"
                                  :width          (px 24)
                                  :vertical-align "top"}}
                         "✗")
      :started   [:img {:style {:width          (px 24)
                                :vertical-align "top"
                                :margin-top     (px 5)}
                        :src   "images/spinner.gif"}]
      :warning   [:img {:style {:width          (px 24)
                                :vertical-align "top"
                                :margin-top     (px 5)
                                :margin-right   (px 3)}
                        :src   "images/warning.svg"}])
    (inline
     {:style {:white-space "normal"}}
     name))
   [:table
    {:style {:margin-left (px 30)}}
    [:tbody
     (for [message messages]
       (let [{:keys [type text]} message]
         [:tr
          [:td
           {:style {:vertical-align "top"}}
           (condp = type
             :warning [:img {:style {:width          (px 24)
                                     :vertical-align "middle"
                                     :margin-top     (px -5)}
                             :src   "images/warning.svg"}]
             :error   (inline {:style {:font-weight "bold"
                                       :color       "red"
                                       :width       (px 24)}}
                              "✗"))]
          [:td
           [:div
            {:style (if (= type :error)
                      {:white-space "pre"
                       :font-family "monospace"
                       :margin-top  (px 7)}
                      {:white-space "normal"})}
            text]]]))]]))

(defn- tree-level
  "Returns data structure describing tree at level having parent `parent`."
  ([items collapsed] (tree-level items collapsed nil))
  ([items collapsed parent]
   (for [item  items
         :let  [{:keys [id]} item]
         :when (= (:parent item) parent)]
     {:ui        (tree-item item)
      :id        id
      :expanded? (not (get collapsed id))
      :children  (when-let [kids (->> items
                                      (filter #(= (:parent %) id))
                                      seq)]
                   (tree-level items collapsed id))})))

(html
 (ui/head)
 (body
  :css {:margin "0"}
  (div
   :css {:display        "flex"
         :flex-direction "column"
         :height         "100vh"}
   (div
    :css {:padding "8px 8px 0"}
    (ui/titlebar))
   (let [button-style {:display "inline-block"
                       :margin  (px 5 10)}]
     (div
      :css {:font-size (pct 150)}
      (buttons/a-button
       :disabled? loading?
       :css button-style
       :click load-mission
       "Load mission (.cam/.tac)")
      (buttons/a-button
       :disabled? loading?
       :css button-style
       :click load-briefing
       "Load briefing (.vmtb)")))
   (with-let [elem (div
                    :css {:overflow-y "auto"
                          :overflow-x "auto"
                          :flex       1
                          :font-size  (pct 150)
                          :border     "solid 1px black"
                          :margin     (px 0 10 20 10)
                          :padding    (px 4 24 4 4)}
                    (comm/styled
                     :garden [:ol {:margin-top 0}]
                     (let [collapsed        (cell {})
                           load-status-tree (formula-of [load-status collapsed]
                                              (tree-level load-status collapsed))]
                       (trees/rtree load-status-tree
                                    (fn [id]
                                      (swap! collapsed
                                             update
                                             id
                                             not))))))]
     (add-watch load-status
                ::scroll-notifications
                (fn [_ _ _ _]
                  (with-timeout 0
                    ;; TODO: Scroll to the bottom of the element, not the top
                    (-> elem .-scrollTop (set! (.-scrollHeight elem))))))))))
