#lang syndicate/core

(require racket/set)
(require "../trie.rkt")
(require "../demand-matcher.rkt")
(require/activate "../drivers/timer.rkt")

(actor (lambda (e old-count)
         (match e
           [(? patch?)
            (define-values (in out) (patch-project/set #:take 2 e `(parent ,(?!) ,(?!))))
            (define new-count (+ old-count (set-count in) (- (set-count out))))
            (printf "New parent-record count: ~v\n" new-count)
            (transition new-count
                        (list (retract `(parent-count ,?))
                              (assert `(parent-count ,new-count))))]
           [_ #f]))
       0
       (patch-seq (sub `(parent ,? ,?))
                  (assert `(parent-count 0))))

(define (insert-record record . monitors)
  (printf "Record ~v inserted, depending on ~v\n" record monitors)
  (actor (lambda (e s)
           (match e
             [(? patch/removed?)
              (printf "Retracting ~v because dependencies ~v vanished\n"
                      record
                      (set->list (trie-project/set/single (patch-removed e) (?!))))
              (quit)]
             [(message `(retract ,(== record)))
              (printf "Retracting ~v because we were told to explicitly\n" record)
              (quit)]
             [_ #f]))
         (void)
         (patch-seq (assert record)
                    (sub `(retract ,record))
                    (patch-seq* (map sub monitors)))))

(insert-record `(parent john douglas))
(insert-record `(parent bob john))
(insert-record `(parent ebbon bob))

(actor (lambda (e s)
         (match e
           [(? patch?)
            (transition s
                        (for/list [(AB (trie-project/set #:take 2
                                                         (patch-added e)
                                                         `(parent ,(?!) ,(?!))))]
                          (match-define (list A B) AB)
                          (insert-record `(ancestor ,A ,B)
                                         `(parent ,A ,B))))]
           [_ #f]))
       (void)
       (sub `(parent ,? ,?)))

(actor (lambda (e s)
         (match e
           [(? patch?)
            (transition s
                        (for/list [(AC (trie-project/set #:take 2
                                                         (patch-added e)
                                                         `(parent ,(?!) ,(?!))))]
                          (match-define (list A C) AC)
                          (printf "Inductive step for ~v asserted\n" `(parent ,A ,C))
                          (actor (lambda (e s)
                                   (define removed-parents
                                     (and (patch? e)
                                          (trie-project (patch-removed e) `(parent ,(?!) ,(?!)))))
                                   (if (trie-non-empty? removed-parents)
                                       (begin
                                         (printf
                                          "Inductive step for ~v retracted because of removal ~v\n"
                                          `(parent ,A ,C)
                                          (trie-key-set #:take 2 removed-parents))
                                         (quit))
                                       (and (patch? e)
                                            (transition s
                                                        (for/list [(CB (trie-project/set
                                                                        #:take 2
                                                                        (patch-added e)
                                                                        `(ancestor ,(?!) ,(?!))))]
                                                          (match-define (list _ B) CB)
                                                          (insert-record `(ancestor ,A ,B)
                                                                         `(parent ,A ,C)
                                                                         `(ancestor ,C ,B)))))))
                                 (void)
                                 (patch-seq (sub `(parent ,A ,C))
                                            (sub `(ancestor ,C ,?))))))]
           [_ #f]))
       (void)
       (sub `(parent ,? ,?)))

;;;; Backward-chaining, below, doesn't quite work as formulated with
;;;; this code snippet: the demand-matcher gets confused by wildcard
;;;; demand from the recursive step. One way out of this might be to
;;;; avoid patterns quantifying over more than one place at once: to
;;;; have a process for *all* potential ancestor-queries for a given
;;;; person, rather than one for each *specific* ancestor-query.
;;
;; (spawn-demand-matcher (observe `(ancestor ,(?!) ,(?!)))
;;                       (advertise `(ancestor ,(?!) ,(?!)))
;;                       (lambda (A B)
;;                         (actor (lambda (e old-facts)
;;                                  (match e
;;                                    [(? patch/removed?) (quit)]
;;                                    [(? patch?)
;;                                     (define new-facts (trie-union old-facts (patch-added e)))
;;                                     (define triples (trie-project/set #:take 3 new-facts
;;                                                                       `(,(?!) ,(?!) ,(?!))))
;;                                     (printf "Learned new facts: ~v\n" triples)
;;                                     (transition new-facts
;;                                                 (when (or (set-member? triples `(parent ,A ,B))
;;                                                           (for/or ((triple triples))
;;                                                             (match triple
;;                                                               [`(ancestor ,C ,(== B))
;;                                                                (set-member? triples `(parent ,A ,C))]
;;                                                               [_ #f])))
;;                                                   (printf "... and as a result, asserting ~v\n"
;;                                                           `(ancestor ,A ,B))
;;                                                   (assert `(ancestor ,A ,B))))]
;;                                    [_ #f]))
;;                                trie-empty
;;                                (patch-seq
;;                                 (sub `(parent ,A ,B))
;;                                 (sub `(parent ,A ,?))
;;                                 (sub `(ancestor ,? ,B))
;;                                 (pub `(ancestor ,A ,B))))))

(actor (lambda (e s)
         (when (patch? e) (pretty-print-patch e))
         #f)
       (void)
       (sub `(ancestor ebbon douglas)))

(define (after msec thunk)
  (define id (gensym 'after))
  (if (zero? msec)
      (thunk)
      (actor (lambda (e s) (and (message? e) (quit (thunk))))
             (void)
             (list (message (set-timer id msec 'relative))
                   (sub (timer-expired id ?))))))

(define use-delays? #t)

(after (if use-delays? 1000 0) (lambda ()
                                 (printf "----- Retracting\n")
                                 (message `(retract (parent bob john)))))
(after (if use-delays? 2000 0) (lambda ()
                                 (printf "----- Asserting\n")
                                 (list (insert-record `(parent bob mary))
                                       (insert-record `(parent mary sue))
                                       (insert-record `(parent sue john)))))
