;;; -*-Scheme-*-
;;;
;;; VCR simulation


;; Initialization

(require 'motif)
(load-widgets shell bulletin-board row-column label push-button)

(define top (application-initialize 'vcr))
(define con (widget-context top))


;; The layout of the VCR's controls

(define vcr (create-managed-widget (find-class 'row-column) top))

(define panel (create-managed-widget (find-class 'bulletin-board) vcr))

(define tape (create-managed-widget (find-class 'push-button) panel))
(set-values! tape 'x 10 'y 10 'width 150 'border-width 2 'label-string 'empty
                  'recompute-size #f
		  'activate-callback (list (lambda _ (engine 'load))))

(define counter (create-managed-widget (find-class 'push-button) panel))
(set-values! counter 'x 170 'y 10 'width 50 'label-string "0"
		     'alignment "alignment_end" 'recompute-size #f)

(define function (create-managed-widget (find-class 'push-button) panel))
(set-values! function 'x 230 'y 10 'width 70 'label-string "stop"
                      'recompute-size #f)

(define buttons (create-managed-widget (find-class 'row-column) vcr))
(set-values! buttons 'orientation 'horizontal)

(define-macro (define-button label activate arm disarm)
  `(let ((b (create-managed-widget (find-class 'push-button) buttons)))
     (set-values! b 'label-string ,label)
     (add-callback b 'activate-callback (lambda _ ,activate))
     (add-callback b 'arm-callback      (lambda _ ,arm))
     (add-callback b 'disarm-callback   (lambda _ ,disarm))))

(define-button 'eject (begin (engine 'stop) (engine 'empty)) #f #f)
(define-button 'play  (engine 'play) #f #f)
(define-button 'stop  (engine 'stop) #f #f)
(define-button 'forw  (engine 'forw) (engine 'cue #t) (engine 'cue #f))
(define-button 'rew   (engine 'rew) (engine 'review #t) (engine 'review #f))
(define-button 'pause (engine 'pause) #f #f)


;; The `logic' of the VCR

(define engine
  (let ((timer #f) (interval) (loaded #f) (cnt 0) (state 'stop))

  (define (advance x)
    (set! cnt (modulo (+ cnt x) 10000000))
    (set-values! counter 'label-string (format #f "~s" cnt)))

  (define (timeout x)
    (advance x)
    (set! timer (context-add-timeout con interval (lambda _ (timeout x)))))

  (define (set-timer when x)
    (stop-timer)
    (set! interval when)
    (set! timer (context-add-timeout con when (lambda _ (timeout x)))))

  (define (stop-timer)
    (if timer (remove-timeout timer))
    (set! timer #f))

  (define (cue/review on? x)
    (if on?
	(if (not (eq? state 'play))          ; do nothing if not playing
	    state
	    (set-timer 100 x)                ; else
	    'cue/review)
	(if (not (eq? state 'cue/review))    ; do nothing if not in cue/review
	    state                            ;   mode
	    (set-timer 1000 100)             ; else switch back to play mode
	    'play)))

  (lambda (op . args)
    (call-with-current-continuation
      (lambda (return)
        (case op
          (load
	    (set-values! tape 'label-string 'loaded)
	    (set! loaded #t))
          (empty
	    (set-values! tape 'label-string 'empty)
	    (set! loaded #f))
          (else
	    (if (not loaded)
		(return #f))
            (case op
	      (stop
		(stop-timer))
	      (cue    (set! op (cue/review (car args) 100)))
	      (review (set! op (cue/review (car args) -100)))
	      (pause
		(cond ((eq? state 'pause)
		       (set-timer 1000 100)
		       (set! op 'play))
		      ((eq? state 'play)
		       (stop-timer))
		      (else
		       (return #f))))
              (forw
		 (cond ((eq? state 'pause)
			(advance 4)
			(set! op 'pause))              ; stay in pause mode
		       ((not (eq? state 'cue/review))
		        (set-timer 1000 10000))
		       (else (set! op state))))        ; stay in the old mode
              (rew
		 (cond ((eq? state 'pause)
			(advance -4)
			(set! op 'pause))
		       ((not (eq? state 'cue/review))
		        (set-timer 1000 -10000))
		       (else (set! op state))))
              (play
	         (set-timer 1000 100)))
	    (set! state op)
            (set-values! function 'label-string op))))))))

(realize-widget top)
(context-main-loop con)