#|

Conceptually, The Label Maker is very similar to the DRAW component of The Deck Builder.

We currently support Avery 5160 and ULINE S-20246 labels.

|#

(in-package #:cl-user)

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; This package doesn't touch the DB ... yet
(defpackage #:cl-deck-builder2.models.label-maker
  (:use :cl)
  (:import-from #:cl-deck-builder2.toolkit
                #:latex-escape)
  (:export #:latex-label
           #:latex-label-page
           #:latex-label-uline-s-20247
           #:latex-label-avery-5160
           #:render-page))

(in-package #:cl-deck-builder2.models.label-maker)

(defclass latex-label ()
  ((barcode :accessor latex-label-barcode
            :initform nil
            :initarg :barcode)
   (description :accessor latex-label-description
                :initform nil
                :initarg :description)
   (price :accessor latex-label-price
          :initform nil
          :initarg :price))
  (:documentation "An individual LaTeX label. Encapsulate metadata for a particular label."))

(defclass latex-label-page ()
  ((;; List of LATEX-LABEL content
    labels :accessor latex-label-page-labels
    :initarg :page-labels
    :initform '())
   (;; Number of Labels this page thinks it has
    labels-length :accessor latex-label-page-labels-length
    :initarg :labels-length
    :initform 0)
   ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
   ;; Templates
   (;; Template for this page
    label-template :accessor latex-label-label-template
    :initform nil
    :initarg :label-template)
   (;; Header template for this page
    header-template :accessor latex-label-header-template
    :initform #P"latex/header.tex"
    :initarg :header-template))
  (:documentation "Class for a LaTeX Label Page, which encapsulates label information and additional output templating information."))

(defclass latex-label-uline-s-20247 (latex-label-page)
  ((labels-length :initform 32)
   (label-template :initform #P"latex/ULINE-S-20247.tpl.tex")))

(defclass latex-label-avery-5160 (latex-label-page)
  ((labels-length :initform 30)
   (label-template :initform #P"latex/Avery-5160.tpl.tex")))

(defmethod initialize-instance :after ((page latex-label-page) &rest initargs &key csv &allow-other-keys)
  (declare (ignore initargs))
  (when csv
    (initialize-with-csv page csv)))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defmethod add-label ((page latex-label-page) (label latex-label))
  "Add a LABEL to PAGE."
  (push label (latex-label-page-labels page)))

(defmethod initialize-with-csv ((page latex-label-page) pathspec)
  "Create LATEX-LABEL information for this LATEX-LABEL-PAGE by reading CSV information from PATHSPEC."
  (let ((csv (cl-csv:read-csv pathspec)))
    (loop for i from 1 upto (latex-label-page-labels-length page) do
      (destructuring-bind (label-no description price barcode)
          (elt csv i)
        (declare (ignore label-no))
        (when (> (length description) 0)
          (barcode-png barcode)
          (let ((label (ignore-errors
                        (make-instance 'latex-label
                                       :barcode barcode
                                       :description (latex-escape description)
                                       :price (cl-ppcre:regex-replace "\\$" price "\\\\$")))))
            (add-label page label)))))))

(defmethod render-page ((page latex-label-page))
  "Use Djula to render the LATEX-LABEL-LABEL-TEMPLATE."
  (cl-deck-builder2.view:render (latex-label-label-template page)
                                (list :labels (latex-label-page-labels page))))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defun label-info (&optional t-or-nil)
  (let ((arg (if t-or-nil "true" "false")))
    (format nil "\LabelGrid~a\LabelInfo~a%%~%" arg arg)))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; TODO (defclass barcode () ())
(defun barcode-png (barcode &optional (directory #P"/tmp/labels/") (size "1x30"))
  (ensure-directories-exist directory)
  (let* ((barcode (etypecase barcode
                    (string barcode)
                    (integer (princ-to-string barcode))))
         (output (merge-pathnames
                  (make-pathname :name barcode :type "png")
                  (probe-file directory))))
    (unless (probe-file output)
      (and
       (> (length barcode) 0)
       (multiple-value-bind (stdout stderr rc)
           (inferior-shell:run
            `("ZXingWriter" -size ,size
                            ,(case (length barcode)
                               ;; 12 and 11 digit barcodes for products in store on the shelves usually - UPC-A
                               ((12 11) 'UPC-A)
                               ;; 10 digit barcodes for "fake" Amazon ASIN "X0037THWV7"
                               (10 'Code128)
                               ;; Not sure why we have UPC-E in here
                               ;; but presumbly we have encountered
                               ;; products with a 6 digit barcode...
                               (6 'UPC-E)
                               ;; Treat everything else as 13 digit
                               ;; international EAN-13 barcodes like
                               ;; stuff from Pokemon Stores:
                               ;; 8206508090648
                               (t 'EAN-13))
                            ,barcode
                            ,output))
         (declare (ignore stdout stderr))
         (eq rc 0))
       (probe-file output)))))
