;; This Source Code Form is subject to the terms of the Mozilla Public
;; License, v. 2.0. If a copy of the MPL was not distributed with this
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
;;
;; Copyright (c) KALEIDOS INC Sucursal en España SL

(ns app.main.ui.modal
  (:require-macros [app.main.style :as stl])
  (:require
   [app.common.data.macros :as dm]
   [app.main.data.modal :as modal]
   [app.main.store :as st]
   [app.main.ui.hooks :as hooks]
   [app.util.dom :as dom]
   [app.util.keyboard :as k]
   [goog.events :as events]
   [okulary.core :as l]
   [rumext.v2 :as mf])
  (:import goog.events.EventType))

(defn- on-esc-clicked
  [event allow-click-outside]
  (when (and (k/esc? event) (not allow-click-outside))
    (dom/stop-propagation event)
    (st/emit! (modal/hide))))

(defn- on-pop-state
  [event]
  (dom/prevent-default event)
  (dom/stop-propagation event)
  (st/emit! (modal/hide))
  (.forward js/history))

(defn- on-click-outside
  [event wrapper-ref type allow-click-outside]
  (let [wrapper (mf/ref-val wrapper-ref)
        current (dom/get-target event)]

    (when (and wrapper
               (not allow-click-outside)
               (not (.contains wrapper current))
               (not (= type (keyword (dom/get-data current "allow-click-modal"))))
               (= (.-button event) 0))
      (dom/stop-propagation event)
      (dom/prevent-default event)
      (st/emit! (modal/hide)))))

(mf/defc modal-wrapper*
  {::mf/wrap [mf/memo]}
  [{:keys [data]}]
  (let [wrapper-ref    (mf/use-ref nil)
        components     (mf/deref modal/components)

        allow-click-outside (:allow-click-outside data)

        handle-click-outside
        (fn [event]
          (on-click-outside event wrapper-ref (:type data) allow-click-outside))

        handle-keydown
        (fn [event]
          (on-esc-clicked event allow-click-outside))]

    (mf/with-effect [allow-click-outside]
      (let [keys [(events/listen js/window   EventType.POPSTATE    on-pop-state)
                  (events/listen js/document EventType.KEYDOWN     handle-keydown)

                  ;; Changing to js/document breaks the color picker
                  (events/listen (dom/get-root) EventType.POINTERDOWN handle-click-outside)

                  (events/listen js/document EventType.CONTEXTMENU handle-click-outside)]]
        (fn []
          (run! events/unlistenByKey keys))))

    (when-let [component (get components (:type data))]
      [:div {:ref wrapper-ref
             :class (stl/css :modal-wrapper)}
       (mf/element component (:props data))])))

(def ^:private ref:modal
  (l/derived ::modal/modal st/state))

(mf/defc modal-container*
  []
  (let [container (hooks/use-portal-container :modal)]
    (when-let [modal (mf/deref ref:modal)]
      (mf/portal
       (mf/html [:> modal-wrapper* {:data modal :key (dm/str (:id modal))}])
       container))))
