;; content-on-sx — block query + table of contents.
;;
;; Collect blocks across the whole tree (descending into sections) by predicate
;; or type, search them by prose, and derive a table of contents from headings.
;; Tree detection is inline (class + st-iv-get) so this needs no section.sx.
;;
;; Requires (loaded by harness): block.sx, doc.sx, text.sx (asText for search).

(define
  qry-section?
  (fn (b) (and (st-instance? b) (= (get b :class) "CtSection"))))
(define
  qry-tree
  (fn
    (blocks)
    (if
      (= (len blocks) 0)
      (list)
      (let
        ((b (first blocks)))
        (append
          (cons
            b
            (if
              (qry-section? b)
              (let
                ((ch (st-iv-get b "children")))
                (if (list? ch) (qry-tree ch) (list)))
              (list)))
          (qry-tree (rest blocks)))))))

(define
  content/select
  (fn (doc pred) (filter pred (qry-tree (doc-blocks doc)))))

(define
  content/select-type
  (fn (doc type) (content/select doc (fn (b) (= (blk-type b) type)))))

(define
  content/count-type
  (fn (doc type) (len (content/select-type doc type))))

(define
  content/select-ids
  (fn (doc pred) (map (fn (b) (blk-id b)) (content/select doc pred))))

;; Blocks (tree-wide, excluding section containers) whose own prose contains
;; `term`. "Prose" is (asText b), so search covers exactly what every block
;; exposes as text — text/heading/code/quote/callout text, image alt, list
;; items, table headers+cells — with no separate field list to drift from
;; asText / find-replace / stats. Case-sensitive substring match.
(define
  content/search-text
  (fn
    (doc term)
    (content/select
      doc
      (fn
        (b)
        (and
          (not (qry-section? b))
          (>= (index-of (asText b) term) 0))))))

;; Same search, returning matching block ids in document order.
(define
  content/search-text-ids
  (fn
    (doc term)
    (map (fn (b) (blk-id b)) (content/search-text doc term))))

;; table of contents: {:id :level :text} for every heading, in document order.
(define
  content/headings
  (fn (doc) (map (fn (b) {:id (blk-id b) :text (blk-get b "text") :level (blk-get b "level")}) (content/select-type doc "heading"))))
