;; 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

(ns app.main.ui.viewer.comments
  (:require-macros [app.main.style :as stl])
  (:require
   [app.common.data :as d]
   [app.common.data.macros :as dm]
   [app.common.geom.matrix :as gmt]
   [app.common.geom.point :as gpt]
   [app.common.geom.rect :as grc]
   [app.common.geom.shapes :as gsh]
   [app.main.data.comments :as dcm]
   [app.main.data.events :as ev]
   [app.main.refs :as refs]
   [app.main.store :as st]
   [app.main.ui.comments :as cmt]
   [app.main.ui.components.dropdown :refer [dropdown]]
   [app.main.ui.icons :as i]
   [app.main.ui.workspace.comments :as wc]
   [app.util.dom :as dom]
   [app.util.i18n :as i18n :refer [tr]]
   [okulary.core :as l]
   [rumext.v2 :as mf]))

(mf/defc comments-menu
  {::mf/props :obj
   ::mf/memo true}
  []
  (let [state           (mf/deref refs/comments-local)
        cmode           (:mode state)
        cshow           (:show state)
        show-sidebar?   (:show-sidebar? state false)

        show-dropdown?  (mf/use-state false)
        toggle-dropdown (mf/use-fn #(swap! show-dropdown? not))
        hide-dropdown   (mf/use-fn #(reset! show-dropdown? false))

        update-mode
        (mf/use-fn
         (fn [event]
           (let [mode (-> (dom/get-current-target event)
                          (dom/get-data "value")
                          (keyword))]
             (st/emit! (dcm/update-filters {:mode mode})))))

        update-show
        (mf/use-fn
         (fn [event]
           (let [mode (-> (dom/get-current-target event)
                          (dom/get-data "value")
                          (keyword))
                 mode (if (= :pending mode) :all :pending)]
             (st/emit! (dcm/update-filters {:show mode})))))

        update-options
        (mf/use-fn
         (fn [event]
           (let [mode (-> (dom/get-current-target event)
                          (dom/get-data "value")
                          (parse-boolean))]
             (st/emit! (dcm/update-options {:show-sidebar? (not mode)})))))]

    [:div {:class (stl/css :view-options)
           :on-click toggle-dropdown}
     [:span {:class (stl/css :dropdown-title)} (tr "labels.comments")]
     [:span {:class (stl/css :icon-dropdown)} i/arrow]

     [:& dropdown {:show @show-dropdown?
                   :on-close hide-dropdown}
      [:ul {:class (stl/css :dropdown)}

       [:li {:class (stl/css-case
                     :dropdown-element true
                     :selected (or (= :all cmode) (nil? cmode)))
             :data-value "all"
             :on-click update-mode}
        [:span {:class (stl/css :label)} (tr "labels.show-all-comments")]
        (when (or (= :all cmode) (nil? cmode))
          [:span {:class (stl/css :icon)} i/tick])]

       [:li {:class (stl/css-case
                     :dropdown-element true
                     :selected (= :yours cmode))
             :data-value "yours"
             :on-click update-mode}
        [:span {:class (stl/css :label)} (tr "labels.show-your-comments")]
        (when (= :yours cmode)
          [:span {:class (stl/css :icon)}
           i/tick])]

       [:li {:class (stl/css :separator)}]

       [:li {:class (stl/css-case
                     :dropdown-element true
                     :selected (= :pending cshow))
             :data-value (d/name cshow)
             :on-click update-show}
        [:span {:class (stl/css :label)} (tr "labels.hide-resolved-comments")]
        (when  (= :pending cshow)
          [:span {:class (stl/css :icon)}
           i/tick])]

       [:li {:class (stl/css :separator)}]

       [:li {:class (stl/css-case
                     :dropdown-element true
                     :selected show-sidebar?)
             :data-value (dm/str show-sidebar?)
             :on-click update-options}
        [:span {:class (stl/css :label)} (tr "labels.show-comments-list")]
        (when show-sidebar?
          [:span {:class (stl/css :icon)} i/tick])]]]]))


(defn- update-thread-position
  [positions {:keys [id] :as thread}]
  (if-let [data (get positions id)]
    (-> thread
        (assoc :position (:position data))
        (assoc :frame-id (:frame-id data)))
    thread))

(mf/defc comments-layer
  {::mf/props :obj}
  [{:keys [zoom file users frame page]}]
  (let [profile        (mf/deref refs/profile)
        local          (mf/deref refs/comments-local)

        open-thread-id (:open local)
        page-id        (:id page)
        file-id        (:id file)
        frame-id       (:id frame)

        tpos-ref     (mf/with-memo [page-id]
                       (-> (l/in [:pages page-id :options :comment-threads-position])
                           (l/derived refs/viewer-data)))

        positions    (mf/deref tpos-ref)
        threads-map  (mf/deref refs/comment-threads)

        frame-corner (mf/with-memo [frame]
                       (-> frame :points grc/points->rect gpt/point))

        modifier1    (mf/with-memo [frame-corner]
                       (-> (gmt/matrix)
                           (gmt/translate (gpt/negate frame-corner))))

        modifier2    (mf/with-memo [frame-corner]
                       (-> (gpt/point frame-corner)
                           (gmt/translate-matrix)))

        threads      (mf/with-memo [threads-map positions frame local profile]
                       (->> (vals threads-map)
                            (map (partial update-thread-position positions))
                            (filter #(= (:frame-id %) (:id frame)))
                            (dcm/apply-filters local profile)
                            (filter (fn [{:keys [position]}]
                                      (gsh/has-point? frame position)))))

        on-bubble-click
        (mf/use-fn
         (mf/deps open-thread-id)
         (fn [{:keys [id] :as thread}]
           (st/emit! (if (= open-thread-id id)
                       (dcm/close-thread)
                       (-> (dcm/open-thread thread)
                           (with-meta {::ev/origin "viewer"}))))))

        on-click
        (mf/use-fn
         (mf/deps open-thread-id zoom page-id file-id modifier2)
         (fn [event]
           (dom/stop-propagation event)
           (if (some? open-thread-id)
             (st/emit! (dcm/close-thread))
             (let [event    (dom/event->native-event event)
                   position (-> (dom/get-offset-position event)
                                (update :x #(/ % zoom))
                                (update :y #(/ % zoom))
                                (gpt/transform modifier2))
                   params   {:position position
                             :page-id (:id page)
                             :file-id (:id file)}]
               (st/emit! (dcm/create-draft params))))))

        on-draft-cancel
        (mf/use-fn #(st/emit! (dcm/close-thread)))

        on-draft-submit
        (mf/use-fn
         (mf/deps frame-id modifier2)
         (fn [draft]
           (let [params (assoc draft :frame-id frame-id)]
             (st/emit! (dcm/create-thread-on-viewer params)
                       (dcm/close-thread)))))]

    [:div {:class (stl/css :comments-section)
           :on-click on-click}
     [:div {:class (stl/css :viewer-comments-container)}
      [:div {:class (stl/css :threads)}
       (for [item threads]
         [:& cmt/thread-bubble
          {:thread item
           :position-modifier modifier1
           :zoom zoom
           :on-click on-bubble-click
           :open? (= (:id item) (:open local))
           :key (:seqn item)
           :origin :viewer}])

       (when-let [thread (get threads-map open-thread-id)]
         [:& cmt/thread-comments
          {:thread thread
           :position-modifier modifier1
           :users users
           :zoom zoom}])

       (when-let [draft (:draft local)]
         [:& cmt/draft-thread
          {:draft draft
           :position-modifier modifier1
           :on-cancel on-draft-cancel
           :on-submit on-draft-submit
           :zoom zoom}])]]]))

(mf/defc comments-sidebar
  [{:keys [users frame page]}]
  (let [profile     (mf/deref refs/profile)
        local      (mf/deref refs/comments-local)
        threads-map (mf/deref refs/comment-threads)
        threads     (->> (vals threads-map)
                         (dcm/apply-filters local profile)
                         (filter (fn [{:keys [position]}]
                                   (gsh/has-point? frame position))))]
    [:aside {:class (stl/css :comments-sidebar)}
     [:div {:class (stl/css :settings-bar-inside)}
      [:& wc/comments-sidebar {:from-viewer true :users users :threads threads :page-id (:id page)}]]]))
