;;; Sample code for concurrent.el  -*- lexical-binding: t; -*-

;; Evaluate following code in the scratch buffer.

(require 'cl-lib)
(require 'concurrent)

;;==================================================
;;; generator

(defvar fib-list nil)

(defvar fib-gen ; Create a generator object.
  (let ((a1 0) (a2 1))
    (cc:generator
     (lambda (x) (push x fib-list)) ; receiving values
     (yield a1)
     (yield a2)
     (while t
       (let ((next (+ a1 a2)))
         (setq a1 a2
               a2 next)
         (yield next))))))

(funcall fib-gen) ; Generate 5 times
(funcall fib-gen) (funcall fib-gen)
(funcall fib-gen) (funcall fib-gen)

fib-list ;=> (3 2 1 1 0)


;;==================================================
;;; thread

(let ((count 0) (anm "-/|\\-")
      (end 50) (pos (point)))
  (cc:thread
   60
   (message "Animation started.")
   (while (> end (cl-incf count))
     (save-excursion
       (when (< 1 count)
         (goto-char pos) (delete-char 1))
       (insert (char-to-string
                (aref anm (% count (length anm)))))))
   (save-excursion
     (goto-char pos) (delete-char 1))
   (message "Animation finished.")))

;; Play the simple character animation here.


;;==================================================
;;; semaphore

;; create a semaphore object with permit=1.
(defvar smp (cc:semaphore-create 1))

;; executing three tasks...
(deferred:nextc (cc:semaphore-acquire smp)
  (lambda (_)
    (message "go1")))
(deferred:nextc (cc:semaphore-acquire smp)
  (lambda (_)
    (message "go2")))
(deferred:nextc (cc:semaphore-acquire smp)
  (lambda (_)
    (message "go3")))

;; => Only the fist task is executed and displays "go1".

(cc:semaphore-release smp)

;; => The second task is executed and displays "go2".

(cc:semaphore-waiting-deferreds smp) ; return the deferred object that displays "go3".

(cc:semaphore-release-all smp) ; => reset permit count and return the deferred object that displays "go3".

(cc:semaphore-waiting-deferreds smp) ; => nil


;;==================================================
;; Dataflow

;; create a parent environment and bind "aaa" to 256.
(defvar dfenv-parent (cc:dataflow-environment))
(cc:dataflow-set dfenv-parent "aaa" 256)

;; create an environment with the parent one.
(defvar dfenv (cc:dataflow-environment dfenv-parent))

;; Return the parent value.
(cc:dataflow-get-sync dfenv "aaa") ; => 256

(deferred:$
  (cc:dataflow-get dfenv "abc")
  (deferred:nextc it
    (lambda (x) (message "Got abc : %s" x))))
;; => This task is blocked

(cc:dataflow-set dfenv "abc" 256) ; bind 256 to "abc"

;; => The blocked task is executed and displays "Got abc : 256".

(cc:dataflow-get-sync dfenv "abc") ; => 256

;; unbind the variable "abc"
(cc:dataflow-clear dfenv "abc")

(cc:dataflow-get-sync dfenv "abc") ; => nil


;; complicated key (`equal' can compare nested lists.)

(deferred:$
  (cc:dataflow-get dfenv '("http://example.com/a.jpg" 300))
  (deferred:nextc it
    (lambda (x) (message "a.jpg:300 OK %s" x))))

(cc:dataflow-set dfenv '("http://example.com/a.jpg" 300) 'jpeg)

;; waiting for two variables

(deferred:$
  (deferred:parallel
    (cc:dataflow-get dfenv "abc")
    (cc:dataflow-get dfenv "def"))
  (deferred:nextc it
    (lambda (values)
      (apply 'message "Got values : %s, %s" values)
      (apply '+ values)))
  (deferred:nextc it
    (lambda (x) (insert (format ">> %s" x)))))

(cc:dataflow-get-waiting-keys dfenv)   ; => ("def" "abc")
(cc:dataflow-get-avalable-pairs dfenv) ; => (("aaa" . 256))

(cc:dataflow-set dfenv "abc" 128)
(cc:dataflow-set dfenv "def" 256)

;; => "Got values : 128, 256"
;; inserted ">> 384"

(cc:dataflow-get-avalable-pairs dfenv)

(cc:dataflow-clear-all dfenv)

(cc:dataflow-get-avalable-pairs dfenv)


;;==================================================
;; Signal

(progn
  (defvar parent-channel (cc:signal-channel "parent"))
  (cc:signal-connect
   parent-channel 'parent-load
   (lambda (event) (message "Parent Signal : %s" event)))
  (cc:signal-connect
   parent-channel t
   (lambda (event) (message "Parent Listener : %s" event)))

  (defvar channel (cc:signal-channel "child" parent-channel))
  (cc:signal-connect
   channel 'window-load
   (lambda (event) (message "Signal : %s" event)))
  (cc:signal-connect
   channel t
   (lambda (event) (message "Listener : %s" event)))
  (deferred:$
    (cc:signal-connect channel 'window-load)
    (deferred:nextc it
      (lambda (x) (message "Deferred Signal : %s" x))))
  )

(cc:signal-send channel 'window-load "hello signal!")
(cc:signal-send channel 'some "some signal!")

(cc:signal-send parent-channel 'parent-load "parent hello!")
(cc:signal-send parent-channel 'window-load "parent hello!")
(cc:signal-send parent-channel 'some "parent some hello!")
(cc:signal-send-global channel 'some "parent some hello!")

(cc:signal-disconnect-all channel)
