   [f]
   (WIGWriter. (cio/writer (util/compressor-output-stream f)) (util/as-url f)))
 
+(def ^:const wig-fields [:format :chrom :start :end :value])
+
 (defn- header-or-comment?
   "Checks if given string is neither a header nor a comment line."
   [^String s]

  [^WIGWriter wtr xs]
  (let [w ^BufferedWriter (.writer wtr)]
    (->> xs
         (map serialize-wig)
         (cstr/join \newline)
         (.write w))))

    (as-double s)))

(defn- deserialize-wigs
  "Parse WIG lines and return a lazy sequence of flat map."
  [lines]
  (letfn [(deserialize [lines current-track]
            (lazy-seq
             (when (some? (first lines))
               (let [fields (-> lines first cstr/trim (cstr/split #"\s+"))]
                 (case (first fields)
                   ; declaration line of variableStep
                   "variableStep"
                   (let [{:keys [chrom span] :or {span 1}} (->> fields rest fields->map)
                         span (as-long span)]
                     (deserialize (rest lines) {:format :variable-step, :chrom chrom, :pre-start nil, :span span, :step nil}))

                   ; declaration line of fixedStep
                   "fixedStep"
                   (let [{:keys [chrom start span step] :or {span 1, step 1}} (->> fields rest fields->map)
                         step (as-long step)
                         pre-start (- (as-long start) step)
                         span (as-long span)]
                     (deserialize (rest lines) {:format :fixed-step, :chrom chrom, :pre-start pre-start, :span span, :step step}))

                   ; data line
                   (let [m (case (:format current-track)
                             :variable-step
                             (let [{:keys [chrom span]} current-track
                                   [start value] fields
                                   start (as-long start)
                                   end (dec (+ start span))
                                   value (str->wiggle-track-data value)]
                               {:format :variable-step, :chrom chrom, :start start, :end end, :value value})

                             :fixed-step
                             (let [{:keys [chrom pre-start span step]} current-track
                                   start (+ pre-start step)
                                   end (dec (+ start span))
                                   value (-> fields first str->wiggle-track-data)]
                               {:format :fixed-step, :chrom chrom, :start start, :end end, :value value})

                             (throw (IllegalArgumentException. "Invalid wiggle format")))]
                     (cons m (deserialize (rest lines)
                                          (update current-track :pre-start (fn [_] (:start m)))))))))))]
    (deserialize lines {:format nil, :chrom nil, :pre-start nil, :span nil :step nil})))

(defn- serialize-wigs
  "Serialize a sequence of WIG fields into a lazy sequence of string."
  [wigs]
  {:pre [;; These fields are required.
         (map (fn [wig] (every? some? ((apply juxt wig-fields) wig))) wigs)
         ;; There are two options for formatting wiggle data.
         (map (fn [{:keys [format]} wig] (or (= format :fixed-step) (= format :variable-step))) wigs)]}
  (letfn [(same-track? [first-wig wig]
            (and (= (:format first-wig) (:format wig))
                 (= (- (:end first-wig) (:start first-wig))
                    (- (:end wig) (:start wig)))))
          (serialize [wigs]
            (lazy-seq
             (when (some? (first wigs))
               (let [[track rest-wigs] (split-with (partial same-track? (first wigs)) wigs)
                     track (case (-> track first :format)
                             :variable-step
                             (let [{:keys [chrom start end]} (first track)
                                   span (inc (- end start))
                                   declaration-line (->> (cond-> ["variableStep" (str "chrom=" chrom)]
                                                           (not= span 1) (conj (str "span=" span)))
                                                         (cstr/join \space))
                                   data-lines (->> track
                                                   (map (fn [{:keys [start value]}] (cstr/join \space [start value])))
                                                   (cstr/join \newline))]
                               (cstr/join \newline [declaration-line data-lines]))

                             :fixed-step
                             (let [{:keys [chrom start end]} (first track)
                                   step (- (-> track rest first :start) start)
                                   span (inc (- end start))
                                   declaration-line (->> (cond-> ["fixedStep" (str "chrom=" chrom) (str "start=" start)]
                                                           (not= step 1) (conj (str "step=" step))
                                                           (not= span 1) (conj (str "span=" span)))
                                                         (cstr/join \space))
                                   data-lines (->> track
                                                   (map (fn [{:keys [value]}] value))
                                                   (cstr/join \newline))]
                               (cstr/join \newline [declaration-line data-lines])))]
                 (cons track (serialize rest-wigs))))))]
    (serialize wigs)))

(defn read-fields
  "Read sequence of WIG fields from reader"
