#lang syndicate
;; Toy file system, based on the example in the ESOP2016 submission.
;; Low-level implementation, without subconversation.

(require/activate "fs-shell.rkt")
(require/activate "fs-protocol.rkt")

(require (only-in syndicate [assert core:assert]))
(require racket/set)

(struct fs-state (files monitored) #:prefab)

(define (update-file state name new-content)
  (transition (struct-copy fs-state state
                           [files (if new-content
                                      (hash-set (fs-state-files state) name new-content)
                                      (hash-remove (fs-state-files state) name))])
              (if (set-member? (fs-state-monitored state) name)
                  (patch-seq (retract (file name (hash-ref (fs-state-files state) name #f)))
                             (core:assert (file name new-content)))
                  '())))

(define (file-system-event-handler e state)
  (match e
    [(? patch? p)
     (define monitored-to-add
       (for-trie/set [((observe (file $name _)) (patch-added p))] name))
     (define monitored-to-remove
       (for-trie/set [((observe (file $name _)) (patch-removed p))] name))
     (transition (struct-copy fs-state state
                              [monitored (set-subtract (set-union (fs-state-monitored state)
                                                                  monitored-to-add)
                                                       monitored-to-remove)])
                 (list (for/list [(name monitored-to-add)]
                         (printf "At least one reader exists for ~v\n" name)
                         (core:assert (file name (hash-ref (fs-state-files state) name #f))))
                       (for/list [(name monitored-to-remove)]
                         (printf "No remaining readers exist for ~v\n" name)
                         (retract (file name ?)))))]
    [(message (save (file name new-content)))
     (update-file state name new-content)]
    [(message (delete name))
     (update-file state name #f)]
    [_ #f]))

(spawn file-system-event-handler
       (fs-state (hash) (set))
       (patch-seq (sub (observe (file ? ?)))
                  (sub (save (file ? ?)))
                  (sub (delete ?))))
