;; stream-process-pair template (streaming-compatible)
;;
;; Macro for processing a video source pair with full effects.
;; Reads source, applies A/B effects (rotate, zoom, invert, hue), blends,
;; and applies pair-level rotation.
;;
;; Required context (must be defined in calling scope):
;;   - sources: array of video sources
;;   - pair-configs: array of {:dir :rot-a :rot-b :zoom-a :zoom-b} configs
;;   - pair-states: array from (bind pairs :states)
;;   - now: current time (t)
;;   - e: audio energy (0-1)
;;
;; Required effects (must be loaded):
;;   - rotate, zoom, invert, hue_shift, blend
;;
;; Usage:
;;   (include :path "../templates/stream-process-pair.sexp")
;;   ...in frame pipeline...
;;   (let [pair-states (bind pairs :states)
;;         now t
;;         e (streaming:audio-energy music now)]
;;     (process-pair 0))  ;; process source at index 0

(require-primitives "core")

(defmacro process-pair (src-idx)
  (let [src (nth sources src-idx)
        frame (streaming:source-read src now)
        cfg (nth pair-configs src-idx)
        state (nth pair-states src-idx)

        ;; Get state values (invert uses countdown > 0)
        inv-a-active (if (> (get state :inv-a) 0) 1 0)
        inv-b-active (if (> (get state :inv-b) 0) 1 0)
        ;; Hue is active only when countdown > 0
        hue-a-val (if (> (get state :hue-a) 0) (get state :hue-a-val) 0)
        hue-b-val (if (> (get state :hue-b) 0) (get state :hue-b-val) 0)
        mix-opacity (get state :mix)
        pair-rot-angle (* (get state :angle) (get cfg :dir))

        ;; Get config values for energy-mapped ranges
        rot-a-max (get cfg :rot-a)
        rot-b-max (get cfg :rot-b)
        zoom-a-max (get cfg :zoom-a)
        zoom-b-max (get cfg :zoom-b)

        ;; Energy-driven rotation and zoom
        rot-a (core:map-range e 0 1 0 rot-a-max)
        rot-b (core:map-range e 0 1 0 rot-b-max)
        zoom-a (core:map-range e 0 1 1 zoom-a-max)
        zoom-b (core:map-range e 0 1 1 zoom-b-max)

        ;; Apply effects to clip A
        clip-a (-> frame
                   (rotate :angle rot-a)
                   (zoom :amount zoom-a)
                   (invert :amount inv-a-active)
                   (hue_shift :degrees hue-a-val))

        ;; Apply effects to clip B
        clip-b (-> frame
                   (rotate :angle rot-b)
                   (zoom :amount zoom-b)
                   (invert :amount inv-b-active)
                   (hue_shift :degrees hue-b-val))

        ;; Blend A+B
        blended (blend clip-a clip-b :opacity mix-opacity)]

    ;; Apply pair-level rotation
    (rotate blended :angle pair-rot-angle)))
