    [yada.util :refer (parse-csv)]
    [yada.negotiation :as negotiation]))
 
+(defn best
+  "Pick the best item from a collection. Since we are only interested in
+  the best, we can avoid sorting the entire collection, which could be
+  inefficient with large collections. The best element is selected by
+  comparing items. An optional comparator can be given."
+  ([coll]
+   (best compare coll))
+  ([^java.util.Comparator comp coll]
+   (reduce
+    (fn [x y]
+      (case (. comp (compare x y)) (0 1) x -1 y))
+    coll)))
+
+(defn best-by
+  "Pick the best item from a collection. Since we are only interested in
+  the best, we can avoid sorting the entire collection, which could be
+  inefficient with large collections. The best element is selected by
+  applying the function given by keyfn to each item and comparing the
+  result. An optional comparator can be given. The implementation uses a
+  pair to keep hold of the result of applying the keyfn function, to
+  avoid the redundancy of calling keyfn unnecessarily."
+  ([keyfn coll]
+   (best-by keyfn compare coll))
+  ([keyfn ^java.util.Comparator comp coll]
+   (first ;; of the pair
+    (reduce (fn [x y]
+              (if-not x
+                ;; Our first pair
+                [y (keyfn y)]
+                ;; Otherwise compare
+                (let [py (keyfn y)]
+                  (case (. comp (compare (second x) py))
+                    (0 1) x
+                    -1 [y py]))))
+            nil ;; seed
+            coll))))
+
+(deftest best-test
+  (is (= (best [3 2 3 nil 19]) 19))
+  (is (= (best (comp - compare) [3 2 3 nil 19]) nil)))
+
+(deftest best-by-test
+  (is (= (best-by first (comp - compare) [[3 9] [2 20] [3 -2] [nil 0] [19 10]]) [nil 0]))
+  (is (= (best-by first (comp - compare) [[3 9] [2 20] [3 -2] [-2 0] [19 10]]) [-2 0])))
 
 (defn content-type-acceptable?
   "Compare a single acceptable mime-type (extracted from an Accept


(defn make-content-type-weigher
  [req k]
  (let [accepts (->> (get-in req [:headers "accept"]) parse-csv (map mime/string->media-type)
                     (sort-by :weight (comp - compare)))]
    (->
     (fn [rep]
       (some (partial content-type-acceptable? (:content-type rep)) accepts))
     (wrap-weigher :content-type)
     skip-rejected)))

;; Add lots of tests for this

(deftest content-type-test
  (let [req {:headers {"accept" "text/xml;q=0.8"}}
        f (make-content-type-weigher req :content-type)]

    (is (= (get-in (f {:content-type (mime/string->media-type "text/xml")})
                   [:weights :content-type])
           [(float 0.8) 3 0 (float 1.0)]))))

;; "A request without any Accept header
;; field implies that the user agent

      (and (= (mime/media-type acceptable) "*/*"))
      [(:weight acceptable) 1 (count (:parameters candidate)) (:weight candidate)])))

(defn best-content-type-acceptable? [accepts]
  (fn [rep]
    (best-by identity (map (partial content-type-acceptable? (:content-type rep)) accepts))))

(defn skip-rejected [f]
  (fn [rep]
    (if (:rejected rep) rep (f rep))))
