   "justify text provided as a collection of words to given page width"
   [text separate-paragraphs page-width]
   {:pre [(> page-width 0)]}
+  
   (clear-state)
-  (doseq [paragraph (prepare-text text separate-paragraphs)]
-    ; (println "PARAGRAPH:" paragraph)
-    (.profile js.console "text-justification")
-    (let [words-raw (.split paragraph #" +")
-          ;;words (vec (filter .blank? (flatten (map #(prepare-word page-width %) words-raw))))
-          words (vec (flatten (map #(prepare-word page-width %) words-raw)))
-          word-lens (vec (map count words))
-          justified-text (tj-inner page-width word-lens)]
-        ; (.log js/console "words" (count words) ":" words)
-        ; (.log js/console "state" (count justified-text) ":" justified-text)
-        (update-state
+  (.profile js.console "text-justification")
+  (doseq [words-in-paragraph (prepare-text text separate-paragraphs page-width)]
+    ; (println "PARAGRAPH:" words-in-paragraph)
+    (let [words-lengths (mapv count words-in-paragraph)
+          justified-text (tj-inner page-width words-lengths)]
+       ; (.log js/console "words" (count words) ":" words)
+       ; (.log js/console "state" (count justified-text) ":" justified-text)
+       (update-state
         (second (reduce
-          (fn [[start-word-idx lines-atm] words-in-line]
-            (let [next-word-start-idx (+ start-word-idx words-in-line)
-                  words-in-this-line (subvec words start-word-idx next-word-start-idx)]
+          (fn [[start-word-idx lines-atm] line-words-count]
+            (let [next-word-start-idx (+ start-word-idx line-words-count)
+                  words-in-this-line (subvec words-in-paragraph start-word-idx next-word-start-idx)]
               ; (println words-in-this-line)
-              [next-word-start-idx (conj lines-atm words-in-this-line)]
-              ))
+              [next-word-start-idx (conj lines-atm words-in-this-line)] ))
           [0 []]
-          justified-text)))
-      )
-    (.profileEnd js.console)
-  ))
+          justified-text))) ))
+  (.profileEnd js.console)
+  )
 
-

          (map inc (range (count words))) )))) ;; TODO (dec (count words)) ?


(defn- prepare-text
  "split by paragraphs or treat text as single text flow"
  [text separate-paragraphs]
  (if separate-paragraphs
    (seq (.split text "\n"))
    [(clojure.string/replace text #"\n" " ")]
    ))


(defn- prepare-word
  "remove whitespaces, split word if it is too long"
  [max-len word-raw]
  (let [word (gstring/trim word-raw) len (count word)]
    (cond
      (<= max-len 0) [word]
      (zero? len) [invisible-char]
      (<= len max-len) [word]
      :else (concat
          [(.substring word 0 max-len)]
          (prepare-word max-len (str " -" (.substring word max-len)))) )))
; (defn prepare-word-test [str]
  ; (let [res (prepare-word 11 str)]
    ; (println "test(" (count res) ")" res)))
; (prepare-word-test "aaabbbccc111")
; (prepare-word-test "aaabbbccc111aaabbbccc111")
; (prepare-word-test "aaabbbccc111aaabbbccc111aaabbbccc111")

;;;;
;;;; public interface


(defonce state (atom []))

(defn- set-state    [new-state] (swap! state (fn [] new-state))) ;; TODO just use reset!
(defn- clear-state  [] (set-state []))
(defn- update-state [new-lines] (set-state (concat @state new-lines)))

;;;;
;;;; implementation
