#cs(module arrow mzscheme
  
  (require (lib "error.ss" "htdp")
           (lib "big-draw.ss" "htdp")
           (lib "etc.ss")
           (lib "class.ss")
           (lib "mred.ss" "mred")
           (lib "prim.ss" "lang"))
  
  (provide
   control
   control-up-down
   control-left-right
   )

  (define-higher-order-primitive control-up-down control-up-down/proc 
                                 (_ _ up-down draw))

  (define-higher-order-primitive control-left-right control-left-right/proc 
                                 (_ _ left-right draw))

  (define-higher-order-primitive control control/proc 
                                 (_ _ left-right up-down draw))  

  
  ;; CONSTANTS ---------------------------------------------------------------
  (define MY-ICONS "/home/matthias/icons/")
  (define TITLE "Controller")
  
  (define (mk-image-constant kind)
    (make-object bitmap%
      (build-path (collection-path "icons") (format "arrow.~a.gif" kind)) 'gif))
  
  ;(define LEFT-ARROW  (mk-image-constant "marble.left"))
  ;(define RIGHT-ARROW (mk-image-constant "marble.right"))
  ;(define UP-ARROW    (mk-image-constant "marble.up"))
  ;(define DOWN-ARROW  (mk-image-constant "marble.down"))
  
  (define LEFT-ARROW  (mk-image-constant "blue.left"))
  (define RIGHT-ARROW (mk-image-constant "blue.right"))
  (define UP-ARROW    (mk-image-constant "blue.up"))
  (define DOWN-ARROW  (mk-image-constant "blue.down"))
  
  ;; LAYOUT ------------------------------------------------------------------
  
  ;; layout = (listof (listof (union #f bitmap%)))  
  
  (define FOUR 
    `( (,#f         ,UP-ARROW   ,#f)
       (,LEFT-ARROW ,#f         ,RIGHT-ARROW)
       (,#f         ,DOWN-ARROW ,#f) ))
  
  (define UP-DOWN
    `( (,UP-ARROW   )
       (,DOWN-ARROW ) ))
  
  (define LEFT-RIGHT
    `( (,LEFT-ARROW ,RIGHT-ARROW ) ))
  
  ;; make-button-table : 
  ;;   panel% layout (bitmap% -> (_ _ -> X))
  ;;   -> 
  ;;   (listof (listof (union panel% button%)))
  ;; to translate a layout table into a button table 
  ;;   each button is controled by (control a-bitmap)
  (define (make-button-table panel control layout)
    (define (make-row a-row)
      (define row-panel (make-object horizontal-panel% panel))
      (define (make-item an-item)
        (if an-item
            (make-object button% an-item row-panel (control an-item))
            (let ([panel (make-object horizontal-panel% row-panel)])
              (send panel min-width 30))))
      ;; --- 
      (map make-item a-row))
    ;; --- 
    (map make-row layout))
  
  ;; GUI ---------------------------------------------------------------------
  
  ;; make-controller :
  ;;   symbol layout number X (number X -> true) (number X -> true) (X -> true)-> void
  ;; effect: create a left-right controller that invokes move on delta
  (define (make-controller tag layout shape delta left-right-action up-down-action draw-shape)
    (check-arg  tag
                (and (number? delta) (integer? delta) (>= delta 1))
                "positive integer"
                '2nd
                delta)
    (check-proc tag left-right-action 2 "move-left-right" "two arguments")
    (check-proc tag up-down-action 2 "move-up-down" "two arguments")
    (check-proc tag draw-shape 1 "draw" "one argument")
    ;; --- 
    (local ((define frame (make-object frame% TITLE #f 10 10))
            (define panel (make-object vertical-panel% frame))
            ;; control : bitmap% -> (_ _ -> void)
            ;; to check which button was clicked 
            (define (control an-item)
              (lambda (x y)
                ;; DESIGN DECISION:
                ;; by handing over the number first, nesting the moves becomes easier
                (evcase an-item
                        (UP-ARROW
                         (set! shape (up-down-action (- delta) shape)))
                        (DOWN-ARROW
                         (set! shape (up-down-action delta shape)))
                        (LEFT-ARROW
                         (set! shape (left-right-action (- delta) shape)))
                        (RIGHT-ARROW
                         (set! shape (left-right-action delta shape))))
                (draw-shape shape))))
      (make-button-table panel control layout)
      (send frame show #t)
      #t))
  
  ;; EXPORTS:
  
  (define (void2 x y) (void))
  
  ;; control-left-right/proc : XShape number (number XShape -> XShape) (XShape -> true) -> true
  ;; effect: create a window from which a user can control L/R moves
  (define (control-left-right/proc shape delta lr draw)
    (make-controller 'control-left-right LEFT-RIGHT shape delta lr void2 draw))
  
  ;; control-up-down : X number (number X -> true) (X -> true) -> true
  ;; effect: create a window from which a user can control U/D moves      
  (define (control-up-down/proc shape delta ud draw)
    (make-controller 'control-up-down UP-DOWN shape delta void2 ud draw))
  
  ;; control/proc : X number (number X -> true) (number X -> true) (X -> true) -> true
  ;; effect: create a window from which a user can control moves
  (define (control/proc shape delta lr ud draw)
    (make-controller 'control FOUR shape delta lr ud draw))
  )
