(ns frontend.extensions.latex
  (:require [frontend.config :as config]
            [frontend.handler.plugin :refer [hook-extensions-enhancers-by-key] :as plugin-handler]
            [frontend.loader :as loader]
            [frontend.ui :as ui]
            [frontend.util :as util]
            [goog.dom :as gdom]
            [promesa.core :as p]
            [rum.core :as rum]))

;; TODO: extracted to a rum mixin
(defn loaded? []
  js/window.katex)

(defonce *loading? (atom false))

(defn render!
  [state]
  (let [[s _ display?] (:rum/args state)
        id (:id state)]
    (try
      (when-let [elem (gdom/getElement id)]
        (js/katex.render s elem
                         #js {:displayMode display?
                              :throwOnError false
                              :strict false}))

      (catch :default e
        (js/console.error e)))))

(defn- load-and-render!
  [state]
  (if (loaded?)
    (do
      (reset! *loading? false)
      (render! state))
    (when-not @*loading?
      (reset! *loading? true)
      (loader/load "./js/katex.min.js"
                   (fn []
                     (loader/load "./js/mhchem.min.js"
                                  (fn []
                                    (-> (when-let [enhancers (and config/lsp-enabled?
                                                                  (seq (hook-extensions-enhancers-by-key :katex)))]
                                          (for [{f :enhancer} enhancers]
                                            (when (fn? f) (f js/window.katex))))
                                        (p/all)
                                        (p/finally (fn []
                                                     (reset! *loading? false)
                                                     (render! state)))))))
                   state))))

(defn- state-&-load-and-render!
  [state]
  (load-and-render! state)
  state)

(rum/defcs latex < rum/reactive
  {:init (fn [state]
           (assoc state :id (str (random-uuid))))
   :did-mount  state-&-load-and-render!
   :did-update state-&-load-and-render!}
  [state s block? _display?]
  (let [id (:id state)
        loading? (rum/react *loading?)]
    (if loading?
      (ui/loading)
      (let [element (if block?
                      :div.latex
                      :span.latex-inline)]
        [element {:id    id
                  :class "initial"}
         [:span.opacity-0 s]]))))

(defn html-export
  [s block? display?]
  (let [element (if block?
                  :div.latex
                  :span.latex-inline)]
    [element (if (or block? display?)
               (util/format "$$%s$$" s)
               ;; inline
               (util/format "$%s$" s))]))
