 (require '[clojure.string :as string])
 (use 'clojure.pprint)
 
-;; you can get the field from the root value
-;; by calling view on it with the lens
+;; you can get the field from the root value by calling view on it
+;; with the lens:
 
 (pprint (lens/view user first-name))
 ;; => "tobias"
 
-;; you can also update the field with the update function
+;; you can also update the field with the update function:
 
 (pprint (lens/update user first-name string/capitalize))
 ;; => {:last-name "funke",
 ;;     :first-name "Tobias", <-- capitalized
 ;;     :email "tobias.funke@gmail.com"}
 
-;; you can also write to the field with lens/write
-;; this is the same as doing (update root lens (fn [_] new-value))
+;; you can also write to the field with `lens/write`: this is the same
+;; as doing `(update root lens (fn [_] new-value))`.
 
 (pprint (lens/write user first-name "Newname"))
 ;; => {:last-name "funke",
 ;;     :first-name "Newname",
 ;;     :email "tobias.funke@gmail.com"}
 
-;; you can also define lenses using lens/getter-setter-lens
-;; which makes a lens from a getter and a setter function
+;; you can also define lenses using `lens/getter-setter-lens`
+;; which makes a lens from a getter and a setter function:
 
 (def first-name2
   (lens/getter-setter-lens
    (fn [user] (:first-name user))
    (fn [user new-first-name] (assoc user :first-name new-first-name))))
 
-;; this will make you the same lens
+;; this will give you the same lens:
 (= (lens/write user first-name  "Newname")
    (lens/write user first-name2 "Newname"))
 
 ;; since using keywords to reference fields in maps is so common
-;; you can use them directly as lenses
+;; you can use them directly as lenses:
 
 (= (lens/write user first-name  "Newname")
    (lens/write user first-name2 "Newname")

;; =====================================================
;; iso-lens

;; the postcode in the nested structure is a string atm
;; but we can write a lens that lets us treat it as a number

(def str->num (lens/iso-lens #(Integer/parseInt %) str))

;; iso-lens takes 2 functions one to convert it to a integer in this case
;; and another one to convert it back to a string

(lens/update user2 [:address :postcode str->num] inc)
;; {:last-name "funke",
;;  :first-name "tobias",
;;  :address {:address_line "sudden vale street 23",
;;            :postcode "56322",
;;            :city "bluethwill"}}

;; the result after incrementing the postcode is still a string

;; =====================================================
;; extract

;; extract is a function that lets you transform a structure
;; to a map of a different shape and later write back changes
;; that are done to the new map

(def user-view
  {:first-name   :first-name


(def xml-catalog (parse-xml-str xml-string))

;; First task! Something simple: let's change the name of all the
;; authors to Tobias Funke in this xml document.

;; This could be accomplished by digging down to the right elements
;; and updating them appropriately... But a lazier way would be to
;; just walk all of the tree and replace the contents of the elements
;; with the author tag.

;; We can use `tree/pre-tree` to accomplish this:
;; it is a function that takes a traversal of all sub nodes of a node
;; and gives you a traversal of all nodes in the tree.

;; EXAMPLE:

(def xml-nodes (tree/pre-tree
                [:content
                 (travers/filtered map?) ; we don't want to consider
                                         ; the strings in the tree as
                                         ; nodes so we filter them
                                         ; here
                 ]))

;; `xml-nodes` defined above using `tree/pre-tree` is a lens/traversal
;; that lets you view and modify nodes of an XML tree in a pre-walk
;; fashion. (There is also a corresponding `tree/post-tree` that lets
;; you do it in a post-walk fashion)

;; ok so let's shrink the nodes we are working with and consider only
;; the nodes with the "author" tag:

(def authors [xml-nodes (travers/shrink #(= (:tag %) :author))])

;; then change the content of these nodes to "Tobias Funke":

(pprint
 (-> xml-catalog
     (lens/write [authors :content] ["Tobias Funke"])))

;; Perfect! All the authors are now Tobias Funke!
