(ns dmr.views
  (:require
   [re-frame.core :as re-frame]
   [clojure.string :as string]
   [goog.string :as gstr]
   [dmr.styles :as styles]
   [dmr.subs :as subs]
   [dmr.events :as events]))

(defn prompt []
  (let [cmdline (re-frame/subscribe [::subs/cmdline])
        update-cmdline #(re-frame/dispatch
                         [::events/update-cmdline (-> % .-target .-value)])
        submit-cmd #((.preventDefault %)
                     (re-frame/dispatch [::events/submit-cmd]))]
    [:form {:class (styles/prompt-style)
            :on-submit submit-cmd}
     [:input {:value @cmdline
              :on-change update-cmdline
              :type "text"}]]))

(defn title []
  (let [name (re-frame/subscribe [::subs/name])]
    [:h1 "Welcome to " @name]))

; TODO: move this functionto a separate 'utils' module
(defn ordinal [n]
    (let [final-digit (mod n 10)
          suffix (case final-digit
                   0 "th"
                   1 "st"
                   2 "nd"
                   3 "rd"
                   "th")]
        (str n suffix)))

(defn- join-lines
    [coll]
    (string/join \newline coll))

(defn- comma-separated
    [coll]
    (string/join ", " coll))

(defn- parenthesize
    [s]
    (when s (str "(" s ")")))

(defn- property-list    [& props]
    (into
     [:ul {:class (styles/property-list)}]
     (->> props
          (partition 2)
          (filter second)
          (map #(vector
                 :li
                 [:span (-> % first)]
                 [:span (-> % second)])))))

(defn spell-panel [spell]
      [:article {:class (styles/spell-panel)}
       [:h1 (:name spell)]
       [:div
        [:span.level
         (-> spell :level ordinal)
         " level"]
        [:span.school
         "school of "
         (-> spell (get-in [:school :name]))]]
       [:ul {:class (styles/property-list)}
        ; Refactor these so that we get the "name" from the keyword by replacing
        ; '-' and '_' and capitalizing
        [:li [:span "Casting Time"]
             [:span (-> spell :casting_time)]]
        [:li [:span "Range"]
             [:span (-> spell :range)]]
        [:li [:span "Components"]
             [:span (comma-separated (-> spell :components))]
             [:span (parenthesize (-> spell :material))]]
        [:li [:span "Duration"]
             [:span (-> spell :duration)]]]
       [:section.description (-> spell :desc join-lines)]
       [:section.higher-level
        [:span "At higher levels"]
        [:span (-> spell :higher_level join-lines)]]])


(defn equipment-panel [item]
    [:article {:class (styles/equipment-panel)}
     [:h1 (item :name)]])

(defn weapon-panel [weapon]
 (let [weapon-kind (-> weapon :weapon_range string/lower-case)
       weapon-category (-> weapon :weapon_category string/lower-case)]
  [:article {:class (styles/weapon-panel)}
    [:h1 (weapon :name)]
    [:span.kind (string/join " " [weapon-category weapon-kind "weapon"])]
    (property-list
     ; 'two-handed-damage' is defined for *single-handed* weapons with the "versatile"
     ; property. Actual two-handed weapons have 'damage' only.
     :Damage
       (let [damage-type (-> weapon :damage :damage_type :name string/lower-case)
             damage (-> weapon :damage :damage_dice)
             two-handed-damage (some-> weapon :two_handed_damage :damage_dice)]
        (if two-handed-damage
          (str damage " " (parenthesize two-handed-damage) ", " damage-type)
          (str damage ", " damage-type)))
     :Range
       (when (= weapon-kind "ranged")
         (gstr/format "%d' (%d')" (-> weapon :range :normal) (-> weapon :range :long)))
     :Properties
       (comma-separated (->> weapon :properties (map :name) (map string/lower-case)))
     :Weight
       (spaced (weapon :weight) "lbs")
     :Cost
       (spaced (-> weapon :cost :quantity) (-> weapon :cost :unit)))]))

(defn error-message
    [e]
    [:div {:class (styles/error-message)} e])

(defn input [x] [:div.input x])

; TODO: refactor this using the 'match' macro
(defn output [x]
    [:div.output
     (cond
      (contains? x :spell) (spell-panel (x :spell))
      (contains? x :equipment) (weapon-panel (x :equipment))
      (contains? x :err) (error-message (x :err)))])


(defn history []
  (let [history (re-frame/subscribe [::subs/cmd-history])]
    (into
     [:ul {:class (styles/history-style)}]
     (map #(vector :li
                   (-> % :input input)
                   (-> % :output output))
          @history))))

(defn main-panel []
  [:div {:class (styles/screen)}
   [:div {:class (styles/main-panel)}
    (title) (history) (prompt)]])