(in-package #:cl-user)

(defpackage #:cl-deck-builder2.i18n
  (:use :cl)
  (:import-from :gettext #:*current-locale*)
  (:export
   #:_
   #:n_
   #:reload-translations
   #:list-loaded-locales
   #:*current-locale*
   #:set-locale
   #:with-locale
   #:update-djula.pot
   #:generate-templates-list)
  (:documentation "Internationalization Utilities"))

(in-package #:cl-deck-builder2.i18n)

(defun setup-gettext ()
  (setf djula:*translation-backend* :gettext
        (gettext:textdomain) "cl-deck-builder2")
  (gettext:setup-gettext #.*package* "cl-deck-builder2"))

(setup-gettext)

(defun reload-translations ()
  (format *debug-io* "~%Reading all *.mo files...")
  ;; Clear gettext's cache
  (clrhash gettext::*catalog-cache*)
  (setup-gettext)
  (gettext:preload-catalogs
   ;; Tell gettext where to find the .mo files
   #.(asdf:system-relative-pathname :cl-deck-builder2 "locale/"))
  ;; What about this?
  (setf djula::*gettext-domain* "cl-deck-builder2"))

;; Only preload the translations into the image if we're not deployed yet.
(unless (deploy:deployed-p)
  (reload-translations))

;; Run this when developping to reload the translations
#+ (or)
(progn
  ;; Clear gettext's cache
  (clrhash gettext::*catalog-cache*)
  (gettext:preload-catalogs
   ;; Tell gettext where to find the .mo files
   #.(asdf:system-relative-pathname :cl-deck-builder2 "locale/")))

;; Run this to see the list of loaded message for a specific locale
#+ (or)
(gettext::catalog-messages
 (gethash '("es_ES" :LC_MESSAGES "cl-deck-builder2")
	  gettext::*catalog-cache*))

;; Test the translation of a string
#+ (or)
(with-locale ("es_ES")
  (_ "Please login to continue"))


#+ (or)
(set-locale "es_ES")

#+ (or)
*current-locale*

(defun list-loaded-locales ()
  "Get the list of locales loaded in gettext's cache."
  (remove-duplicates
   (mapcar #'first
           (alexandria:hash-table-keys
            gettext::*catalog-cache*))
   :test #'string=))

(defun set-locale (locale)
  "Setf gettext:*current-locale* and djula:*current-language* if LOCALE seems valid."
  ;; It is valid to set the locale to nil.
  (when (and locale
             (not (member locale (list-loaded-locales)
                          :test 'string=)))
    (error "Locale not valid or not available: ~s" locale))
  (setf *current-locale* locale
        djula:*current-language* locale))

(defmacro with-locale ((locale) &body body)
  "Calls BODY with gettext:*current-locale* and djula:*current-language* set to LOCALE."
  `(let (*current-locale* djula:*current-language*)
     (set-locale ,locale)
     ,@body))

;; (trace _)

;; (trace djula:translate gettext:gettext* gettext::lookup)

;; (_ "hi")

#|
This could technically be just
(mapcan #'djula.locale:file-template-translate-strings
        (djula:list-asdf-system-templates :cl-deck-builder2 "templates"))

But I (fstamour) made it just a bit more complex in order to keep track of the source (just the
filename) of each translatable strings. Hence why the hash-table returned is named `locations`.
|#
(defun extract-translate-strings ()
  "Extract all {_ ... _} string from the djula templates."
  (loop
    :with locations = (make-hash-table :test 'equal)
    :for path :in (djula:list-asdf-system-templates :cl-deck-builder2 "templates")
    :for strings = (djula.locale:file-template-translate-strings path)
    :do (loop :for string :in strings
              :unless (gethash string locations)
                :do (setf (gethash string locations) path))
    :finally (return locations)))

(defun update-djula.pot ()
  "Update djula.pot from *.html files."
  (with-open-file (s (asdf:system-relative-pathname
                      :cl-deck-builder2 "locale/templates/LC_MESSAGES/djula.pot")
                     :direction :output
                     :if-exists :supersede
                     :if-does-not-exist :create)
    (let* ((locations (extract-translate-strings))
           (strings (alexandria:hash-table-keys locations)))
      (loop
        :for string :in strings
        :for location = (gethash string locations)
        :do
           (format s "~%#: ~a~%#, lisp-format~%msgid ~s~%msgstr \"\" ~%"
                   (enough-namestring
                    location
                    (asdf:system-source-directory :cl-deck-builder2))
                   string)))))

;; TODO UPDATE-STATIC-TEMPLATE-FILES
(defun generate-templates-list ()
  "Generate a list of all files in the \"templates/\" subdirectory, in the form of (:STATIC-FILE \"file...\"). Currently you have to copy/paste this into the ASDF file whenever you add a template."
  (let* ((lst '())
         (root (asdf:system-relative-pathname :cl-deck-builder2 "templates/")))
    (cl-fad:walk-directory
     root
     (lambda (name)
       (push (list :static-file (namestring (uiop:enough-pathname name root))) lst)))
    lst))
