; feed/stream — a stream is an APL vector (rank-1 array) whose ravel holds
; activity dicts. Operations lift APL primitives onto this shape: filter via
; compress (/), sort via grade (⍋), take via ↑, reverse via ⌽.
;
; Requires: lib/apl/runtime.sx, lib/feed/normalize.sx (loaded by harness).

(define feed/stream (fn (acts) (make-array (list (len acts)) acts)))

(define feed/items (fn (s) (get s :ravel)))

(define feed/count (fn (s) (len (get s :ravel))))

(define feed/empty (feed/stream (list)))

(define feed/empty? (fn (s) (= (feed/count s) 0)))

; filter — bool mask ∘ compress. pred : activity -> truthy
(define
  feed/filter
  (fn
    (s pred)
    (let
      ((items (get s :ravel)))
      (let
        ((mask (make-array (list (len items)) (map (fn (a) (if (pred a) 1 0)) items))))
        (apl-compress mask s)))))

; sort-by — ascending, stable on ties (grade-up is stable). key-fn : activity -> number
(define
  feed/sort-by
  (fn
    (s key-fn)
    (let
      ((items (get s :ravel)))
      (let
        ((keys (make-array (list (len items)) (map key-fn items))))
        (let
          ((order (get (apl-grade-up keys) :ravel)))
          (feed/stream (map (fn (i) (nth items (- i 1))) order)))))))

(define feed/sort-by-at (fn (s) (feed/sort-by s feed/at)))

; newest-first: ascending sort then reverse (⌽)
(define feed/recent (fn (s) (apl-reverse (feed/sort-by-at s))))

; take N (↑), clamped to stream length so it never over-takes/pads
(define
  feed/take
  (fn
    (s n)
    (let
      ((c (feed/count s)))
      (if (>= n c) s (apl-take (apl-scalar n) s)))))

(define feed/reverse (fn (s) (apl-reverse s)))

; common predicates
(define
  feed/by-actor
  (fn (s actor) (feed/filter s (fn (a) (equal? (get a :actor) actor)))))

(define
  feed/by-verb
  (fn (s verb) (feed/filter s (fn (a) (equal? (get a :verb) verb)))))

(define
  feed/by-object
  (fn
    (s object)
    (feed/filter s (fn (a) (equal? (get a :object) object)))))

; activities at or after timestamp t
(define
  feed/since
  (fn (s t) (feed/filter s (fn (a) (>= (get a :at) t)))))
