(ns asciidoc.render
  (:require
   [clojure.pprint :refer [pprint]]
   [asciidoc.types :as types]
   [clojure.spec.alpha :as s]
   [clojure.zip :as zip]))

(defn- block-zip [root-block]
  (zip/zipper #(s/conform ::block %)
              :content
              (fn [block children] (assoc block :content children))
              root-block))

(defn- escape-line [line]
  ;; TODO
  line)

(declare render*)

(defn- render-attributes [attributes]
  (let [sorted-attributes (->> attributes
                               seq
                               (sort-by first))]
    (doseq [[k v] sorted-attributes]
      (printf ":%s: %s\n" (name k) v))))

(defn- render-document-header [depth {:keys [arguments] :as block}]
  (println "=" (:title arguments))
  (when-let [author (:author arguments)]
    (println author)
    (when-let [version (:version arguments)]
      (println version)))
  (when-let [attributes (:a arguments)]
    (render-attributes attributes))
  (println)
  (run! #(render* (inc depth) %) (:content block)))

(defn- render-p [block]
  (apply println (:content block)))

(defn- render-description [depth {{:keys [described]} :arguments :as block}]
  (printf "%s::\n" described)
  (run! #(render* (inc depth) %) (:content block)))

(defn- render-section [depth {:keys [arguments] :as block}]
  (print (apply str (repeat (inc depth) \=))
         (:title arguments)
         "\n\n")
  (run! #(render* (inc depth) %) (:content block))
  (println))

(defn- render* [depth block]
  (case (:context block)
    :document (do (assert (zero? depth)
                          "Document block should only occur as root node.")
                  (render-document-header depth block))
    :section (render-section depth block)
    :p (render-p block)
    :description (render-description depth block)
    :<> (run! #(render* (inc depth) %) (apply concat (:content block)))
    (throw (ex-info "no case" {:for (:context block)
                               :block block}))))

(def my-manpage
  [:document {:title "sydnix(1)"
              :author "Madeleine Sydney Ślaga"
              :a {:doctype "manpage"
                  :manmanual "SYDNIX"
                  :mansource "SYDNIX"}}
   [:section {:title "Name"}
    [:p "sydnix - Inspect and operate upon the system."]]
   [:section {:title "Synopsis"}
    [:p "*sydnix* [_COMMAND_]... [_OPTION_]... [_FILE_]..."]]
   [:section {:title "Options"}
    [:description {:described "*--flake=URI*"}
     "UJri is a flake"]]])

(defn conform! [spec x]
  (let [x' (s/conform spec x)]
    (if (= x' :clojure.spec.alpha/invalid)
      (throw (ex-info "invalid" (s/explain-data spec x)))
      x')))

;; Currently unrelentingly recursive.  We'll fix this if/when it bnecomes a
;; problem.  Shame Clojure skimps out on general TCO. }:(
(defn render
  "Render an AsciiDoc block to `*out*`."
  [block]
  (render* 0 (conform! :asciidoc.types/document block)))
