;; 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.workspace.shapes
  "A workspace specific shapes wrappers.

  Shapes that has some peculiarities are defined in its own
  namespace under app.ui.workspace.shapes.* prefix, all the
  others are defined using a generic wrapper implemented in
  common."
  (:require
   [app.common.data :as d]
   [app.common.data.macros :as dm]
   [app.common.files.helpers :as cfh]
   [app.common.geom.rect :as grc]
   [app.common.geom.shapes :as gsh]
   [app.common.uuid :as uuid]
   [app.main.ui.context :as ctx]
   [app.main.ui.shapes.circle :as circle]
   [app.main.ui.shapes.image :as image]
   [app.main.ui.shapes.rect :as rect]
   [app.main.ui.shapes.text.fontfaces :as ff]
   [app.main.ui.workspace.shapes.bool :as bool]
   [app.main.ui.workspace.shapes.common :as common]
   [app.main.ui.workspace.shapes.frame :as frame]
   [app.main.ui.workspace.shapes.group :as group]
   [app.main.ui.workspace.shapes.path :as path]
   [app.main.ui.workspace.shapes.svg-raw :as svg-raw]
   [app.main.ui.workspace.shapes.text :as text]
   [app.util.object :as obj]
   [rumext.v2 :as mf]))

(declare shape-wrapper)
(declare group-wrapper)
(declare svg-raw-wrapper)
(declare bool-wrapper)
(declare nested-frame-wrapper)
(declare root-frame-wrapper)

(def circle-wrapper (common/generic-wrapper-factory circle/circle-shape))
(def image-wrapper (common/generic-wrapper-factory image/image-shape))
(def rect-wrapper (common/generic-wrapper-factory rect/rect-shape))

(defn- make-is-frame-overlap
  [vbox objects]
  (fn [shape]
    (let [bounds
          (if (dm/get-prop shape :show-content)
            (let [children (->> (cfh/get-children-ids objects (dm/get-prop shape :id))
                                (map (d/getf objects)))]
              (gsh/shapes->rect (cons shape children)))
            (dm/get-prop shape :selrect))]
      (grc/overlaps-rects? vbox bounds))))

(mf/defc root-shape
  "Draws the root shape of the viewport and recursively all the shapes"
  {::mf/wrap [mf/memo]
   ::mf/wrap-props false}
  [props]
  (let [objects             (obj/get props "objects")
        active-frames       (obj/get props "active-frames")
        disable-thumbnails  (obj/get props "disable-thumbnails")
        shapes              (cfh/get-immediate-children objects)
        vbox                (mf/use-ctx ctx/current-vbox)

        frame-overlap? (mf/with-memo [vbox objects]
                         #(make-is-frame-overlap vbox objects))

        shapes         (mf/with-memo [shapes vbox frame-overlap?]
                         (cond->> shapes
                           (some? vbox)
                           (filter frame-overlap?)))]

    [:g {:id (dm/str "shape-" uuid/zero)}
     [:& (mf/provider ctx/active-frames) {:value active-frames}
      ;; Render font faces only for shapes that are part of the root
      ;; frame but don't belongs to any other frame.
      (let [xform (comp
                   (remove cfh/frame-shape?)
                   (mapcat #(cfh/get-children-with-self objects (:id %))))]
        [:> ff/fontfaces-style* {:shapes (into [] xform shapes)}])

      [:g.frame-children
       (for [shape shapes]
         (let [thumbnail?
               (and (not disable-thumbnails)
                    (not (contains? active-frames (dm/get-prop shape :id))))]
           [:g.ws-shape-wrapper {:key (dm/str (dm/get-prop shape :id))}
            (if ^boolean (cfh/frame-shape? shape)
              [:& root-frame-wrapper
               {:shape shape
                :objects objects
                :thumbnail? thumbnail?}]
              [:& shape-wrapper {:shape shape}])]))]]]))

(defn- render-shape-content
  [shape thumbnail?]
  (let [shape-type    (dm/get-prop shape :type)
        rawsvg?       (= :svg-raw shape-type)
        wrapper-elem  (if ^boolean rawsvg? mf/Fragment "g")
        wrapper-props (if ^boolean rawsvg?
                        #js {}
                        #js {:className "workspace-shape-wrapper"})
        props         #js {:shape shape :thumbnail? thumbnail?}]
    (when (and (some? shape)
               (not ^boolean (:hidden shape)))
      (mf/html
       [:> wrapper-elem wrapper-props
        (case shape-type
          :path    [:> path/path-wrapper* props]
          :text    [:> text/text-wrapper props]
          :group   [:> group-wrapper props]
          :rect    [:> rect-wrapper props]
          :image   [:> image-wrapper props]
          :circle  [:> circle-wrapper props]
          :svg-raw [:> svg-raw-wrapper props]
          :bool    [:> bool-wrapper props]
          :frame   [:> nested-frame-wrapper props]
          nil)]))))

(mf/defc root-frame-shape-wrapper
  {::mf/wrap [#(mf/memo' % common/check-shape-props)]
   ::mf/wrap-props false}
  [props]
  (let [shape         (unchecked-get props "shape")
        shape-id      (dm/get-prop shape :id)
        active-frames (mf/use-ctx ctx/active-frames)
        thumbnail?    (and (some? active-frames)
                           (not (contains? active-frames shape-id)))]
    (render-shape-content shape thumbnail?)))

(mf/defc shape-wrapper
  {::mf/wrap [#(mf/memo' % common/check-shape-props)]
   ::mf/wrap-props false}
  [props]
  (let [shape (unchecked-get props "shape")]
    (if ^boolean (cfh/root-frame? shape)
      [:> root-frame-shape-wrapper props]
      (render-shape-content shape false))))

(def group-wrapper (group/group-wrapper-factory shape-wrapper))
(def svg-raw-wrapper (svg-raw/svg-raw-wrapper-factory shape-wrapper))
(def bool-wrapper (bool/bool-wrapper-factory shape-wrapper))
(def nested-frame-wrapper (frame/nested-frame-wrapper-factory shape-wrapper))
(def root-frame-wrapper (frame/root-frame-wrapper-factory shape-wrapper))
