(ns frontend.modules.outliner.op
  "Build outliner ops"
  (:require [datascript.impl.entity :as de]))

(def ^:private ^:dynamic *outliner-ops*
  "Stores outliner ops that are generated by the following calls"
  nil)

(defn- op-transact!
  [fn-var & args]
  {:pre [(var? fn-var)]}
  (when (nil? *outliner-ops*)
    (throw (js/Error. (str (:name (meta fn-var)) " is not used in (transact! ...)"))))
  (let [result (apply @fn-var args)]
    (conj! *outliner-ops* result)
    result))

(defn save-block
  [block opts]
  (when-let [block' (if (de/entity? block)
                      (assoc (.-kv ^js block) :db/id (:db/id block))
                      block)]
    [:save-block [block' opts]]))

(defn insert-blocks
  [blocks target-block opts]
  (let [id (:db/id target-block)]
    [:insert-blocks [blocks id opts]]))

(defn delete-blocks
  [blocks opts]
  (let [ids (map :db/id blocks)]
    [:delete-blocks [ids opts]]))

(defn move-blocks
  [blocks target-block sibling?]
  (let [ids (map :db/id blocks)
        target-id (:db/id target-block)]
    [:move-blocks [ids target-id sibling?]]))

(defn move-blocks-up-down
  [blocks up?]
  (let [ids (map :db/id blocks)]
    [:move-blocks-up-down [ids up?]]))

(defn indent-outdent-blocks
  [blocks indent? & {:as opts}]
  (let [ids (map :db/id blocks)]
    [:indent-outdent-blocks [ids indent? opts]]))

(defn save-block!
  [block & {:as opts}]
  (op-transact! #'save-block block opts))

(defn insert-blocks!
  [blocks target-block opts]
  (op-transact! #'insert-blocks blocks target-block opts))

(defn delete-blocks!
  [blocks opts]
  (op-transact! #'delete-blocks blocks opts))

(defn move-blocks!
  [blocks target-block sibling?]
  (op-transact! #'move-blocks blocks target-block sibling?))

(defn move-blocks-up-down!
  [blocks up?]
  (op-transact! #'move-blocks-up-down blocks up?))

(defn indent-outdent-blocks!
  [blocks indent? & {:as opts}]
  (op-transact! #'indent-outdent-blocks blocks indent? opts))
