              (valid-attrs? attrs :job))
       (letfn [(add-doc [doc-map]
                 (assoc-in doc-map [:obj] (delay (make-doc doc-map))))
-              (add-docs [m]
-                (update-in m [:docs] (fn [doc-list] (map add-doc  doc-list))))
-              (add-jobs [m]
-                (assoc-in m [:jobs] (for [d docs] (.. printer createPrintJob))))]
-        (-> spec add-docs add-jobs)))))
+              (add-docs [spec]
+                (update-in spec [:docs] (fn [doc-list] (map add-doc  doc-list))))
+              (add-jobs [spec]
+                (assoc-in spec [:jobs] (for [doc docs] (.. printer createPrintJob))))]
+        (-> spec add-docs add-jobs map->MultiJobSpec)))))
 
 ;; (defn make-job
 ;;   "Returns a JobSpec map that is the result of

      :author "Roberto Acevedo"}
  clj-print.core
  (:require [clj-print [doc-flavors :as flavors]
                       [listeners :as listeners]]
            [clojure.java [io :as io]]
            [clojure.pprint :refer [pprint]]
            [taoensso.timbre :as timbre])

          :attrs #{MediaTray/MAIN}
          :listener (listeners/basic-listener)})

(defprotocol IJobSpec
  (submit [this]) 
  (add-listener [this listener-fn]))

;; TODO: Handle exception scenarios
(defrecord JobSpec [doc printer job attrs]
  IJobSpec
  (submit [this]
    (if-let [{listener-fn :listener-fn} this]
      (add-listener this listener-fn))
    (let [doc-obj (make-doc doc)
          {:keys [^DocPrintJob job attrs]} this
          attr-set (make-set attrs :request)]
      (.. job (print doc-obj attr-set))))
  (add-listener [this listener-fn]
    (let [{:keys [^DocPrintJob job listener-fn]} this
          listener (listener-fn)]
      (doto job (.addPrintJobListener listener)))))

(defrecord MultiJobSpec [docs printer jobs attrs]
  IJobSpec
  (submit [this]
    (if-let [{listener-fn :listener-fn} this]
      (add-listener this listener-fn))
    (let [{:keys [docs jobs]} this
          doc-objs (map :obj docs)]
      (doall (map (fn [j d] (.. j (print @d (make-set attrs :request)))) jobs doc-objs)))) ;; TODO: reflection
  (add-listener [this listener-fn]
    (let [{:keys [^DocPrintJob jobs listener-fn]} this]
      (doseq [j jobs]
        (.. j (addPrintJobListener (listener-fn)))))))

;; TODO: SEE http://oobaloo.co.uk/clojure-from-callbacks-to-sequences!!!
(defmulti make-job
  "Dispatch on which key is present in the spec map."
  (fn [spec] (apply (some-fn #{:doc} #{:docs}) (keys spec))))

(defmethod make-job :doc [spec]
  (let [{:keys [^PrintService printer ^PrintJobListener listener doc attrs]} spec
        {doc-attrs :attrs} doc]
    (if (and (valid-attrs? doc-attrs :doc)
             (valid-attrs? attrs :job))
      (letfn [(add-doc [doc-map]
                (assoc-in doc-map [:doc :obj] (delay (make-doc doc-map))))
              (add-job [spec]
                (assoc-in spec [:job] (.. printer createPrintJob)))]
        (-> spec add-doc add-job map->JobSpec)))))

(defmethod make-job :docs [spec]
  (let [{:keys [^PrintService printer
