;;; column_writer.clj -- part of the pretty printer for Clojure


;   Copyright (c) Rich Hickey. All rights reserved.
;   The use and distribution terms for this software are covered by the
;   Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
;   which can be found in the file epl-v10.html at the root of this distribution.
;   By using this software in any fashion, you are agreeing to be bound by
;   the terms of this license.
;   You must not remove this notice, or any other, from this software.

;; Author: Tom Faulhaber
;; April 3, 2009
;; Revised to use proxy instead of gen-class April 2010

;; This module implements a column-aware wrapper around an instance of erlang.io.IWriter

(in-ns 'clojure.pprint)

(import [clojerl IDeref]
        [erlang.io IWriter])

(def ^:dynamic ^{:private true} *default-page-width* 72)

(defn- get-field [^IWriter this sym]
  (sym @@this))

(defn- set-field [^IWriter this sym new-val]
  (swap! @this assoc sym new-val))

(defn- get-column [this]
  (get-field this :cur))

(defn- get-line [this]
  (get-field this :line))

(defn- get-max-column [this]
  (get-field this :max))

(defn- set-max-column [this new-max]
  (set-field this :max new-max)
  nil)

(defn- get-writer [this]
  (get-field this :base))

(defn- c-write-char [^IWriter this ^clojerl.Integer c]
  (if (= c (int \newline))
    (do
      (set-field this :cur 0)
      (set-field this :line (inc (get-field this :line))))
    (set-field this :cur (inc (get-field this :cur))))
  (.write ^IWriter (get-field this :base) c))

(deftype ColumnWriter [writer fields]
  clojerl.IDeref
  (deref [_] fields)
  erlang.io.IWriter
  (write [this x]
    (condp = (type x)
      clojerl.String
      (let [^clojerl.String s x
            nl (.last_index_of s "\n")]
        (if (neg? nl)
          (set-field this :cur (+ (get-field this :cur) (count s)))
          (do
            (set-field this :cur (- (count s) nl 1))
            (set-field this :line (+ (get-field this :line)
                                     (count (filter #(= % \newline) s))))))
        (.write ^IWriter (get-field this :base) s))

      clojerl.Integer
      (c-write-char this x))))

(defn- column-writer
  ([writer] (column-writer writer *default-page-width*))
  ([^IWriter writer max-columns]
   (let [fields (atom {:max max-columns, :cur 0, :line 0 :base writer})]
     (ColumnWriter. writer fields))))
