;; 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.renderer.pdf
  "A pdf renderer."
  (:require
   [app.browser :as bw]
   [app.common.data.macros :as dm]
   [app.common.logging :as l]
   [app.common.uri :as u]
   [app.config :as cf]
   [app.util.mime :as mime]
   [app.util.shell :as sh]
   [promesa.core :as p]))

(defn render
  [{:keys [file-id page-id share-id token scale type objects] :as params} on-object]
  (letfn [(prepare-options [uri]
            #js {:screen #js {:width bw/default-viewport-width
                              :height bw/default-viewport-height}
                 :viewport #js {:width bw/default-viewport-width
                                :height bw/default-viewport-height}
                 :locale "en-US"
                 :storageState #js {:cookies (bw/create-cookies uri {:token token})}
                 :deviceScaleFactor scale
                 :userAgent bw/default-user-agent})

          (prepare-uri [base-uri object-id]
            (let [params {:file-id file-id
                          :page-id page-id
                          :share-id share-id
                          :object-id object-id
                          :route "objects"}]
              (-> base-uri
                  (u/join "render.html")
                  (assoc :query (u/map->query-string params)))))

          (sync-page-size! [dom]
            (bw/eval! dom
                      (fn [elem]
                        ;; IMPORTANT: No CLJS runtime allowed. Use only JS
                        ;; primitives.  This runs in a context without access to
                        ;; cljs.core. Avoid any functions that transpile to
                        ;; cljs.core/* calls, as they will break in the browser
                        ;; runtime.

                        (let [width (.getAttribute ^js elem "width")
                              height (.getAttribute ^js elem "height")
                              style-node (let [node (.createElement js/document "style")]
                                           (.appendChild (.-head js/document) node)
                                           node)]
                          (set! (.-textContent style-node)
                                (dm/str "@page { size: " width "px " height "px; margin: 0; }\n"
                                        "html, body, #app { margin: 0; padding: 0; width: " width "px; height: " height "px; overflow: visible; }"))))))

          (render-object [page base-uri {:keys [id] :as object}]
            (p/let [uri  (prepare-uri base-uri id)
                    path (sh/tempfile :prefix "penpot.tmp.pdf." :suffix (mime/get-extension type))]
              (l/info :uri uri)
              (bw/nav! page uri)
              (p/let [dom (bw/select page (dm/str "#screenshot-" id))]
                (bw/wait-for dom)
                (sync-page-size! dom)
                (bw/screenshot dom {:full-page? true})
                (bw/sleep page 2000) ; the good old fix with sleep
                (bw/wait-for-fonts page)
                (bw/pdf page {:path path})
                path)))

          (render [base-uri page]
            (p/loop [objects (seq objects)]
              (when-let [object (first objects)]
                (p/let [path (render-object page base-uri object)]
                  (on-object (assoc object :path path))
                  (p/recur (rest objects))))))]

    (let [base-uri (-> (cf/get-internal-uri)
                       (u/ensure-path-slash))]
      (bw/exec! (prepare-options base-uri)
                (partial render base-uri)))))
