;; ==========================================================================
;; freeze.sx — Serializable state boundaries
;;
;; Freeze scopes collect signals registered within them. On freeze,
;; their current values are serialized to SX. On thaw, values are
;; restored. Multiple named scopes can coexist independently.
;;
;; This is a library built on top of the evaluator's scoped effects
;; (scope-push!/scope-pop!/context) and signal system. It is NOT
;; part of the core evaluator — it loads after evaluator.sx.
;;
;; Usage:
;;   (freeze-scope "editor"
;;     (let ((doc (signal "hello")))
;;       (freeze-signal "doc" doc)
;;       ...))
;;
;;   (cek-freeze-scope "editor")  → {:name "editor" :signals {:doc "hello"}}
;;   (cek-thaw-scope "editor" frozen-data)  → restores signal values
;; ==========================================================================

;; Registry of freeze scopes: name → list of {name signal} entries

(define-library
  (sx freeze)
  (export
    freeze-registry
    freeze-signal
    freeze-scope
    cek-freeze-scope
    cek-freeze-all
    cek-thaw-scope
    cek-thaw-all
    freeze-to-sx
    thaw-from-sx)
  (begin
    (define freeze-registry (dict))
    (define
      freeze-signal
      :effects (mutation)
      (fn
        (name sig)
        (let
          ((scope-name (context "sx-freeze-scope" nil)))
          (when
            scope-name
            (let
              ((entries (or (get freeze-registry scope-name) (list))))
              (append! entries (dict "name" name "signal" sig))
              (dict-set! freeze-registry scope-name entries))))))
    (define
      freeze-scope
      :effects (mutation)
      (fn
        (name body-fn)
        (scope-push! "sx-freeze-scope" name)
        (dict-set! freeze-registry name (list))
        (cek-call body-fn nil)
        (scope-pop! "sx-freeze-scope")
        nil))
    (define
      cek-freeze-scope
      :effects ()
      (fn
        (name)
        (let
          ((entries (or (get freeze-registry name) (list)))
            (signals-dict (dict)))
          (for-each
            (fn
              (entry)
              (dict-set!
                signals-dict
                (get entry "name")
                (signal-value (get entry "signal"))))
            entries)
          (dict "name" name "signals" signals-dict))))
    (define
      cek-freeze-all
      :effects ()
      (fn
        ()
        (map (fn (name) (cek-freeze-scope name)) (keys freeze-registry))))
    (define
      cek-thaw-scope
      :effects (mutation)
      (fn
        (name frozen)
        (let
          ((entries (or (get freeze-registry name) (list)))
            (values (get frozen "signals")))
          (when
            values
            (for-each
              (fn
                (entry)
                (let
                  ((sig-name (get entry "name"))
                    (sig (get entry "signal"))
                    (val (get values sig-name)))
                  (when (not (nil? val)) (reset! sig val))))
              entries)))))
    (define
      cek-thaw-all
      :effects (mutation)
      (fn
        (frozen-list)
        (for-each
          (fn (frozen) (cek-thaw-scope (get frozen "name") frozen))
          frozen-list)))
    (define
      freeze-to-sx
      :effects ()
      (fn (name) (sx-serialize (cek-freeze-scope name))))
    (define
      thaw-from-sx
      :effects (mutation)
      (fn
        (sx-text)
        (let
          ((parsed (sx-parse sx-text)))
          (when
            (not (empty? parsed))
            (let
              ((frozen (first parsed)))
              (cek-thaw-scope (get frozen "name") frozen)))))))) ;; end define-library

;; Re-export to global namespace for backward compatibility
(import (sx freeze))
