(ns auto-ap.numeric
  (:require [clojure.string :as str]))

(def ^:private smalls ["zero"
                       "one"
                       "two"
                       "three"
                       "four"
                       "five"
                       "six"
                       "seven"
                       "eight"
                       "nine"
                       "ten"
                       "eleven"
                       "twelve"
                       "thirteen"
                       "fourteen"
                       "fifteen"
                       "sixteen"
                       "seventeen"
                       "eighteen"
                       "nineteen"])

(def ^:private tens ["twenty"
                     "thirty"
                     "forty"
                     "fifty"
                     "sixty"
                     "seventy"
                     "eighty"
                     "ninety"])

(def ^:private magnitudes ["thousand"
                           "million"
                           "billion"
                           "trillion"
                           "quadrillion"
                           "quintillion"
                           "sextillion"
                           "septillion"
                           "octillion"
                           "nonillion"
                           "decillion"
                           "undecillion"
                           "duodecillion"
                           "tredecillion"
                           "quattuordecillion"
                           "quindecillion"
                           "quinquadecillion"
                           "sexdecillion"
                           "sedecillion"
                           "septendecillion"
                           "octodecillion"
                           "novemdecillion"
                           "novendecillion"
                           "vigintillion"
                           #_"centillion"])

(defn words
  [n]
  {:pre [(not (neg? n))]}
  (cond
    (< n 20) (smalls n)
    (< n 100) (let [pre (tens (- (quot n 10) 2)),
                    x (rem n 10)]
                (if (zero? x) pre (str pre "-" (words x))))
    (< n 1000) (let [hun (quot n 100),
                     x (rem n 100),
                     s (str (words hun) " hundred")]
                 (if (zero? x) s (str s " and " (words x))))
    :else (loop [parts [] n n mag -1]
            (if (zero? n)
              (str/join " " parts)
              (let [x (rem n 1000)
                    n (quot n 1000)
                    parts (if (zero? x)
                            parts
                            (let [s (words x)]
                              (cons
                               (if (neg? mag)
                                 (if (< x 100)
                                   (str "and " s)
                                   s)
                                 (str s " " (magnitudes mag)))
                               parts)))]
                (recur parts n (inc mag)))))))


(defn num->words [num]
  (let [total-cents (int (Math/round (* 100 num))) 
        dollar-num (int (quot total-cents 100))
        cent-num (int (rem total-cents 100))
        dollars (str (words dollar-num) " dollars")
        cents  (str (words cent-num) " cents")]
    (cond
      (and (> dollar-num 0) (> cent-num 0))
      (str dollars " and " cents)

      (> dollar-num 0)
      dollars

      :else
      cents)))
